Implement look-ahead parser
sebastienros opened this issue · comments
A parser that comes back to the start position when it succeeds.
Example:
{% assign a = b | plus 1 %}
To detect that :
is missing after plus
we need to check that the next token after plus
is either ,
or %}
, without consuming the next tokens.
Here's a simple lookahead parser:
public sealed class Lookahead<T, T2> : Parser<T>
{
private readonly Parser<T> _parser;
private readonly Parser<T2> _lookaheadParser;
public Lookahead(Parser<T> parser, Parser<T2> lookaheadParser)
{
_parser = parser ?? throw new ArgumentNullException(nameof(parser));
_lookaheadParser = lookaheadParser ?? throw new ArgumentNullException(nameof(lookaheadParser));
}
public override bool Parse(ParseContext context, ref ParseResult<T> result)
{
context.EnterParser(this);
var start = context.Scanner.Cursor.Position;
if (_parser.Parse(context, ref result))
{
var parseResult2 = new ParseResult<T2>();
var start2 = context.Scanner.Cursor.Position;
var valid = _lookaheadParser.Parse(context, ref parseResult2);
context.Scanner.Cursor.ResetPosition(start2);
return valid;
}
context.Scanner.Cursor.ResetPosition(start);
return false;
}
}
FollowedBy
?
FollowedBy
?
It's kind of ambiguous whether parser backtracks it's position. Peek
or AndPeek
maybe?
I am not sure Peek
makes it obvious that it enhances the previous parser. Maybe same issue with FollowedBy
, so maybe something like WhenFollowedBy
, WhenBefore
. Could be IfFollowedBy
but we already have a When
so to keep it consistent.
Also we need the opposite to it, aka WhenNotFollowedBy
, such that a parser succeeds if the next token is not something.
Ideally, it would communicate that it enhances the previous parser (FollowedBy
, When
) and that it backtracks the position (Lookahead
, Peek
). Maybe WhenPeek
? WhenFollowedBy
kind of implies (?) that it might backtrack, but is long. WhenNext
seems similar to WhenFollowedBy
. Naming things is hard :)
Another issue is: are there any use cases where peeked result should be returned? My implementation discards the result, as I don't need it in my case. Maybe there could be two versions of lookahead, a When...
that discards the result and an And...
that keeps it. I'm not sure there is any need for the latter.
For the opposite version. I checked my uses of look-ahed parser and I always negate it (eg. .Lookahead(NotDigit)
). So a WhenNot...
version would definitely make it easier.
are there any use cases where peeked result should be returned?
Reminder that there is a SkipAnd
and AndSkip
. It will parse, advance (consume) but ignore the result (noise).