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)
41 ReplacementEvaluator ev = new ReplacementEvaluator (match.Regex, replacement);
42 return ev.Evaluate (match);
45 public ReplacementEvaluator (Regex regex, string replacement)
48 this.replacement = replacement;
54 public string Evaluate (Match match)
58 StringBuilder sb = new StringBuilder ();
59 EvaluateAppend (match, sb);
60 return sb.ToString ();
63 public void EvaluateAppend (Match match, StringBuilder sb)
66 sb.Append (replacement);
71 while (i < n_pieces) {
74 int count = pieces [i++];
75 sb.Append (replacement, k, count);
77 Group group = match.Groups [-(k + 4)];
78 sb.Append (group.Text, group.Index, group.Length);
80 sb.Append (match.Text);
82 sb.Append (match.Text, 0, match.Index);
84 int matchend = match.Index + match.Length;
85 sb.Append (match.Text, matchend, match.Text.Length - matchend);
90 public bool NeedsGroupsOrCaptures {
99 void Ensure (int size)
102 if (pieces == null) {
106 pieces = new int [new_size];
107 } else if (size >= pieces.Length) {
108 new_size = pieces.Length + (pieces.Length >> 1);
111 int [] new_pieces = new int [new_size];
112 Array.Copy (pieces, new_pieces, n_pieces);
117 void AddFromReplacement (int start, int end)
121 Ensure (n_pieces + 2);
122 pieces [n_pieces++] = start;
123 pieces [n_pieces++] = end - start;
128 Ensure (n_pieces + 1);
129 pieces [n_pieces++] = i;
133 private void Compile ()
135 int anchor = 0, ptr = 0, saveptr;
137 while (ptr < replacement.Length) {
138 c = replacement [ptr++];
143 // If the '$' was the last character, just emit it as is
144 if (ptr == replacement.Length)
148 if (replacement [ptr] == '$') {
149 // Everthing from 'anchor' upto and including the first '$' is copied from the replacement string
150 AddFromReplacement (anchor, ptr);
151 // skip over the second '$'.
158 int from_match = CompileTerm (ref ptr);
160 // We couldn't recognize the term following the '$'. Just treat it as a literal.
161 // 'ptr' has already been advanced, no need to rewind it back
165 AddFromReplacement (anchor, saveptr);
170 // If we never needed to advance anchor, it means the result is the whole replacement string.
171 // We optimize that case by never allocating the pieces array.
173 AddFromReplacement (anchor, ptr);
176 private int CompileTerm (ref int ptr)
178 char c = replacement [ptr];
180 if (Char.IsDigit (c)) { // numbered group
181 int n = Parser.ParseDecimal (replacement, ref ptr);
182 if (n < 0 || n > regex.GroupCount)
191 case '{': { // named group
196 // The parser is written such that there are few explicit range checks
197 // and depends on 'IndexOutOfRangeException' being thrown.
199 if (Char.IsDigit (replacement [ptr])) {
200 n = Parser.ParseDecimal (replacement, ref ptr);
203 name = Parser.ParseName (replacement, ref ptr);
205 } catch (IndexOutOfRangeException) {
206 ptr = replacement.Length;
210 if (ptr == replacement.Length || replacement[ptr] != '}' || name == null)
212 ++ptr; // Swallow the '}'
215 n = regex.GroupNumberFromName (name);
217 if (n < 0 || n > regex.GroupCount)
223 case '&': // entire match. Value should be same as $0
226 case '`': // text before match
229 case '\'': // text after match
232 case '+': // last group
233 return -regex.GroupCount - 4;
235 case '_': // entire text
245 private int [] pieces;