-//\r
-// assembly: System\r
-// namespace: System.Text.RegularExpressions\r
-// file: replace.cs\r
-//\r
-// author: Dan Lewis (dlewis@gmx.co.uk)\r
-// (c) 2002\r
+//
+// assembly: System
+// namespace: System.Text.RegularExpressions
+// file: replace.cs
+//
+// author: Dan Lewis (dlewis@gmx.co.uk)
+// (c) 2002
//
// Permission is hereby granted, free of charge, to any person obtaining
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-\r
-using System;\r
-using System.Text;\r
-using System.Collections;\r
-\r
-using Parser = System.Text.RegularExpressions.Syntax.Parser;\r
-\r
-namespace System.Text.RegularExpressions {\r
-\r
- class ReplacementEvaluator {\r
- public static string Evaluate (string replacement, Match match) {\r
- ReplacementEvaluator ev = new ReplacementEvaluator (match.Regex, replacement);\r
- return ev.Evaluate (match);\r
- }\r
-\r
- public ReplacementEvaluator (Regex regex, string replacement) {\r
- this.regex = regex;\r
- terms = new ArrayList ();\r
- Compile (replacement);\r
- }\r
-\r
- public string Evaluate (Match match) {\r
- StringBuilder result = new StringBuilder ();\r
- foreach (Term term in terms)\r
- result.Append (term.GetResult (match));\r
-\r
- return result.ToString ();\r
- }\r
-\r
- // private\r
-\r
- private void Compile (string replacement) {\r
- replacement = Parser.Unescape (replacement);\r
- StringBuilder literal = new StringBuilder ();\r
-\r
- int ptr = 0;\r
- char c;\r
- Term term = null;\r
- while (ptr < replacement.Length) {\r
- c = replacement[ptr ++];\r
-\r
- if (c == '$') {\r
- if (replacement[ptr] != '$') \r
- term = CompileTerm (replacement, ref ptr);\r
- else\r
- ++ ptr;\r
- }\r
-\r
- if (term != null) {\r
- term.Literal = literal.ToString ();\r
- terms.Add (term);\r
-\r
- term = null;\r
- literal.Length = 0;\r
- }\r
- else\r
- literal.Append (c);\r
- }\r
-\r
- if (term == null && literal.Length > 0) {\r
- terms.Add (new Term (literal.ToString ()));\r
- }\r
- }\r
-\r
- private Term CompileTerm (string str, ref int ptr) {\r
- char c = str[ptr];\r
-\r
- if (Char.IsDigit (c)) { // numbered group\r
- int n = Parser.ParseDecimal (str, ref ptr);\r
- if (n < 0 || n > regex.GroupCount)\r
- throw new ArgumentException ("Bad group number.");\r
- \r
- return new Term (TermOp.Match, n);\r
- }\r
- \r
- ++ ptr;\r
-\r
- switch (c) {\r
- case '{': { // named group\r
- string name = Parser.ParseName (str, ref ptr);\r
- if (str[ptr ++] != '}' || name == null)\r
- throw new ArgumentException ("Bad group name.");\r
- \r
- int n = regex.GroupNumberFromName (name);\r
- \r
- if (n < 0)\r
- throw new ArgumentException ("Bad group name.");\r
-\r
- return new Term (TermOp.Match, n);\r
- }\r
-\r
- case '&': // entire match\r
- return new Term (TermOp.Match, 0);\r
-\r
- case '`': // text before match\r
- return new Term (TermOp.PreMatch, 0);\r
-\r
- case '\'': // text after match\r
- return new Term (TermOp.PostMatch, 0);\r
-\r
- case '+': // last group\r
- return new Term (TermOp.Match, regex.GroupCount - 1);\r
-\r
- case '_': // entire text\r
- return new Term (TermOp.All, 0);\r
-\r
- default:\r
- throw new ArgumentException ("Bad replacement pattern.");\r
- }\r
- }\r
-\r
- private Regex regex;\r
- private ArrayList terms;\r
-\r
- private enum TermOp {\r
- None, // no action\r
- Match, // input within group\r
- PreMatch, // input before group\r
- PostMatch, // input after group\r
- All // entire input\r
- }\r
-\r
- private class Term {\r
- public Term (TermOp op, int arg) {\r
- this.op = op;\r
- this.arg = arg;\r
- this.literal = "";\r
- }\r
-\r
- public Term (string literal) {\r
- this.op = TermOp.None;\r
- this.arg = 0;\r
- this.literal = literal;\r
- }\r
-\r
- public string Literal {\r
- set { literal = value; }\r
- }\r
-\r
- public string GetResult (Match match) {\r
- Group group = match.Groups[arg];\r
- \r
- switch (op) {\r
- case TermOp.None:\r
- return literal;\r
-\r
- case TermOp.Match:\r
- return literal + group.Value;\r
-\r
- case TermOp.PreMatch:\r
- return literal + group.Text.Substring (0, group.Index);\r
-\r
- case TermOp.PostMatch:\r
- return literal + group.Text.Substring (group.Index + group.Length);\r
-\r
- case TermOp.All:\r
- return literal + group.Text;\r
- }\r
-\r
- return "";\r
- }\r
- \r
- public TermOp op; // term type\r
- public int arg; // group argument\r
- public string literal; // literal to prepend\r
-\r
- public override string ToString () {\r
- return op.ToString () + "(" + arg + ") " + literal;\r
- }\r
- }\r
- }\r
-}\r
+
+using System;
+using System.Text;
+using System.Collections;
+
+using Parser = System.Text.RegularExpressions.Syntax.Parser;
+
+namespace System.Text.RegularExpressions {
+
+ class ReplacementEvaluator {
+ public static string Evaluate (string replacement, Match match) {
+ ReplacementEvaluator ev = new ReplacementEvaluator (match.Regex, replacement);
+ return ev.Evaluate (match);
+ }
+
+ public ReplacementEvaluator (Regex regex, string replacement) {
+ this.regex = regex;
+ this.replacement = replacement;
+ this.pieces = null;
+ this.n_pieces = 0;
+ Compile ();
+ }
+
+ public string Evaluate (Match match)
+ {
+ 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;
+ }
+
+ while (i < n_pieces) {
+ k = pieces [i++];
+ if (k >= 0) {
+ count = pieces [i++];
+ sb.Append (replacement, k, count);
+ } else if (k < -3) {
+ Group group = match.Groups [-(k + 4)];
+ sb.Append (group.Text, group.Index, group.Length);
+ } else if (k == -1) {
+ sb.Append (match.Text);
+ } else if (k == -2) {
+ sb.Append (match.Text, 0, match.Index);
+ } else { // k == -3
+ int matchend = match.Index + match.Length;
+ sb.Append (match.Text, matchend, match.Text.Length - matchend);
+ }
+ }
+ }
+
+ void Ensure (int size)
+ {
+ int new_size;
+ if (pieces == null) {
+ new_size = 4;
+ if (new_size < size)
+ new_size = size;
+ pieces = new int [new_size];
+ } else if (size >= pieces.Length) {
+ new_size = pieces.Length + (pieces.Length >> 1);
+ if (new_size < size)
+ new_size = size;
+ int [] new_pieces = new int [new_size];
+ Array.Copy (pieces, new_pieces, n_pieces);
+ pieces = new_pieces;
+ }
+ }
+
+ void AddFromReplacement (int start, int end)
+ {
+ if (start == end)
+ return;
+ Ensure (n_pieces + 2);
+ pieces [n_pieces++] = start;
+ pieces [n_pieces++] = end - start;
+ }
+
+ void AddInt (int i)
+ {
+ Ensure (n_pieces + 1);
+ pieces [n_pieces++] = i;
+ }
+
+ // private
+ private void Compile () {
+ replacement = Parser.Unescape (replacement);
+
+ int anchor = 0, ptr = 0, saveptr;
+ char c;
+ while (ptr < replacement.Length) {
+ c = replacement [ptr++];
+
+ if (c != '$')
+ continue;
+
+ // If the '$' was the last character, just emit it as is
+ if (ptr == replacement.Length)
+ break;
+
+ // If we saw a '$$'
+ if (replacement [ptr] == '$') {
+ // Everthing from 'anchor' upto and including the first '$' is copied from the replacement string
+ AddFromReplacement (anchor, ptr);
+ // skip over the second '$'.
+ anchor = ++ptr;
+ continue;
+ }
+
+ saveptr = ptr - 1;
+
+ int from_match = CompileTerm (ref ptr);
+
+ // We couldn't recognize the term following the '$'. Just treat it as a literal.
+ // 'ptr' has already been advanced, no need to rewind it back
+ if (from_match >= 0)
+ continue;
+
+ AddFromReplacement (anchor, saveptr);
+ AddInt (from_match);
+ anchor = ptr;
+ }
+
+ // If we never needed to advance anchor, it means the result is the whole replacement string.
+ // We optimize that case by never allocating the pieces array.
+ if (anchor != 0)
+ AddFromReplacement (anchor, ptr);
+ }
+
+ private int CompileTerm (ref int ptr) {
+ char c = replacement [ptr];
+
+ if (Char.IsDigit (c)) { // numbered group
+ int n = Parser.ParseDecimal (replacement, ref ptr);
+ if (n < 0 || n > regex.GroupCount)
+ return 0;
+
+ return -n - 4;
+ }
+
+ ++ ptr;
+
+ switch (c) {
+ case '{': { // named group
+ 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 (replacement [ptr])) {
+ n = Parser.ParseDecimal (replacement, ref ptr);
+ name = "";
+ } else {
+ name = Parser.ParseName (replacement, ref ptr);
+ }
+ } catch (IndexOutOfRangeException) {
+ ptr = replacement.Length;
+ return 0;
+ }
+
+ if (ptr == replacement.Length || replacement[ptr] != '}' || name == null)
+ return 0;
+ ++ptr; // Swallow the '}'
+
+ if (name != "")
+ n = regex.GroupNumberFromName (name);
+
+ if (n < 0 || n > regex.GroupCount)
+ return 0;
+
+ return -n - 4;
+ }
+
+ case '&': // entire match. Value should be same as $0
+ return -4;
+
+ case '`': // text before match
+ return -2;
+
+ case '\'': // text after match
+ return -3;
+
+ case '+': // last group
+ return -regex.GroupCount - 4;
+
+ case '_': // entire text
+ return -1;
+
+ default:
+ return 0;
+ }
+ }
+
+ private Regex regex;
+ int n_pieces;
+ private int [] pieces;
+ string replacement;
+ }
+}