//
// 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
// 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 ParserSystem.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)
{
if (n_pieces == 0)
return replacement;
StringBuilder sb = new StringBuilder ();
EvaluateAppend (match, sb);
return sb.ToString ();
}
public void EvaluateAppend (Match match, StringBuilder sb)
{
if (n_pieces == 0) {
sb.Append (replacement);
return;
}
int i = 0;
while (i < n_pieces) {
int k = pieces [i++];
if (k >= 0) {
int 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);
}
}
}
public bool NeedsGroupsOrCaptures {
get {
if (n_pieces == 0)
return false;
else
return true;
}
}
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 ()
{
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;
}
}
|