private static List <State> ParseRepetitionRange(IPrattParseContext <char> ctx, List <State> states, IParser <char, int> digits) { if (states.Last().Type == StateType.EndOfInput) { throw new RegexException("Cannot quantify the end anchor $"); } int min = 0; var first = ctx.TryParse(digits); if (first.Success) { min = first.Value; } var comma = ctx.TryParse(MatchChar(',')); if (!comma.Success) { ctx.Expect(MatchChar('}')); // No comma, so we must have {X} form if (!first.Success) { throw new RegexException("Invalid range specifier. Must be one of {X} {X,} {,Y} or {X,Y}"); } return(State.SetPreviousStateRange(states, min, min)); } // At this point we might have X, X,Y or ,Y // In any case, min is filled in now with either a value or 0 var second = ctx.TryParse(digits); ctx.Expect(MatchChar('}')); return(State.SetPreviousStateRange(states, min, second.Success ? second.Value : int.MaxValue)); }
private static List <State> ParseAlternation(IPrattParseContext <char, List <State> > ctx, List <State> states) { var options = new List <List <State> >() { states }; while (true) { var option = ctx.TryParse(0); if (!option.Success || option.Value.Count == 0) { break; } options.Add(option.Value); } if (options.Count == 1) { return(states); } return(new List <State> { new State("alternation") { Type = StateType.Alternation, Alternations = options } }); }
private static (char low, char high) ParseCharacterRange(IPrattParseContext <char> ctx, char c) { var low = c; var next = ctx.Input.Peek(); if (next != '-') { return(low, low); } // We're keeping the peek'd char (dash) so advance input and keep going ctx.Input.GetNext(); c = ctx.Input.GetNext(); if (c == ']') { throw new RegexException("Unexpected ] after -. Expected end of range. Did you mean '\\]'?"); } c = GetUnescapedCharacter(ctx, c); var high = c; if (high < low) { throw new RegexException($"Invalid range {high}-{low} should be {low}-{high}"); } return(low, high); }
private static List <State> ParseCharacterClass(IPrattParseContext <char> ctx, List <State>?states) { var invertResult = ctx.TryParse(MatchChar('^')); var ranges = new List <(char low, char high)>(); while (true) { if (ctx.Input.IsAtEnd) { throw new RegexException("Incomplete character class"); } var c = ctx.Input.GetNext(); if (c == ']') { break; } c = GetUnescapedCharacter(ctx, c); var range = ParseCharacterRange(ctx, c); ranges.Add(range); } if (ranges.Count == 0) { throw new RegexException("Empty character class"); } var matcher = new CharacterMatcher(invertResult.Success, ranges); return(State.AddMatch(states, c => matcher.IsMatch(c), "class")); }
private static char GetUnescapedCharacter(IPrattParseContext <char> ctx, char c) { if (c != '\\') { return(c); } if (ctx.Input.IsAtEnd) { throw new RegexException("Expected character, found end"); } return(ctx.Input.GetNext()); }
public IOption <IPrattToken <TOutput> > LeftDenominator(IPrattParseContext <TInput, TOutput> context, IPrattToken left) => FailureOption <IPrattToken <TOutput> > .Instance;
public IOption <IPrattToken <TOutput> > LeftDenominator(IPrattParseContext <TInput, TOutput> context, IPrattToken left) => _parselet.Led(context, left, this);
public IOption <IPrattToken <TOutput> > NullDenominator(IPrattParseContext <TInput, TOutput> context) => _parselet.Nud(context, this);