3 // namespace: System.Text.RegularExpressions
6 // author: Dan Lewis (dlewis@gmx.co.uk)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
34 using Parser = System.Text.RegularExpressions.Syntax.Parser;
36 namespace System.Text.RegularExpressions {
38 class ReplacementEvaluator {
39 public static string Evaluate (string replacement, Match match) {
40 ReplacementEvaluator ev = new ReplacementEvaluator (match.Regex, replacement);
41 return ev.Evaluate (match);
44 public ReplacementEvaluator (Regex regex, string replacement) {
46 this.replacement = replacement;
52 public string Evaluate (Match match)
54 StringBuilder sb = new StringBuilder ();
55 EvaluateAppend (match, sb);
56 return sb.ToString ();
59 public void EvaluateAppend (Match match, StringBuilder sb)
64 sb.Append (replacement);
68 while (i < n_pieces) {
72 sb.Append (replacement, k, count);
74 Group group = match.Groups [-(k + 4)];
75 sb.Append (group.Text, group.Index, group.Length);
77 sb.Append (match.Text);
79 sb.Append (match.Text, 0, match.Index);
81 int matchend = match.Index + match.Length;
82 sb.Append (match.Text, matchend, match.Text.Length - matchend);
87 void Ensure (int size)
94 pieces = new int [new_size];
95 } else if (size >= pieces.Length) {
96 new_size = pieces.Length + (pieces.Length >> 1);
99 int [] new_pieces = new int [new_size];
100 Array.Copy (pieces, new_pieces, n_pieces);
105 void AddFromReplacement (int start, int end)
109 Ensure (n_pieces + 2);
110 pieces [n_pieces++] = start;
111 pieces [n_pieces++] = end - start;
116 Ensure (n_pieces + 1);
117 pieces [n_pieces++] = i;
121 private void Compile () {
122 replacement = Parser.Unescape (replacement);
124 int anchor = 0, ptr = 0, saveptr;
126 while (ptr < replacement.Length) {
127 c = replacement [ptr++];
132 // If the '$' was the last character, just emit it as is
133 if (ptr == replacement.Length)
137 if (replacement [ptr] == '$') {
138 // Everthing from 'anchor' upto and including the first '$' is copied from the replacement string
139 AddFromReplacement (anchor, ptr);
140 // skip over the second '$'.
147 int from_match = CompileTerm (ref ptr);
149 // We couldn't recognize the term following the '$'. Just treat it as a literal.
150 // 'ptr' has already been advanced, no need to rewind it back
154 AddFromReplacement (anchor, saveptr);
159 // If we never needed to advance anchor, it means the result is the whole replacement string.
160 // We optimize that case by never allocating the pieces array.
162 AddFromReplacement (anchor, ptr);
165 private int CompileTerm (ref int ptr) {
166 char c = replacement [ptr];
168 if (Char.IsDigit (c)) { // numbered group
169 int n = Parser.ParseDecimal (replacement, ref ptr);
170 if (n < 0 || n > regex.GroupCount)
179 case '{': { // named group
184 // The parser is written such that there are few explicit range checks
185 // and depends on 'IndexOutOfRangeException' being thrown.
187 if (Char.IsDigit (replacement [ptr])) {
188 n = Parser.ParseDecimal (replacement, ref ptr);
191 name = Parser.ParseName (replacement, ref ptr);
193 } catch (IndexOutOfRangeException) {
194 ptr = replacement.Length;
198 if (ptr == replacement.Length || replacement[ptr] != '}' || name == null)
200 ++ptr; // Swallow the '}'
203 n = regex.GroupNumberFromName (name);
205 if (n < 0 || n > regex.GroupCount)
211 case '&': // entire match. Value should be same as $0
214 case '`': // text before match
217 case '\'': // text after match
220 case '+': // last group
221 return -regex.GroupCount - 4;
223 case '_': // entire text
233 private int [] pieces;