internal RegexNode ScanRegex()
{
char ch = '@'; // nonspecial ch, means at beginning
bool isQuantifier = false;
StartGroup(new RegexNode(RegexNode.Capture, _options, 0, -1));
while (CharsRight() > 0)
{
bool wasPrevQuantifier = isQuantifier;
isQuantifier = false;
ScanBlank();
int startpos = Textpos();
// move past all of the normal characters. We'll stop when we hit some kind of control character,
// or if IgnorePatternWhiteSpace is on, we'll stop when we see some whitespace.
if (UseOptionX())
while (CharsRight() > 0 && (!IsStopperX(ch = RightChar()) || ch == '{' && !IsTrueQuantifier()))
MoveRight();
else
while (CharsRight() > 0 && (!IsSpecial(ch = RightChar()) || ch == '{' && !IsTrueQuantifier()))
MoveRight();
int endpos = Textpos();
ScanBlank();
if (CharsRight() == 0)
ch = '!'; // nonspecial, means at end
else if (IsSpecial(ch = RightChar()))
{
isQuantifier = IsQuantifier(ch);
MoveRight();
}
else
ch = ' '; // nonspecial, means at ordinary char
if (startpos < endpos)
{
int cchUnquantified = endpos - startpos - (isQuantifier ? 1 : 0);
wasPrevQuantifier = false;
if (cchUnquantified > 0)
AddConcatenate(startpos, cchUnquantified, false);
if (isQuantifier)
AddUnitOne(CharAt(endpos - 1));
}
switch (ch)
{
case '!':
goto BreakOuterScan;
case ' ':
goto ContinueOuterScan;
case '[':
AddUnitSet(ScanCharClass(UseOptionI()).ToStringClass());
break;
case '(':
{
RegexNode grouper;
PushOptions();
if (null == (grouper = ScanGroupOpen()))
{
PopKeepOptions();
}
else
{
PushGroup();
StartGroup(grouper);
}
}
continue;
case '|':
AddAlternate();
goto ContinueOuterScan;
case ')':
if (EmptyStack())
throw MakeException(SR.TooManyParens);
AddGroup();
PopGroup();
PopOptions();
if (Unit() == null)
goto ContinueOuterScan;
break;
case '\\':
AddUnitNode(ScanBackslash());
break;
case '^':
AddUnitType(UseOptionM() ? RegexNode.Bol : RegexNode.Beginning);
break;
case '$':
AddUnitType(UseOptionM() ? RegexNode.Eol : RegexNode.EndZ);
break;
case '.':
if (UseOptionS())
AddUnitSet(RegexCharClass.AnyClass);
else
AddUnitNotone('\n');
break;
case '{':
case '*':
case '+':
case '?':
if (Unit() == null)
throw MakeException(wasPrevQuantifier ?
SR.Format(SR.NestedQuantify, ch.ToString()) :
SR.QuantifyAfterNothing);
MoveLeft();
break;
default:
throw MakeException(SR.InternalError);
}
ScanBlank();
if (CharsRight() == 0 || !(isQuantifier = IsTrueQuantifier()))
{
AddConcatenate();
goto ContinueOuterScan;
}
ch = MoveRightGetChar();
// Handle quantifiers
while (Unit() != null)
{
int min;
int max;
bool lazy;
switch (ch)
{
case '*':
min = 0;
max = int.MaxValue;
break;
case '?':
min = 0;
max = 1;
break;
case '+':
min = 1;
max = int.MaxValue;
break;
case '{':
{
startpos = Textpos();
max = min = ScanDecimal();
if (startpos < Textpos())
{
if (CharsRight() > 0 && RightChar() == ',')
{
MoveRight();
if (CharsRight() == 0 || RightChar() == '}')
max = int.MaxValue;
else
max = ScanDecimal();
}
}
if (startpos == Textpos() || CharsRight() == 0 || MoveRightGetChar() != '}')
{
AddConcatenate();
Textto(startpos - 1);
goto ContinueOuterScan;
}
}
break;
default:
throw MakeException(SR.InternalError);
}
ScanBlank();
if (CharsRight() == 0 || RightChar() != '?')
lazy = false;
else
{
MoveRight();
lazy = true;
}
if (min > max)
throw MakeException(SR.IllegalRange);
AddConcatenate(lazy, min, max);
}
ContinueOuterScan:
;
}
BreakOuterScan:
;
if (!EmptyStack())
throw MakeException(SR.NotEnoughParens);
AddGroup();
return Unit();
}