petitparser / dart-petitparser

Dynamic parser combinators in Dart.
https://pub.dartlang.org/packages/petitparser
MIT License
453 stars 47 forks source link

Choice parser behavior #162

Closed f3ath closed 8 months ago

f3ath commented 8 months ago

Hi @renggli , here's an issue I'm struggling with. There's a certain set of repeated patterns in which I want to attempt to detect a stricter subset first, and then fall back to a more generic one. Let's say I want to first detect a string of one or more uppercase A s or otherwise fall back to a string of case-insensitive as, e.g.

    final uppercase = char('A').plus().map((_) => 'success');
    final anycase = charIgnoringCase('A').plus().map((_) => 'fallback');
    final parser = [uppercase, anycase].toChoiceParser().end();
    print(parser.parse('AAAA')); // "success", as expected
    print(parser.parse('aaaAAaaa')); // "fallback", as expected
    print(parser.parse('AAaaaA')); // I expect "fallback", but getting "Failure[1:3]: end of input expected"

On the last print() I want to get a "fallback" as well. What's the right way to achieve this?

renggli commented 8 months ago

You get this behavior in your last example because your uppercase parser succeeds consuming the two first uppercase A and the choice picks this result as a success.

In your example you can fix this by pulling up the .end() predicate into the choice:

final uppercase = char('A').plus().map((_) => 'success');
final anycase = charIgnoringCase('A').plus().map((_) => 'fallback');
final parser = [uppercase.end(), anycase.end()].toChoiceParser();

In a more general case you can say that uppercase is not followed by an anycase-character, i.e. something along the lines of:

final uppercase = char('A').plus().map((_) => 'success');
final anycase = charIgnoringCase('A').plus().map((_) => 'fallback');
final parser = [uppercase.skip(after: charIgnoringCase('A').not()), anycase].toChoiceParser().end();
f3ath commented 8 months ago

Thank you, the general case is what I was looking for!