-//\r
-// BaseMachine.jvm.cs\r
-//\r
-// Author:\r
-// author: Dan Lewis (dlewis@gmx.co.uk)\r
-// (c) 2002\r
-// Copyright (C) 2005 Novell, Inc (http://www.novell.com)\r
-//\r
-\r
-//\r
-// Permission is hereby granted, free of charge, to any person obtaining\r
-// a copy of this software and associated documentation files (the\r
-// "Software"), to deal in the Software without restriction, including\r
-// without limitation the rights to use, copy, modify, merge, publish,\r
-// distribute, sublicense, and/or sell copies of the Software, and to\r
-// permit persons to whom the Software is furnished to do so, subject to\r
-// the following conditions:\r
-// \r
-// The above copyright notice and this permission notice shall be\r
-// included in all copies or substantial portions of the Software.\r
-// \r
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
-//\r
-\r
-\r
-\r
-using System;\r
-using System.Collections;\r
-\r
-namespace System.Text.RegularExpressions\r
-{\r
- abstract class BaseMachine : IMachine\r
- {\r
- delegate void MatchAppendEvaluator (Match match, StringBuilder sb);\r
-\r
- virtual public string Replace (Regex regex, string input, string replacement, int count, int startat) {\r
- ReplacementEvaluator ev = new ReplacementEvaluator (regex, replacement);\r
- return Replace (regex, input, new MatchAppendEvaluator (ev.EvaluateAppend), count, startat);\r
- }\r
-\r
- virtual public string [] Split (Regex regex, string input, int count, int startat) {\r
- ArrayList splits = new ArrayList ();\r
- if (count == 0)\r
- count = Int32.MaxValue;\r
-\r
- int ptr = startat;\r
- Match m = null;\r
- while (--count > 0) {\r
- if (m != null)\r
- m = m.NextMatch ();\r
- else\r
- m = regex.Match (input, ptr);\r
-\r
- if (!m.Success)\r
- break;\r
-\r
- if (regex.RightToLeft)\r
- splits.Add (input.Substring (m.Index + m.Length, ptr - m.Index - m.Length));\r
- else\r
- splits.Add (input.Substring (ptr, m.Index - ptr));\r
-\r
- int gcount = m.Groups.Count;\r
- for (int gindex = 1; gindex < gcount; gindex++) {\r
- Group grp = m.Groups [gindex];\r
- splits.Add (input.Substring (grp.Index, grp.Length));\r
- }\r
-\r
- if (regex.RightToLeft)\r
- ptr = m.Index;\r
- else\r
- ptr = m.Index + m.Length;\r
-\r
- }\r
-\r
- if (regex.RightToLeft && ptr >= 0)\r
- splits.Add (input.Substring (0, ptr));\r
- if (!regex.RightToLeft && ptr <= input.Length)\r
- splits.Add (input.Substring (ptr));\r
-\r
- return (string []) splits.ToArray (typeof (string));\r
- }\r
-\r
- virtual public Match Scan (Regex regex, string text, int start, int end) {\r
- throw new NotImplementedException ("Scan method must be implemented in derived classes");\r
- }\r
-\r
- virtual public string Result (string replacement, Match match)\r
- {\r
- return ReplacementEvaluator.Evaluate (replacement, match);\r
- }\r
-\r
- private static string Replace (Regex regex, string input, MatchAppendEvaluator evaluator, int count, int startat) {\r
- StringBuilder result = new StringBuilder ();\r
- int ptr = startat;\r
- int counter = count;\r
-\r
- result.Append (input, 0, ptr);\r
-\r
- Match m = regex.Match (input, startat);\r
- while (m.Success) {\r
- if (count != -1)\r
- if (counter-- <= 0)\r
- break;\r
- if (m.Index < ptr)\r
- throw new SystemException ("how");\r
- result.Append (input, ptr, m.Index - ptr);\r
- evaluator (m, result);\r
-\r
- ptr = m.Index + m.Length;\r
- m = m.NextMatch ();\r
- }\r
-\r
- if (ptr == 0)\r
- return input;\r
-\r
- result.Append (input, ptr, input.Length - ptr);\r
-\r
- return result.ToString ();\r
- }\r
- }\r
-}\r
+//
+// BaseMachine.jvm.cs
+//
+// Author:
+// author: Dan Lewis (dlewis@gmx.co.uk)
+// (c) 2002
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+
+namespace System.Text.RegularExpressions
+{
+ abstract class BaseMachine : IMachine
+ {
+ internal delegate void MatchAppendEvaluator (Match match, StringBuilder sb);
+
+ public virtual string Replace (Regex regex, string input, string replacement, int count, int startat)
+ {
+ ReplacementEvaluator ev = new ReplacementEvaluator (regex, replacement);
+ if (regex.RightToLeft)
+ return RTLReplace (regex, input, new MatchEvaluator (ev.Evaluate), count, startat);
+ else
+ return LTRReplace (regex, input, new MatchAppendEvaluator (ev.EvaluateAppend), count, startat);
+ }
+
+ virtual public string [] Split (Regex regex, string input, int count, int startat)
+ {
+ ArrayList splits = new ArrayList ();
+ if (count == 0)
+ count = Int32.MaxValue;
+
+ int ptr = startat;
+ Match m = null;
+ while (--count > 0) {
+ if (m != null)
+ m = m.NextMatch ();
+ else
+ m = regex.Match (input, ptr);
+
+ if (!m.Success)
+ break;
+
+ if (regex.RightToLeft)
+ splits.Add (input.Substring (m.Index + m.Length, ptr - m.Index - m.Length));
+ else
+ splits.Add (input.Substring (ptr, m.Index - ptr));
+
+ int gcount = m.Groups.Count;
+ for (int gindex = 1; gindex < gcount; gindex++) {
+ Group grp = m.Groups [gindex];
+ splits.Add (input.Substring (grp.Index, grp.Length));
+ }
+
+ if (regex.RightToLeft)
+ ptr = m.Index;
+ else
+ ptr = m.Index + m.Length;
+
+ }
+
+ if (regex.RightToLeft && ptr >= 0)
+ splits.Add (input.Substring (0, ptr));
+ if (!regex.RightToLeft && ptr <= input.Length)
+ splits.Add (input.Substring (ptr));
+
+ return (string []) splits.ToArray (typeof (string));
+ }
+
+ virtual public Match Scan (Regex regex, string text, int start, int end)
+ {
+ throw new NotImplementedException ("Scan method must be implemented in derived classes");
+ }
+
+ virtual public string Result (string replacement, Match match)
+ {
+ return ReplacementEvaluator.Evaluate (replacement, match);
+ }
+
+ internal static string LTRReplace (Regex regex, string input, MatchAppendEvaluator evaluator, int count, int startat)
+ {
+ StringBuilder result = new StringBuilder ();
+ int ptr = startat;
+ int counter = count;
+
+ result.Append (input, 0, ptr);
+
+ Match m = regex.Match (input, startat);
+ while (m.Success) {
+ if (count != -1)
+ if (counter-- <= 0)
+ break;
+ if (m.Index < ptr)
+ throw new SystemException ("how");
+ result.Append (input, ptr, m.Index - ptr);
+ evaluator (m, result);
+
+ ptr = m.Index + m.Length;
+ m = m.NextMatch ();
+ }
+
+ if (ptr == startat)
+ return input;
+
+ result.Append (input, ptr, input.Length - ptr);
+
+ return result.ToString ();
+ }
+
+ internal static string RTLReplace (Regex regex, string input, MatchEvaluator evaluator, int count, int startat)
+ {
+ int ptr = startat;
+ int counter = count;
+
+ StringCollection pieces = new StringCollection ();
+ pieces.Add (input.Substring (ptr));
+
+ Match m = regex.Match (input, startat);
+ while (m.Success) {
+ if (count != -1)
+ if (counter-- <= 0)
+ break;
+ if (m.Index + m.Length > ptr)
+ throw new SystemException ("how");
+ pieces.Add (input.Substring (m.Index + m.Length, ptr - m.Index - m.Length));
+ pieces.Add (evaluator (m));
+
+ ptr = m.Index;
+ m = m.NextMatch ();
+ }
+
+ if (ptr == startat)
+ return input;
+
+ StringBuilder result = new StringBuilder ();
+
+ result.Append (input, 0, ptr);
+ for (int i = pieces.Count; i > 0; )
+ result.Append (pieces [--i]);
+
+ pieces.Clear ();
+
+ return result.ToString ();
+ }
+ }
+}
2007-11-07 Raja R Harinath <harinath@gmail.com>
+ Support RegexOptions.RightToLeft in Replace().
+ * BaseMachine.cs (Replace): Use either LTRReplace or RTLReplace
+ based on regex.
+ (LTRReplace): Make internal and rename the MatchAppendEvaluator
+ version of Replace to this.
+ (RTLReplace): New.
+ * Regex.cs (Replace): Use LTRReplace and RTLReplace from BaseMachine.
+ * replace.cs (ReplacementEvaluator.Evaluate): Optimize simple case.
+ Based on patch by Stephane Delcroix.
+
* replace.cs (Compile): Don't unescape string.
2007-11-01 Gert Driesen <drieseng@users.sourceforge.net>
public void Evaluate (Match m, StringBuilder sb) { sb.Append (ev (m)); }
}
- delegate void MatchAppendEvaluator (Match match, StringBuilder sb);
-
public string Replace (string input, MatchEvaluator evaluator, int count, int startat)
{
- Adapter a = new Adapter (evaluator);
- return Replace (input, new MatchAppendEvaluator (a.Evaluate), count, startat);
- }
-
- string Replace (string input, MatchAppendEvaluator evaluator, int count, int startat)
- {
- StringBuilder result = new StringBuilder ();
- int ptr = startat;
- int counter = count;
-
- result.Append (input, 0, ptr);
+ if (RightToLeft)
+ return BaseMachine.RTLReplace (this, input, evaluator, count, startat);
- Match m = Match (input, startat);
- while (m.Success) {
- if (count != -1)
- if(counter -- <= 0)
- break;
- if (m.Index < ptr)
- throw new SystemException ("how");
- result.Append (input, ptr, m.Index - ptr);
- evaluator (m, result);
-
- ptr = m.Index + m.Length;
- m = m.NextMatch ();
- }
-
- if (ptr == 0)
- return input;
-
- result.Append (input, ptr, input.Length - ptr);
+ // NOTE: If this is a cause of a lot of allocations, we can convert it to
+ // use a ThreadStatic allocation mitigator
+ Adapter a = new Adapter (evaluator);
- return result.ToString ();
+ return BaseMachine.LTRReplace (this, input, new BaseMachine.MatchAppendEvaluator (a.Evaluate),
+ count, startat);
}
public string Replace (string input, string replacement)
public string Evaluate (Match match)
{
+ if (n_pieces == 0)
+ return replacement;
StringBuilder sb = new StringBuilder ();
EvaluateAppend (match, sb);
return sb.ToString ();
public void EvaluateAppend (Match match, StringBuilder sb)
{
- int i = 0, k, count;
-
if (n_pieces == 0) {
sb.Append (replacement);
return;
}
+ int i = 0;
while (i < n_pieces) {
- k = pieces [i++];
+ int k = pieces [i++];
if (k >= 0) {
- count = pieces [i++];
+ int count = pieces [i++];
sb.Append (replacement, k, count);
} else if (k < -3) {
Group group = match.Groups [-(k + 4)];
2007-11-07 Raja R Harinath <harinath@gmail.com>
+ * MatchTest.cs (Match_Backref): New.
+ * RegexReplace.cs (direction, testcase.direction): New.
+ (testcase..ctor): Allow specifying the direction of the replace.
+ (ReplaceTests): Test replace in both directions.
+ (EvaluatorTests): New test based on #321036.
+
* RegexReplace.cs (tests): New test from #317092.
(ReplaceTests): Don't catch an AssertException.
Assert.IsNotNull (ex.Message, "#4");
}
}
+
+ [Test]
+ public void Match_Backref ()
+ {
+ Assert.IsTrue (Regex.IsMatch ("F2345678910LL1", @"(F)(2)(3)(4)(5)(6)(7)(8)(9)(10)(L)\11"), "ltr");
+ // FIXME
+ //Assert.IsTrue (Regex.IsMatch ("F2345678910LL1", @"(F)(2)(3)(4)(5)(6)(7)(8)(9)(10)(L)\11", RegexOptions.RightToLeft), "rtl");
+ }
}
}
[TestFixture]
public class RegexReplaceTest {
- struct testcase {
+ [Flags]
+ enum direction {
+ LTR = 1,
+ RTL = 2,
+ Both = 3
+ }
+ struct testcase {
public string original, pattern, replacement, expected;
- public testcase (string o, string p, string r, string e)
+ public direction direction;
+ public testcase (string o, string p, string r, string e, direction d)
{
original = o;
pattern = p;
replacement = r;
expected = e;
+ direction = d;
}
+ public testcase (string o, string p, string r, string e) : this (o, p, r, e, direction.Both) {}
}
-
+
static testcase [] tests = {
- // original pattern replacement expected
+ // original pattern replacement expected
new testcase ("text", "x", "y", "teyt" ),
new testcase ("text", "x", "$", "te$t" ),
new testcase ("text", "x", "$1", "te$1t" ),
new testcase ("text", "e(?<foo>x)", "$${foo}}", "t${foo}}t" ),
new testcase ("text", "e(?<foo>x)", "$${foo}", "t${foo}t" ),
new testcase ("text", "e(?<foo>x)", "$$", "t$t" ),
- new testcase ("text", "(?<foo>e)(?<foo>x)", "${foo}$1$2", "txx$2t" ),
- new testcase ("text", "(e)(?<foo>x)", "${foo}$1$2", "txext" ),
- new testcase ("text", "(?<foo>e)(x)", "${foo}$1$2", "texet" ),
- new testcase ("text", "(e)(?<foo>x)", "${foo}$1$2$+", "txexxt" ),
- new testcase ("text", "(?<foo>e)(x)", "${foo}$1$2$+", "texeet" ),
- new testcase ("314 1592 65358", @"\d\d\d\d|\d\d\d", "a", "a a a8" ),
- new testcase ("2 314 1592 65358", @"\d\d\d\d|\d\d\d", "a", "2 a a a8" ),
- new testcase ("<i>am not</i>", "<(.+?)>", "[$0:$1]", "[<i>:i]am not[</i>:/i]"),
- new testcase ("texts", "(?<foo>e)(x)", "${foo}$1$2$_", "texetextsts" ),
- new testcase ("texts", "(?<foo>e)(x)", "${foo}$1$2$`", "texetts" ),
- new testcase ("texts", "(?<foo>e)(x)", "${foo}$1$2$'", "texetsts" ),
- new testcase ("texts", "(?<foo>e)(x)", "${foo}$1$2$&", "texeexts" ),
- //new testcase ("F2345678910L71", @"(F)(2)(3)(4)(5)(6)(?<S>7)(8)(9)(10)(L)\11", "${S}$11$1", "77F1" ),
- new testcase ("F2345678910L71", @"(F)(2)(3)(4)(5)(6)(7)(8)(9)(10)(L)\11", "${S}$11$1", "F2345678910L71" ),
- new testcase ("F2345678910LL1", @"(F)(2)(3)(4)(5)(6)(7)(8)(9)(10)(L)\11", "${S}$11$1", "${S}LF1" ),
+ new testcase ("text", "(?<foo>e)(?<foo>x)", "${foo}$1$2", "txx$2t", direction.LTR),
+ new testcase ("text", "(?<foo>e)(?<foo>x)", "${foo}$1$2", "tee$2t", direction.RTL),
+ new testcase ("text", "(e)(?<foo>x)", "${foo}$1$2", "txext" ),
+ new testcase ("text", "(?<foo>e)(x)", "${foo}$1$2", "texet" ),
+ new testcase ("text", "(e)(?<foo>x)", "${foo}$1$2$+", "txexxt" ),
+ new testcase ("text", "(?<foo>e)(x)", "${foo}$1$2$+", "texeet" ),
+ new testcase ("314 1592 65358", @"\d\d\d\d|\d\d\d", "a", "a a a8", direction.LTR),
+ new testcase ("314 1592 65358", @"\d\d\d\d|\d\d\d", "a", "a a 6a", direction.RTL),
+ new testcase ("2 314 1592 65358", @"\d\d\d\d|\d\d\d", "a", "2 a a a8", direction.LTR),
+ new testcase ("2 314 1592 65358", @"\d\d\d\d|\d\d\d", "a", "2 a a 6a", direction.RTL),
+ new testcase ("<i>am not</i>", "<(.+?)>", "[$0:$1]", "[<i>:i]am not[</i>:/i]"),
+ new testcase ("texts", "(?<foo>e)(x)", "${foo}$1$2$_", "texetextsts" ),
+ new testcase ("texts", "(?<foo>e)(x)", "${foo}$1$2$`", "texetts" ),
+ new testcase ("texts", "(?<foo>e)(x)", "${foo}$1$2$'", "texetsts" ),
+ new testcase ("texts", "(?<foo>e)(x)", "${foo}$1$2$&", "texeexts" ),
+ //new testcase ("F2345678910L71", @"(F)(2)(3)(4)(5)(6)(?<S>7)(8)(9)(10)(L)\11", "${S}$11$1", "77F1" ),
+ new testcase ("F2345678910L71", @"(F)(2)(3)(4)(5)(6)(7)(8)(9)(10)(L)\11", "${S}$11$1", "F2345678910L71" ),
+ new testcase ("F2345678910LL1", @"(F)(2)(3)(4)(5)(6)(7)(8)(9)(10)(L)\11", "${S}$11$1", "${S}LF1", /*FIXME: this should work in RTL direction too */ direction.LTR),
new testcase ("a", "a", @"\\", @"\\"),
};
string result = "";
int i = 0;
foreach (testcase test in tests) {
- try {
- result = Regex.Replace (test.original, test.pattern, test.replacement);
- } catch (Exception e) {
- Assert.Fail ("rr#{0} Exception thrown: " + e.ToString (), i);
+ if ((test.direction & direction.LTR) == direction.LTR) {
+ try {
+ result = Regex.Replace (test.original, test.pattern, test.replacement);
+ } catch (Exception e) {
+ Assert.Fail ("rr#{0}ltr Exception thrown: " + e.ToString (), i);
+ }
+ Assert.AreEqual (test.expected, result, "rr#{0}ltr: {1} ~ s,{2},{3},", i,
+ test.original, test.pattern, test.replacement);
+ }
+
+ if ((test.direction & direction.RTL) == direction.RTL) {
+ try {
+ result = Regex.Replace (test.original, test.pattern, test.replacement, RegexOptions.RightToLeft);
+ } catch (Exception e) {
+ Assert.Fail ("rr#{0}rtl Exception thrown: " + e.ToString (), i);
+ }
+ Assert.AreEqual (test.expected, result, "rr#{0}rtl: {1} ~ s,{2},{3},", i,
+ test.original, test.pattern, test.replacement);
}
- Assert.AreEqual (result, test.expected, "rr#{0}: {1} ~ s,{2},{3},", i,
- test.original, test.pattern, test.replacement);
++i;
}
}
+
+ static string substitute (Match m)
+ {
+ switch (m.Value.Substring(2, m.Length - 3)) {
+ case "foo": return "bar";
+ case "hello": return "world";
+ default: return "ERROR";
+ }
+ }
+
+ static string resolve (string val, Regex r)
+ {
+ MatchEvaluator ev = new MatchEvaluator (substitute);
+ while (r.IsMatch (val))
+ val = r.Replace (val, ev);
+ return val;
+ }
+
+ [Test] // #321036
+ public void EvaluatorTests ()
+ {
+ string test = "@(foo) @(hello)";
+ string regex = "\\@\\([^\\)]*?\\)";
+ Assert.AreEqual ("bar world", resolve (test, new Regex (regex)), "ltr");
+ Assert.AreEqual ("bar world", resolve (test, new Regex (regex, RegexOptions.RightToLeft)), "rtl");
+ }
}
}