private bool Eval (Mode mode, ref int ref_ptr, int pc) {
int ptr = ref_ptr;
Begin:
for (;;) {
ushort word = program[pc];
OpCode op = (OpCode)(word & 0x00ff);
OpFlags flags = (OpFlags)(word & 0xff00);
switch (op) {
case OpCode.Anchor: {
int skip = program[pc + 1];
int anch_offset = program[pc + 2];
bool anch_reverse = (flags & OpFlags.RightToLeft) != 0;
int anch_ptr = anch_reverse ? ptr - anch_offset : ptr + anch_offset;
int anch_end = text_end - match_min + anch_offset; // maximum anchor position
int anch_begin = 0;
// the general case for an anchoring expression is at the bottom, however we
// do some checks for the common cases before to save processing time. the current
// optimizer only outputs three types of anchoring expressions: fixed position,
// fixed substring, and no anchor.
OpCode anch_op = (OpCode)(program[pc + 3] & 0x00ff);
if (anch_op == OpCode.Position && skip == 6) { // position anchor
// Anchor
// Position
// True
switch ((Position)program[pc + 4]) {
case Position.StartOfString:
if (anch_reverse || anch_offset == 0) {
if (anch_reverse)
ptr = anch_offset;
if (TryMatch (ref ptr, pc + skip))
goto Pass;
}
break;
case Position.StartOfLine:
if (anch_ptr == 0) {
ptr = 0;
if (TryMatch (ref ptr, pc + skip))
goto Pass;
++ anch_ptr;
}
while ((anch_reverse && anch_ptr >= 0) || (!anch_reverse && anch_ptr <= anch_end)) {
if (anch_ptr == 0 || text[anch_ptr - 1] == '\n') {
if (anch_reverse)
ptr = anch_ptr == anch_end ? anch_ptr : anch_ptr + anch_offset;
else
ptr = anch_ptr == 0 ? anch_ptr : anch_ptr - anch_offset;
if (TryMatch (ref ptr, pc + skip))
goto Pass;
}
if (anch_reverse)
-- anch_ptr;
else
++ anch_ptr;
}
break;
case Position.StartOfScan:
if (anch_ptr == scan_ptr) {
ptr = anch_reverse ? scan_ptr + anch_offset : scan_ptr - anch_offset;
if (TryMatch (ref ptr, pc + skip))
goto Pass;
}
break;
default:
// FIXME
break;
}
}
else if (qs != null ||
(anch_op == OpCode.String && skip == 6 + program[pc + 4])) { // substring anchor
// Anchor
// String
// True
bool reverse = ((OpFlags)program[pc + 3] & OpFlags.RightToLeft) != 0;
if (qs == null) {
bool ignore = ((OpFlags)program[pc + 3] & OpFlags.IgnoreCase) != 0;
string substring = GetString (pc + 3);
qs = new QuickSearch (substring, ignore, reverse);
}
while ((anch_reverse && anch_ptr >= anch_begin)
|| (!anch_reverse && anch_ptr <= anch_end)) {
if (reverse)
{
anch_ptr = qs.Search (text, anch_ptr, anch_begin);
if (anch_ptr != -1)
anch_ptr += qs.Length ;
}
else
anch_ptr = qs.Search (text, anch_ptr, anch_end);
if (anch_ptr < 0)
break;
ptr = reverse ? anch_ptr + anch_offset : anch_ptr - anch_offset;
if (TryMatch (ref ptr, pc + skip))
goto Pass;
if (reverse)
anch_ptr -= 2;
else
++ anch_ptr;
}
}
else if (anch_op == OpCode.True) { // no anchor
// Anchor
// True
while ((anch_reverse && anch_ptr >= anch_begin)
|| (!anch_reverse && anch_ptr <= anch_end)) {
ptr = anch_ptr;
if (TryMatch (ref ptr, pc + skip))
goto Pass;
if (anch_reverse)
-- anch_ptr;
else
++ anch_ptr;
}
}
else { // general case
// Anchor
// <expr>
// True
while ((anch_reverse && anch_ptr >= anch_begin)
|| (!anch_reverse && anch_ptr <= anch_end)) {
ptr = anch_ptr;
if (Eval (Mode.Match, ref ptr, pc + 3)) {
// anchor expression passed: try real expression at the correct offset
ptr = anch_reverse ? anch_ptr + anch_offset : anch_ptr - anch_offset;
if (TryMatch (ref ptr, pc + skip))
goto Pass;
}
if (anch_reverse)
-- anch_ptr;
else
++ anch_ptr;
}
}
goto Fail;
}
case OpCode.False: {
goto Fail;
}
case OpCode.True: {
goto Pass;
}
case OpCode.Position: {
if (!IsPosition ((Position)program[pc + 1], ptr))
goto Fail;
pc += 2;
break;
}
case OpCode.String: {
bool reverse = (flags & OpFlags.RightToLeft) != 0;
bool ignore = (flags & OpFlags.IgnoreCase) != 0;
int len = program[pc + 1];
if (reverse) {
ptr -= len;
if (ptr < 0)
goto Fail;
}
else
if (ptr + len > text_end)
goto Fail;
pc += 2;
for (int i = 0; i < len; ++ i) {
char c = text[ptr + i];
if (ignore)
c = Char.ToLower (c);
if (c != (char)program[pc ++])
goto Fail;
}
if (!reverse)
ptr += len;
break;
}
case OpCode.Reference: {
bool reverse = (flags & OpFlags.RightToLeft) != 0;
bool ignore = (flags & OpFlags.IgnoreCase) != 0;
int m = GetLastDefined (program [pc + 1]);
if (m < 0)
goto Fail;
int str = marks [m].Index;
int len = marks [m].Length;
if (reverse) {
ptr -= len;
if (ptr < 0)
goto Fail;
}
else if (ptr + len > text_end)
goto Fail;
pc += 2;
if (ignore) {
for (int i = 0; i < len; ++ i) {
if (Char.ToLower (text[ptr + i]) != Char.ToLower (text[str + i]))
goto Fail;
}
} else {
for (int i = 0; i < len; ++ i) {
if (text[ptr + i] != text[str + i])
goto Fail;
}
}
if (!reverse)
ptr += len;
break;
}
case OpCode.Character: case OpCode.Category: case OpCode.NotCategory:
case OpCode.Range: case OpCode.Set: {
if (!EvalChar (mode, ref ptr, ref pc, false))
goto Fail;
break;
}
case OpCode.In: {
int target = pc + program[pc + 1];
pc += 2;
if (!EvalChar (mode, ref ptr, ref pc, true))
goto Fail;
pc = target;
break;
}
case OpCode.Open: {
Open (program[pc + 1], ptr);
pc += 2;
break;
}
case OpCode.Close: {
Close (program[pc + 1], ptr);
pc += 2;
break;
}
case OpCode.BalanceStart: {
int start = ptr; //point before the balancing group
if (!Eval (Mode.Match, ref ptr, pc + 5))
goto Fail;
if(!Balance (program[pc + 1], program[pc + 2], (program[pc + 3] == 1 ? true : false) , start)) {
goto Fail;
}
pc += program[pc + 4];
break;
}
case OpCode.Balance: {
goto Pass;
}
case OpCode.IfDefined: {
int m = GetLastDefined (program [pc + 2]);
if (m < 0)
pc += program[pc + 1];
else
pc += 3;
break;
}
case OpCode.Sub: {
if (!Eval (Mode.Match, ref ptr, pc + 2))
goto Fail;
pc += program[pc + 1];
break;
}
case OpCode.Test: {
int cp = Checkpoint ();
int test_ptr = ptr;
if (Eval (Mode.Match, ref test_ptr, pc + 3))
pc += program[pc + 1];
else {
Backtrack (cp);
pc += program[pc + 2];
}
break;
}
case OpCode.Branch: {
OpCode branch_op;
do {
int cp = Checkpoint ();
if (Eval (Mode.Match, ref ptr, pc + 2))
goto Pass;
Backtrack (cp);
pc += program[pc + 1];
branch_op = (OpCode)(program[pc] & 0xff);
} while (branch_op != OpCode.False);
goto Fail;
}
case OpCode.Jump: {
pc += program[pc + 1];
break;
}
case OpCode.Repeat: {
this.repeat = new RepeatContext (
this.repeat, // previous context
ReadProgramCount (pc + 2), // minimum
ReadProgramCount (pc + 4), // maximum
(flags & OpFlags.Lazy) != 0, // lazy
pc + 6 // subexpression
);
if (Eval (Mode.Match, ref ptr, pc + program[pc + 1]))
goto Pass;
else {
this.repeat = this.repeat.Previous;
goto Fail;
}
}
case OpCode.Until: {
RepeatContext current = this.repeat;
//
// Can we avoid recursion?
//
// Backtracking can be forced in nested quantifiers from the tail of this quantifier.
// Thus, we cannot, in general, use a simple loop on repeat.Expression to handle
// quantifiers.
//
// If 'deep' was unmolested, that implies that there was no nested quantifiers.
// Thus, we can safely avoid recursion.
//
if (deep == current)
goto Pass;
int start = current.Start;
int start_count = current.Count;
while (!current.IsMinimum) {
++ current.Count;
current.Start = ptr;
deep = current;
if (!Eval (Mode.Match, ref ptr, current.Expression)) {
current.Start = start;
current.Count = start_count;
goto Fail;
}
if (deep != current) // recursive mode
goto Pass;
}
if (ptr == current.Start) {
// degenerate match ... match tail or fail
this.repeat = current.Previous;
deep = null;
if (Eval (Mode.Match, ref ptr, pc + 1))
goto Pass;
this.repeat = current;
goto Fail;
}
if (current.IsLazy) {
for (;;) {
// match tail first ...
this.repeat = current.Previous;
deep = null;
int cp = Checkpoint ();
if (Eval (Mode.Match, ref ptr, pc + 1))
goto Pass;
Backtrack (cp);
// ... then match more
this.repeat = current;
if (current.IsMaximum)
goto Fail;
++ current.Count;
current.Start = ptr;
deep = current;
if (!Eval (Mode.Match, ref ptr, current.Expression)) {
current.Start = start;
current.Count = start_count;
goto Fail;
}
if (deep != current) // recursive mode
goto Pass;
// Degenerate match: ptr has not moved since the last (failed) tail match.
// So, next and subsequent tail matches will fail.
if (ptr == current.Start)
goto Fail;
}
} else {
int stack_size = stack.Count;
// match greedily as much as possible
while (!current.IsMaximum) {
int cp = Checkpoint ();
int old_ptr = ptr;
int old_start = current.Start;
++ current.Count;
current.Start = ptr;
deep = current;
if (!Eval (Mode.Match, ref ptr, current.Expression)) {
-- current.Count;
current.Start = old_start;
Backtrack (cp);
break;
}
if (deep != current) {
// recursive mode: no more backtracking, truncate the stack
stack.Count = stack_size;
goto Pass;
}
stack.Push (cp);
stack.Push (old_ptr);
// Degenerate match: no point going on
if (ptr == current.Start)
break;
}
// then, match the tail, backtracking as necessary.
this.repeat = current.Previous;
for (;;) {
deep = null;
if (Eval (Mode.Match, ref ptr, pc + 1)) {
stack.Count = stack_size;
goto Pass;
}
if (stack.Count == stack_size) {
this.repeat = current;
goto Fail;
}
--current.Count;
ptr = stack.Pop ();
Backtrack (stack.Pop ());
}
}
}
case OpCode.FastRepeat: {
this.fast = new RepeatContext (
fast,
ReadProgramCount (pc + 2), // minimum
ReadProgramCount (pc + 4), // maximum
(flags & OpFlags.Lazy) != 0, // lazy
pc + 6 // subexpression
);
fast.Start = ptr;
int cp = Checkpoint ();
pc += program[pc + 1]; // tail expression
ushort tail_word = program[pc];
int c1 = -1; // first character of tail operator
int c2 = -1; // ... and the same character, in upper case if ignoring case
int coff = 0; // 0 or -1 depending on direction
OpCode tail_op = (OpCode)(tail_word & 0xff);
if (tail_op == OpCode.Character || tail_op == OpCode.String) {
OpFlags tail_flags = (OpFlags)(tail_word & 0xff00);
if ((tail_flags & OpFlags.Negate) != 0)
goto skip;
if (tail_op == OpCode.String)
{
int offset = 0;
if ((tail_flags & OpFlags.RightToLeft) != 0)
{
offset = program[pc + 1] - 1 ;
}
c1 = program[pc + 2 + offset]; // first char of string
}
else
c1 = program[pc + 1]; // character
if ((tail_flags & OpFlags.IgnoreCase) != 0)
c2 = Char.ToUpper ((char)c1); // ignore case
else
c2 = c1;
if ((tail_flags & OpFlags.RightToLeft) != 0)
coff = -1; // reverse
else
coff = 0;
}
skip:
if (fast.IsLazy) {
if (!fast.IsMinimum && !Eval (Mode.Count, ref ptr, fast.Expression)) {
//Console.WriteLine ("lazy fast: failed mininum.");
fast = fast.Previous;
goto Fail;
}
while (true) {
int p = ptr + coff;
if (c1 < 0 || (p >= 0 && p < text_end && (c1 == text[p] || c2 == text[p]))) {
deep = null;
if (Eval (Mode.Match, ref ptr, pc))
break;
}
if (fast.IsMaximum) {
//Console.WriteLine ("lazy fast: failed with maximum.");
fast = fast.Previous;
goto Fail;
}
Backtrack (cp);
if (!Eval (Mode.Count, ref ptr, fast.Expression)) {
//Console.WriteLine ("lazy fast: no more.");
fast = fast.Previous;
goto Fail;
}
}
fast = fast.Previous;
goto Pass;
}
else {
if (!Eval (Mode.Count, ref ptr, fast.Expression)) {
fast = fast.Previous;
goto Fail;
}
int width;
if (fast.Count > 0)
width = (ptr - fast.Start) / fast.Count;
else
width = 0;
while (true) {
int p = ptr + coff;
if (c1 < 0 || (p >= 0 && p < text_end && (c1 == text[p] || c2 == text[p]))) {
deep = null;
if (Eval (Mode.Match, ref ptr, pc))
break;
}
-- fast.Count;
if (!fast.IsMinimum) {
fast = fast.Previous;
goto Fail;
}
ptr -= width;
Backtrack (cp);
}
fast = fast.Previous;
goto Pass;
}
}
case OpCode.Info: {
Debug.Assert (false, "Regex", "Info block found in pattern");
goto Fail;
}
}
}
Pass:
ref_ptr = ptr;
switch (mode) {
case Mode.Match:
return true;
case Mode.Count: {
++ fast.Count;
if (fast.IsMaximum || (fast.IsLazy && fast.IsMinimum))
return true;
pc = fast.Expression;
goto Begin;
}
}
Fail:
switch (mode) {
case Mode.Match:
return false;
case Mode.Count: {
if (!fast.IsLazy && fast.IsMinimum)
return true;
ref_ptr = fast.Start;
return false;
}
}
return false;
}