Avoiding nested objects using ModelBuilderSemantics in Grako

48 Views Asked by At

If you take a look at the grammar below, you can see a primary rule, expression, which gets parsed into more specific expression types.

expression::Expression
=
  or_ex:and_expr {'||' or_ex:and_expr}+
| andex:and_expr
;

and_expr::AndExpression
=
  and_ex:sub_expr {'&&' and_ex:sub_expr}+
| subex:sub_expr
;

sub_expr::SubExpression
=
  {'!!'}* '!(' not_ex:expression ')'
| {'!!'}* '(' sub_ex:expression ')'
| compex:comp_expr
;

comp_expr::CompareExpression
=
  comp:identifier operator:('>=' | '<=' | '==' | '!=' | '>' | '<') comp:identifier
;

identifier::str
=
?/[a-zA-Z][A-Za-z0-9_]*/?
;

The parsing of the test_input, below, works as expected, but I would prefer to label the and_expr element in the expression rule with an '@' instead of 'andex'. My hope was that the parsed output would result in only a CompareExpression object which is inside a not_ex element in an Expression object.

!(a == b)

It seems that when using the '@' label on the and_expr element, there are no attributes shown in the Expression object! Is this a bug or intentional? Must I label all elements with names and not use the '@' label when using ModelBuilderSemantics?

Another issue I've been facing is that if a later rule, such as comp_expr, did not have an associated class name, its elements would appear in a dictionary when printed, but the dot notation accessor would fail with an AttributeError, i.e. "AttributeError: 'dict' object has no attribute 'comp'". Is there any way to use the dot notation accessor even when rules do not have class names associated with them?

1

There are 1 best solutions below

0
Apalala On

Some of the criteria I use:

  1. Not every rule must have an associated Node class.
  2. Rules with a closure {} as main expression are good for returning a list.
  3. Rules with a a choice | as main expression are best returning whatever the successful option returns, even if this often requires factoring the option into its own rule.
  4. Precedence is important.
  5. Ect.

The idea is that generated parse model should be easy to use, specially with walkers, with a minimum of if-else or isinstance().

This is how I would do your example:

start
    =
    expression $
    ;


expression
    =
    | or_expre
    | and_expre
    | sub_expre
    ;


or_expre::OrExpression
    =
    operands:'||'.{and_expre}+
    ;


and_expr::AndExpression
    =
    operands:'&&'.{sub_expre}+
    ;


sub_expr
    =
    | not_expr
    | comp_expre
    | atomic
    ;


not_expre::NotExpression
   =
   '!!' ~ sub_expr
   ;


comp_expr::CompareExpression
    =
    lef:atomic operator:('>=' | '<=' | '==' | '!=' | '>' | '<') ~ right:atomic
    ;


atomic
    =
    | group_expre
    | identifier
    ;


group_expr::GroupExpression
    =
    '(' ~ expre:expression ')'
    ;


identifier::str
    =
    /[a-zA-Z][A-Za-z0-9_]*/
    ;