1

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);
IMSoP
  • 89,526
  • 13
  • 117
  • 169
Coltuxumab
  • 615
  • 5
  • 16
  • For me it is unclear what your goal is. What is your expected outcome when parsing your example above? – puelo Jan 29 '23 at 22:37
  • Since you seem to want a C-like syntax, have you looked at [the Dart grammar example](https://github.com/petitparser/dart-petitparser-examples/blob/main/lib/src/dart/grammar.dart) to see how it handles boolean and bitwise expressions? – jamesdlin Jan 29 '23 at 22:44
  • @puelo At the end of the day I need to know if a list of items meets the criteria of the string. So if I have a list [S|69|R, S|62|L], it can check that against the above criteria. I think I’ll be able to achieve that if I can parse the above string into an object. I’ll admit I can’t conceptually grasp what the object will look like yet… – Coltuxumab Jan 29 '23 at 22:51
  • @jamesdin thanks yeah I was looking at that page to try and get some hints. Are you suggesting it’ll be simpler to do what I need if I use a different syntax? That’s not off the table – Coltuxumab Jan 29 '23 at 22:59

1 Answers1

3

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();
Lukas Renggli
  • 8,754
  • 23
  • 46
  • Holy cow, the expression builder seems to have done it. Thanks! I have a lot more testing and manipulation to do, but is there a way to clean up the output a bit? For example it gives me the following List, but maybe this is what I need and I should work through it to get what I need. `[(, [[(, [S|78|L, ||, S|55|L], )], &&, [(, [[(, [[M|25|L, &&, M|26|L], &&, M|27|L], )], ||, [(, [[M|5|L, &&, M|4|L], &&, M|3|L], )]], )]], )]` EDIT: I guess my biggest question is how to drop the excess layers that contain parentheses and no data. Is this simple? – Coltuxumab Feb 02 '23 at 00:45
  • EDIT 2: Now I'm understanding the logic here and I can turn it into whatever I want. This is gold, thank you. – Coltuxumab Feb 02 '23 at 01:23
  • 1
    By default PetitParser returns lists for all sequences and repetitions. You can transform the output of each parser by using [map](https://pub.dev/documentation/petitparser/latest/parser/MapParserExtension/map.html), or with the callbacks in the `ExpressionBuilder`. Ideally you use these callbacks to build a fully typed tree of objects. Most of the grammars in [dart-petitparser-examples](https://github.com/petitparser/dart-petitparser-examples) are typed and return a meaningful AST. – Lukas Renggli Feb 02 '23 at 20:37