public static void Main()
var parser = new RegexPatternParser(@"rob\in([a-z^0-9_\-])");
public interface ICharPattern
public class Any : ICharPattern
public static readonly Any Instance = new Any();
public bool IsMatch(char c)
public class ConstCharsetPattern : ICharPattern
private readonly string _chars;
public ConstCharsetPattern(string chars)
public bool IsMatch(char c)
return _chars.IndexOf(c) != -1;
public class ConstCharPattern : ICharPattern
private readonly char _character;
public ConstCharPattern(char c)
public bool IsMatch(char c)
public class ConstRangePattern : ICharPattern
private readonly char _rangeStart;
private readonly char _rangeEnd;
public ConstRangePattern(char rangeStart, char rangeEnd)
if (rangeStart >= rangeEnd)
throw new ArgumentException("Start of character range must be smaller then end of range");
_rangeStart = rangeStart;
public bool IsMatch(char c)
return c >= _rangeStart && c <= _rangeEnd;
public class CombinedPattern : ICharPattern
private readonly ICharPattern[] _patterns;
public CombinedPattern(ICharPattern[] patterns)
throw new ArgumentNullException("patterns");
public bool IsMatch(char c)
foreach (ICharPattern pattern in _patterns)
if (pattern.IsMatch(c)) return true;
public class Number : ConstRangePattern
public static readonly Number Instance = new Number();
public Number() : base('0', '9') {}
public class UCaseLetter : ConstRangePattern
public static readonly UCaseLetter Instance = new UCaseLetter();
public UCaseLetter() : base('A', 'Z') {}
public class LCaseLetter : ConstRangePattern
public static readonly LCaseLetter Instance = new LCaseLetter();
public LCaseLetter() : base('a', 'z') {}
public class Esc : ConstCharPattern
public static readonly Esc Instance = new Esc();
public Esc() : base('\\') {}
public class Dollar : ConstCharPattern
public static readonly Dollar Instance = new Dollar();
public Dollar() : base('$') {}
public class Dak : ConstCharPattern
public static readonly Dak Instance = new Dak();
public Dak() : base('^') {}
public class Dash : ConstCharPattern
public static readonly Dash Instance = new Dash();
public Dash() : base('-') {}
public class Underscore : ConstCharPattern
public static readonly Underscore Instance = new Underscore();
public Underscore() : base('_') {}
public class ParOpen : ConstCharPattern
public static readonly ParOpen Instance = new ParOpen();
public ParOpen() : base('(') {}
public class ParClose : ConstCharPattern
public static readonly ParClose Instance = new ParClose();
public ParClose() : base(')') {}
public class BraOpen : ConstCharPattern
public static readonly BraOpen Instance = new BraOpen();
public BraOpen() : base('[') {}
public class BraClose : ConstCharPattern
public static readonly BraClose Instance = new BraClose();
public BraClose() : base(']') {}
private readonly string _source;
private readonly int _length;
public Scanner(string source, int start, int length)
return _position >= _length;
protected void Step(int offset)
if (offset + _position > _length)
throw new InvalidOperationException("Step beond EOF");
return _source[_position];
protected string Token(int offset)
return _source.Substring(_position, offset);
protected bool IsMatch(ICharPattern charPattern, int offset)
return ((offset + _position < _length) &&
charPattern.IsMatch(_source[_position + offset]));
protected bool IsNoMatch(ICharPattern charPattern, int offset)
return ((offset + _position < _length) &&
!charPattern.IsMatch(_source[_position + offset]));
protected bool One(ICharPattern charPattern)
return IsMatch(charPattern, 0);
protected bool None(ICharPattern charPattern)
return IsNoMatch(charPattern, 0);
protected bool OneOrMore(ICharPattern charPattern, out int offset)
while (IsMatch(charPattern, offset))
protected bool ZeroOrMore(ICharPattern charPattern, out int offset)
while (IsMatch(charPattern, offset))
public class RegexPatternParser : Scanner
public RegexPatternParser(string source) : base(source, 0, source.Length)
private static readonly ConstCharsetPattern _reservedChars = new ConstCharsetPattern("()[]$^");
while (!EOF && Expression())
private bool Expression()
Console.WriteLine("No expression found at position {0}", Position);
private bool Character(out char c)
Console.WriteLine("Parsed escaped character {0}", c);
else if (None(_reservedChars))
Console.WriteLine("Parsed const character {0}", c);
if (One(BraOpen.Instance))
Console.WriteLine("Parsed start of range");
while (RangeExpression())
if (!One(BraClose.Instance))
Console.WriteLine("']' expected !");
Console.WriteLine("Parsed end of range");
private bool RangeExpression()
Console.WriteLine("Parsed '^' character");
if (!Character(out start))
Console.WriteLine("Character expected at end of sequential range expression");
Console.WriteLine("Parsed sequential character range expression [{0},{1}]", start, end);
Console.WriteLine("Parsed character range character '{0}'", start);
if (One(ParOpen.Instance))
Console.WriteLine("Parsed start of capture");
if (!One(ParClose.Instance))
Console.WriteLine("')' expected !");
Console.WriteLine("Parsed end of capture");