Search code examples
dartpetitparser

Can someone point me in the right direction to begin parsing this string in dart?


I have a series of string like this:

(((S|69|L || S|69|R || S|72|L || S|72|R) && ((S|62|L && (S|78|L || S|55|L) && (S|77|L || S|1|L)) || (S|62|R && (S|78|R || S|55|R) && (S|77|R || S|1|R)))) && (M|34|L || M|34|R) && (((M|40|L && M|39|L && M|36|L) || (M|40|R && M|39|R && M|36|R)) || ((M|38|L && M|36|L && M|37|L) || (M|38|R && M|36|R && M|37|R))))

And I need to run items that look like S|69|L to see if they satisfy this criteria. You can see that it's a series of && or || operations organized by parentheses.

I'm trying to use this tutorial: https://github.com/petitparser/dart-petitparser

But I'm having trouble getting off of the ground. Can anyone give me an example to get started with this? Maybe I just need more coffee...

Update: Making progress. This at least pulls off outside parentheses. I think I just need to continue to play with it, but I would still appreciate any tips for those who know how this works.

String testString = '((1||2) || 3)';

final inner = undefined();
final paren = (char('(').trim() & inner.star().flatten() & char(')').trim())
    .map((values) => values[1]);
inner.set(paren | pattern('^)'));
final parser = inner.end();
final result = parser.parse(testString);
print(result.value);

Solution

  • The grammar you provide in the question seems to work for me and the provided example inputs pass.

    A couple of tips:

    1. If you are not interested in the parser output, instead of calling parser.parse(input) you could use parser.accept(input) to get a boolean.

    2. Similarly, if you are not interested in the output, you can drop the calls to flatten() and map(...). Both are used to build an AST. Furthermore, flatten() hides the generated tree, which can make it hard to see what is happening.

    3. For the actual values you could use a primitive parser like the following. However, not sure what your exact specification is?

    final primitive = (uppercase() & char('|') & digit().plus() & char('|') & uppercase()).flatten().trim();
    
    1. If you have the primitive parser, you can add an undefined parser for the logical expression (called outer) like so:
    final outer = undefined();
    final inner = undefined();
    
    final operator = string('&&') | string('||');
    outer.set(inner.separatedBy(operator));
    
    final paren = char('(').trim() & outer & char(')').trim();
    inner.set(paren | primitive);
    
    final parser = outer.end();
    
    1. Building expression parsers can get complicated and unwieldy quite quickly. With the Expression Builder this becomes much simpler:
    final builder = ExpressionBuilder();
    builder.group().primitive(primitive);
    builder.group()
        .wrapper(char('(').trim(), char(')').trim(), (l, v, r) => [l, v, r]);
    builder.group()
        ..left(string('&&').trim(), (a, op, b) => [a, '&&', b])
        ..left(string('||').trim(), (a, op, b) => [a, '||', b]);
    final parser = builder.build().end();