parsimonious - a simple recursive pattern

497 Views Asked by At

I want to be able to parse simple rule expressions that can be joined together using conjunction words like and and or within parsimonious.

I've tried a very rudimentary grammar, which parses a simple expression, but fails as soon as I start introducing conjunctions.

import parsimonious

grammar = parsimonious.grammar.Grammar( """
    rule = (rule)+
    rule = (fieldname function parameters) / (rule conjunction rule)
    fieldname = space? ("field1" / "field2" / "field3") space?
    function = space? ("equal to" / "not equal to") space?
    parameters = space? "()" space?
    conjunction = space? ("and" / "or") space?
    space = ~r"\s+"
    """)

Testing for a simple case:

grammar.parse("field1 equal to ()")

Successfully parses (at least it appears to build a node-tree - I've not gone into depth figuring out how well it's split the content - but appears fine at first glance)

But then for a more complex case:

grammar.parse("field1 equal to () and field2 not equal to ()")

It returns IncompleteParseError: Rule 'rule' matched in its entirety, but it didn't consume all the text. The non-matching portion of the text begins with 'and field2 not equal' (line 1, column 20).

The grammar I've set out is trying to allow for arbitrarily conjoined statements, but I must be missing something.

I tried tweaking the grammar to make explicit the difference between the top-level class and lower ones:

grammar = parsimonious.grammar.Grammar( """
    rule = expr+
    expr = (fieldname function parameters) / (expr conjunction expr)
    fieldname = space? ("field1" / "field2" / "field3") space?
    function = space? ("equal to" / "not equal to") space?
    parameters = space? "()" space?
    conjunction = space? ("and" / "or") space?
    space = ~r"\s+"
    """)

And now, when running the 2-part phrase:

grammar.parse("field1 equal to () and field2 not equal to ()")

instead of the IncompleteParseError, I get a RecursionError: maximum recursion depth exceeded in comparison.

1

There are 1 best solutions below

0
On

In the (expr conjunction expr) part of expr you have a left recursion problem. So, you'll need to break it up into separate rules something like this:

rule = expr+
expr = field_expr (conjunction expr)?
field_expr = fieldname function parameters
fieldname = space? ("field1" / "field2" / "field3") space?
function = space? ("not equal to" / "equal to") space?
parameters = space? "()" space?
conjunction = space? ("and" / "or") space?
space = ~r"\s+"