static PatternParsers()
{
// Character Escapes
CharEscape = (specialChars, charEscapeKeys) =>
from esc in
Choice(
NoneOf(specialChars),
PrefixedBy(
Char('\\'),
Choice(
OneOf(specialChars),
from c in OneOf(charEscapeKeys)
select charEscapes[c],
from k in
Choice(from _c in Char('x') select 2,
from _c in Char('u') select 4)
from hs in Count(k, HexDigit)
select (char)Numeric.ReadHex(hs),
from os in Count(2, 3, OctDigit)
select (char)Numeric.ReadOct(os),
Satisfy(c => !char.IsLetterOrDigit(c) && c != '_'))))
select new CharEscapePattern(esc);
CharEscapeOutsideClass = CharEscape(specialCharsOutsideClass,
charEscapeKeysOutsideClass)
.Cast<CharEscapePattern, BasePattern>();
CharEscapeInsideClass = (isFirstPos, isSubtract, isAfterDash) =>
CharEscape(getSpecialCharsInsideClass(isFirstPos, isSubtract, isAfterDash),
charEscapeKeysInsideClass);
// Character Classes
AnyCharClass = from _c in Char('.')
select (CharClassPattern)new AnyCharPattern(false);
NamedCharClass = from cls in PrefixedBy(Char('\\'),
OneOf(namedCharClassKeys))
select namedCharClasses[cls];
CharRange = (isFirstPos, isSubtract) =>
from frm in CharEscapeInsideClass(isFirstPos, isSubtract, false)
from _d in Char('-')
from to in CharEscapeInsideClass(false, isSubtract, true)
select (CharPattern)new CharRangePattern(frm.Value, to.Value);
CharGroupElement = (isFirstPos, isSubtract) =>
Choice(NamedCharClass
.Cast<CharClassPattern, CharPattern>(),
CharRange(isFirstPos, isSubtract),
CharEscapeInsideClass(isFirstPos, isSubtract, false)
.Cast<CharEscapePattern, CharPattern>());
BareCharGroup = isSubtract =>
from positive in
Option(true, from _c in Char('^')
select false)
from first in CharGroupElement(true, isSubtract)
from rest in Many(CharGroupElement(false, isSubtract))
let childPatterns = new[] { first }.Concat(rest)
select (CharClassPattern)new CharGroupPattern(positive, childPatterns);
CharClassSubtract = from baseGrp in BareCharGroup(true)
from _d in Char('-')
from excludedGrp in CharGroup
select (CharClassPattern)new CharClassSubtractPattern(baseGrp, excludedGrp);
CharGroup = Between(Char('['),
Char(']'),
Choice(CharClassSubtract,
BareCharGroup(false)));
CharClass = Choice(AnyCharClass,
NamedCharClass,
CharGroup)
.Cast<CharClassPattern, BasePattern>();
// Anchors
Anchor = from a in Choice(OneOf(bareAnchorKeys),
PrefixedBy(Char('\\'),
OneOf(backslashAnchorKeys)))
select (BasePattern)new AnchorPattern(anchorTypes[a]);
// Atoms (non-backtracking elements)
Atom = Choice(Lazy(() => ParenGroup),
Anchor,
CharEscapeOutsideClass,
CharClass);
// Quantifiers
NaturalNum = from ds in Many1(Digit)
select Numeric.ReadDec(ds);
var RangeQuantifierSuffix = Between(Char('{'),
Char('}'),
from min in NaturalNum
from max in
Option(min, PrefixedBy(Char(','),
Option(null, Nullable(NaturalNum))))
select new { Min = min, Max = max });
Quantifier = from child in Atom
from quant in
Choice(
from _q in Char('*') select new { Min = 0, Max = (int?)null },
from _q in Char('+') select new { Min = 1, Max = (int?)null },
from _q in Char('?') select new { Min = 0, Max = (int?)1 },
RangeQuantifierSuffix)
from greedy in
Option(true, from _c in Char('?')
select false)
select (BasePattern)new QuantifierPattern(child, quant.Min, quant.Max, greedy);
// Alternations
BareGroupNoAlt = Many(Choice(Quantifier,
Atom));
AlternationBranch = from ps in BareGroupNoAlt
select (BasePattern)new GroupPattern(false, ps);
Alternation = from alts in SepBy(2, AlternationBranch, Char('|'))
select (BasePattern)new AlternationPattern(alts);
// Groups
BareGroup = Choice(from a in Alternation select Enumerable.Repeat(a, 1),
BareGroupNoAlt);
ParenGroup = from bare in Between(Char('('),
Char(')'),
BareGroup)
select (BasePattern)new GroupPattern(true, bare);
Regex = from bare in BareGroup
select new GroupPattern(true, bare);
}