/[dotgnu-pnet]/pnetlib/System/Text/RegularExpressions/replace.cs
ViewVC logotype

Diff of /pnetlib/System/Text/RegularExpressions/replace.cs

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.3 by ktreichel, Wed Jun 9 17:01:24 2004 UTC revision 1.4 by t3rmin4t0r, Fri Aug 19 23:36:38 2005 UTC
# Line 1  Line 1 
1  //  //
2  // assembly:    System  // assembly:    System
3  // namespace:   System.Text.RegularExpressions  // namespace:   System.Text.RegularExpressions
4  // file:        replace.cs  // file:        replace.cs
5  //  //
6  // author:      Dan Lewis (dlewis@gmx.co.uk)  // author:      Dan Lewis (dlewis@gmx.co.uk)
7  //              (c) 2002  //              (c) 2002
8    
9  using System;  //
10  using System.Text;  // Permission is hereby granted, free of charge, to any person obtaining
11  using System.Collections;  // a copy of this software and associated documentation files (the
12    // "Software"), to deal in the Software without restriction, including
13  using Parser = System.Text.RegularExpressions.Syntax.Parser;  // without limitation the rights to use, copy, modify, merge, publish,
14    // distribute, sublicense, and/or sell copies of the Software, and to
15  namespace System.Text.RegularExpressions {  // permit persons to whom the Software is furnished to do so, subject to
16    // the following conditions:
17          class ReplacementEvaluator {  //
18                  public static string Evaluate (string replacement, Match match) {  // The above copyright notice and this permission notice shall be
19                          ReplacementEvaluator ev = new ReplacementEvaluator (match.Regex, replacement);  // included in all copies or substantial portions of the Software.
20                          return ev.Evaluate (match);  //
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                  public ReplacementEvaluator (Regex regex, string replacement) {  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24                          this.regex = regex;  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25                          terms = new ArrayList ();  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26                          Compile (replacement);  // 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.
28    //
29                  public string Evaluate (Match match) {  
30                          StringBuilder result = new StringBuilder ();  using System;
31                          foreach (Term term in terms)  using System.Text;
32                                  result.Append (term.GetResult (match));  using System.Collections;
33    
34                          return result.ToString ();  using Parser = System.Text.RegularExpressions.Syntax.Parser;
35                  }  
36    namespace System.Text.RegularExpressions {
37                  // private  
38            class ReplacementEvaluator {
39                  private void Compile (string replacement) {                  public static string Evaluate (string replacement, Match match) {
40                          replacement = Parser.Unescape (replacement);                          ReplacementEvaluator ev = new ReplacementEvaluator (match.Regex, replacement);
41                          string literal = "";                          return ev.Evaluate (match);
42                    }
43                          int ptr = 0;  
44                          char c;                  public ReplacementEvaluator (Regex regex, string replacement) {
45                          Term term = null;                          this.regex = regex;
46                          while (ptr < replacement.Length) {                          this.replacement = replacement;
47                                  c = replacement[ptr ++];                          this.pieces = null;
48                            this.n_pieces = 0;
49                                  if (c == '$') {                          Compile ();
50                                          if (replacement[ptr] != '$')                  }
51                                                  term = CompileTerm (replacement, ref ptr);  
52                                          else                  public string Evaluate (Match match)
53                                                  ++ ptr;                  {
54                                  }                          StringBuilder sb = new StringBuilder ();
55                            EvaluateAppend (match, sb);
56                                  if (term != null) {                          return sb.ToString ();
57                                          term.Literal = literal;                  }
58                                          terms.Add (term);  
59                    public void EvaluateAppend (Match match, StringBuilder sb)
60                                          term = null;                  {
61                                          literal = "";                          int i = 0, k, count;
62                                  }  
63                                  else                          if (n_pieces == 0) {
64                                          literal += c;                                  sb.Append (replacement);
65                          }                                  return;
66                            }
67                          if (term == null && literal.Length > 0) {  
68                                  terms.Add (new Term (literal));                          while (i < n_pieces) {
69                          }                                  k = pieces [i++];
70                  }                                  if (k >= 0) {
71                                            count = pieces [i++];
72                  private Term CompileTerm (string str, ref int ptr) {                                          sb.Append (replacement, k, count);
73                          char c = str[ptr];                                  } else if (k < -3) {
74                                            Group group = match.Groups [-(k + 4)];
75                          if (Char.IsDigit (c)) {         // numbered group                                          sb.Append (group.Text, group.Index, group.Length);
76                                  int n = Parser.ParseDecimal (str, ref ptr);                                  } else if (k == -1) {
77                                  if (n < 0 || n > regex.GroupCount)                                          sb.Append (match.Text);
78                                          throw new ArgumentException ("Bad group number.");                                  } else if (k == -2) {
79                                                                            sb.Append (match.Text, 0, match.Index);
80                                  return new Term (TermOp.Match, n);                                  } else { // k == -3
81                          }                                          int matchend = match.Index + match.Length;
82                                                                    sb.Append (match.Text, matchend, match.Text.Length - matchend);
83                          ++ ptr;                                  }
84                            }
85                          switch (c) {                  }
86                          case '{': {                     // named group  
87                                  string name = Parser.ParseName (str, ref ptr);                  void Ensure (int size)
88                                  if (str[ptr ++] != '}' || name == null)                  {
89                                          throw new ArgumentException ("Bad group name.");                          int new_size;
90                                                            if (pieces == null) {
91                                  int n = regex.GroupNumberFromName (name);                                  new_size = 4;
92                                                                    if (new_size < size)
93                                  if (n < 0)                                          new_size = size;
94                                          throw new ArgumentException ("Bad group name.");                                  pieces = new int [new_size];
95                            } else if (size >= pieces.Length) {
96                                  return new Term (TermOp.Match, n);                                  new_size = pieces.Length + (pieces.Length >> 1);
97                          }                                  if (new_size < size)
98                                            new_size = size;
99                          case '&':                       // entire match                                  int [] new_pieces = new int [new_size];
100                                  return new Term (TermOp.Match, 0);                                  Array.Copy (pieces, new_pieces, n_pieces);
101                                    pieces = new_pieces;
102                          case '`':                       // text before match                          }
103                                  return new Term (TermOp.PreMatch, 0);                  }
104    
105                          case '\'':                      // text after match                  void AddFromReplacement (int start, int end)
106                                  return new Term (TermOp.PostMatch, 0);                  {
107                            if (start == end)
108                          case '+':                       // last group                                  return;
109                                  return new Term (TermOp.Match, regex.GroupCount - 1);                          Ensure (n_pieces + 2);
110                            pieces [n_pieces++] = start;
111                          case '_':                       // entire text                          pieces [n_pieces++] = end - start;
112                                  return new Term (TermOp.All, 0);                  }
113    
114                          default:                  void AddInt (int i)
115                                  throw new ArgumentException ("Bad replacement pattern.");                  {
116                          }                          Ensure (n_pieces + 1);
117                  }                          pieces [n_pieces++] = i;
118                    }
119                  private Regex regex;  
120                  private ArrayList terms;                  // private
121                    private void Compile () {
122                  private enum TermOp {                          replacement = Parser.Unescape (replacement);
123                          None,                           // no action  
124                          Match,                          // input within group                          int anchor = 0, ptr = 0, saveptr;
125                          PreMatch,                       // input before group                          char c;
126                          PostMatch,                      // input after group                          while (ptr < replacement.Length) {
127                          All                             // entire input                                  c = replacement [ptr++];
128                  }  
129                                    if (c != '$')
130                  private class Term {                                          continue;
131                          public Term (TermOp op, int arg) {  
132                                  this.op = op;                                  // If the '$' was the last character, just emit it as is
133                                  this.arg = arg;                                  if (ptr == replacement.Length)
134                                  this.literal = "";                                          break;
135                          }  
136                                    // If we saw a '$$'
137                          public Term (string literal) {                                  if (replacement [ptr] == '$') {
138                                  this.op = TermOp.None;                                          // Everthing from 'anchor' upto and including the first '$' is copied from the replacement string
139                                  this.arg = 0;                                          AddFromReplacement (anchor, ptr);
140                                  this.literal = literal;                                          // skip over the second '$'.
141                          }                                          anchor = ++ptr;
142                                            continue;
143                          public string Literal {                                  }
144                                  set { literal = value; }  
145                          }                                  saveptr = ptr - 1;
146    
147                          public string GetResult (Match match) {                                  int from_match = CompileTerm (ref ptr);
148                                  Group group = match.Groups[arg];  
149                                                            // We couldn't recognize the term following the '$'.  Just treat it as a literal.
150                                  switch (op) {                                  // 'ptr' has already been advanced, no need to rewind it back
151                                  case TermOp.None:                                  if (from_match >= 0)
152                                          return literal;                                          continue;
153    
154                                  case TermOp.Match:                                  AddFromReplacement (anchor, saveptr);
155                                          return literal + group.Value;                                  AddInt (from_match);
156                                    anchor = ptr;
157                                  case TermOp.PreMatch:                          }
158                                          return literal + group.Text.Substring (0, group.Index);  
159                            // If we never needed to advance anchor, it means the result is the whole replacement string.
160                                  case TermOp.PostMatch:                          // We optimize that case by never allocating the pieces array.
161                                          return literal + group.Text.Substring (group.Index + group.Length);                          if (anchor != 0)
162                                    AddFromReplacement (anchor, ptr);
163                                  case TermOp.All:                  }
164                                          return literal + group.Text;  
165                                  }                  private int CompileTerm (ref int ptr) {
166                            char c = replacement [ptr];
167                                  return "";  
168                          }                          if (Char.IsDigit (c)) {         // numbered group
169                                                    int n = Parser.ParseDecimal (replacement, ref ptr);
170                          public TermOp op;               // term type                                  if (n < 0 || n > regex.GroupCount)
171                          public int arg;                 // group argument                                          return 0;
172                          public string literal;          // literal to prepend                                  
173                                    return -n - 4;
174                          public override string ToString () {                          }
175                                  return op.ToString () + "(" + arg + ") " + literal;                          
176                          }                          ++ ptr;
177                  }  
178          }                          switch (c) {
179  }                          case '{': {                     // named group
180                                    string name;
181                                    int n = -1;
182    
183                                    try {
184                                            // The parser is written such that there are few explicit range checks
185                                            // and depends on 'IndexOutOfRangeException' being thrown.
186    
187                                            if (Char.IsDigit (replacement [ptr])) {
188                                                    n = Parser.ParseDecimal (replacement, ref ptr);
189                                                    name = "";
190                                            } else {
191                                                    name = Parser.ParseName (replacement, ref ptr);
192                                            }
193                                    } catch (IndexOutOfRangeException) {
194                                            ptr = replacement.Length;
195                                            return 0;
196                                    }
197    
198                                    if (ptr == replacement.Length || replacement[ptr] != '}' || name == null)
199                                            return 0;
200                                    ++ptr;                  // Swallow the '}'
201    
202                                    if (name != "")
203                                            n = regex.GroupNumberFromName (name);
204    
205                                    if (n < 0 || n > regex.GroupCount)
206                                            return 0;
207    
208                                    return -n - 4;
209                            }
210    
211                            case '&':                       // entire match.  Value should be same as $0
212                                    return -4;
213    
214                            case '`':                       // text before match
215                                    return -2;
216    
217                            case '\'':                      // text after match
218                                    return -3;
219    
220                            case '+':                       // last group
221                                    return -regex.GroupCount - 4;
222    
223                            case '_':                       // entire text
224                                    return -1;
225    
226                            default:
227                                    return 0;
228                            }
229                    }
230    
231                    private Regex regex;
232                    int n_pieces;
233                    private int [] pieces;
234                    string replacement;
235            }
236    }

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.4

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26