while (ptr < replacement.Length) {
c = replacement[ptr ++];
- if (c == '$') {
- if (replacement[ptr] != '$')
- term = CompileTerm (replacement, ref ptr);
- else
- ++ ptr;
+ if (c != '$') {
+ literal.Append (c);
+ continue;
+ }
+
+ // If the '$' was the last character, just emit it as is
+ if (ptr == replacement.Length) {
+ literal.Append (c);
+ break;
}
+ // If we saw a '$$'
+ if (replacement[ptr] == '$') {
+ literal.Append (c);
+ ++ ptr;
+ continue;
+ }
+
+ int saveptr = ptr - 1;
+
+ term = CompileTerm (replacement, ref ptr);
+
if (term != null) {
term.Literal = literal.ToString ();
terms.Add (term);
term = null;
literal.Length = 0;
+ } else {
+ // If 'CompileTerm' couldn't identify it, don't abort, simply copy it over.
+ literal.Append (replacement, saveptr, ptr - saveptr);
}
- else
- literal.Append (c);
}
if (term == null && literal.Length > 0) {
if (Char.IsDigit (c)) { // numbered group
int n = Parser.ParseDecimal (str, ref ptr);
if (n < 0 || n > regex.GroupCount)
- throw new ArgumentException ("Bad group number.");
+ return null;
return new Term (TermOp.Match, n);
}
switch (c) {
case '{': { // named group
- string name = Parser.ParseName (str, ref ptr);
- if (str[ptr ++] != '}' || name == null)
- throw new ArgumentException ("Bad group name.");
-
- int n = regex.GroupNumberFromName (name);
-
- if (n < 0)
- throw new ArgumentException ("Bad group name.");
+ string name;
+ int n = -1;
+
+ try {
+ // The parser is written such that there are few explicit range checks
+ // and depends on 'IndexOutOfRangeException' being thrown.
+
+ if (Char.IsDigit (str [ptr])) {
+ n = Parser.ParseDecimal (str, ref ptr);
+ name = "";
+ } else {
+ name = Parser.ParseName (str, ref ptr);
+ }
+ } catch (IndexOutOfRangeException) {
+ ptr = str.Length;
+ return null;
+ }
+
+ if (ptr == str.Length || str[ptr] != '}' || name == null)
+ return null;
+ ++ptr; // Swallow the '}'
+
+ if (name != "")
+ n = regex.GroupNumberFromName (name);
+
+ if (n < 0 || n > regex.GroupCount)
+ return null;
return new Term (TermOp.Match, n);
}
return new Term (TermOp.All, 0);
default:
- throw new ArgumentException ("Bad replacement pattern.");
+ return null;
}
}
--- /dev/null
+//
+// RegexReplace.cs
+//
+// Author:
+// Raja R Harinath <rharinath@novell.com>
+//
+// (C) 2005, Novell Inc.
+
+using System;
+using System.Text.RegularExpressions;
+
+using NUnit.Framework;
+
+namespace MonoTests.System.Text.RegularExpressions {
+
+ [TestFixture]
+ public class RegexReplaceTest {
+ struct testcase {
+ public string original, pattern, replacement, expected;
+ public testcase (string o, string p, string r, string e)
+ {
+ original = o;
+ pattern = p;
+ replacement = r;
+ expected = e;
+ }
+ }
+
+ static testcase [] tests = {
+ // 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", "x", "${1}", "te${1}t" ),
+ new testcase ("text", "x", "$5", "te$5t" ),
+ new testcase ("te(x)t", "x", "$5", "te($5)t" ),
+ new testcase ("text", "x", "${5", "te${5t" ),
+ new testcase ("text", "x", "${foo", "te${foot" ),
+ new testcase ("text", "(x)", "$5", "te$5t" ),
+ new testcase ("text", "(x)", "$1", "text" ),
+ new testcase ("text", "e(x)", "$1", "txt" ),
+ new testcase ("text", "e(x)", "$5", "t$5t" ),
+ new testcase ("text", "e(x)", "$4", "t$4t" ),
+ new testcase ("text", "e(x)", "$3", "t$3t" ),
+ new testcase ("text", "e(x)", "${1}", "txt" ),
+ new testcase ("text", "e(x)", "${3}", "t${3}t" ),
+ new testcase ("text", "e(x)", "${1}${3}", "tx${3}t" ),
+ new testcase ("text", "e(x)", "${1}${name}", "tx${name}t" ),
+ new testcase ("text", "e(?<foo>x)", "${1}${name}", "tx${name}t" ),
+ new testcase ("text", "e(?<foo>x)", "${1}${foo}", "txxt" ),
+ new testcase ("text", "e(?<foo>x)", "${goll}${foo}", "t${goll}xt" ),
+ new testcase ("text", "e(?<foo>x)", "${goll${foo}", "t${gollxt" ),
+ new testcase ("text", "e(?<foo>x)", "${goll${foo}}", "t${gollx}t" ),
+ new testcase ("text", "e(?<foo>x)", "$${foo}}", "t${foo}}t" ),
+ new testcase ("text", "e(?<foo>x)", "${${foo}}", "t${x}t" ),
+ new testcase ("text", "e(?<foo>x)", "$${foo}}", "t${foo}}t" ),
+ new testcase ("text", "e(?<foo>x)", "$${bfoo}}", "t${bfoo}}t" ),
+ 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" ),
+ };
+
+ [Test]
+ public void ReplaceTests ()
+ {
+ string result;
+ int i = 0;
+ foreach (testcase test in tests) {
+ try {
+ result = Regex.Replace (test.original, test.pattern, test.replacement);
+ Assert.AreEqual (result, test.expected, "rr#{0}: {1} ~ s,{2},{3},", i,
+ test.original, test.pattern, test.replacement);
+ } catch (Exception e) {
+ Assert.Fail ("rr#{0}: Exception thrown", i);
+ }
+ ++i;
+ }
+ }
+ }
+}