| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- //
- // assembly: System
- // namespace: System.Text.RegularExpressions
- // file: replace.cs
- //
- // author: Dan Lewis ([email protected])
- // (c) 2002
- //
- // Permission is hereby granted, free of charge, to any person obtaining
- // a copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to
- // permit persons to whom the Software is furnished to do so, subject to
- // the following conditions:
- //
- // The above copyright notice and this permission notice shall be
- // included in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- 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;
- }
- }
|