Python3-Lark: Convenient way to handle a rule with optional terminals?

296 views Asked by At

In a grammar rule like the following one:

top_rule: header? body footer?
?header: "DAY" DAY
       | "SECT" SECT  
?body: BODY
?footer: "ENDING" 
       | "TERMINATING"  

DAY: /\[^s]+/
SECT: /\d+/
BODY: /\[^s]+/

What is the most convenient way for the transformer method ?

  • Do I go for the @v_args(inline=True)? In this case I have to mess with lengths of the *args and do if-else cases according to them.
  • Do I go for the @v_args(tree=True)? Checking whether the AST contains rules by traversing the AST children?

In python3:

#!/usr/bin/python3

from lark import Lark, Transformer, v_args

GRAMMAR = r"""

 top_rule: header? body footer?
 ?header: "DAY" DAY
        | "SECT" SECT  
 ?body: BODY
 ?footer: "ENDING" DAY
        | "TERMINATING" DAY  

 DAY: /\[^s]+/
 SECT: /\d+/
 BODY: /\[^s]+/
"""

def MyTransformer(Transformer)

    @v_args(inline=True)
    def top_rule(self, *args):
        if len(args) == 3:
             # all optionals are used of the rule...
        elif len(args) == 1: 
             # no optionals (header/footer) used...
        else: 
             # can be args(header, body) or args(body, footer)...


What is the appropriate way of handling it? Am I missing something? Or is this the way to go. As far as I understood I cannot have multiple top_rule methods based on the number of arguments.

1

There are 1 answers

2
MegaIng On

Use inline=True and [TERMINAL] instead of TERMINAL?. Then lark fills in None for the terminals that are missing:

from lark import Lark, Transformer, v_args

GRAMMAR = r"""

 top_rule: [header] body [footer]
 ?header: "DAY" DAY
        | "SECT" SECT  
 ?body: BODY
 ?footer: "ENDING" DAY
        | "TERMINATING" DAY  

 DAY: /\[^s]+/
 SECT: /\d+/
 BODY: /\[^s]+/
"""

def MyTransformer(Transformer)
    @v_args(inline=True)
    def top_rule(self, header: Token | None, body: Token, footer: Token | None):
        if header is not None:
             # header is being used
        if footer is not None: 
             # footer is being used
        # body will always be there.