pythonebnfcisco-iosgrako

How to describe scopes in EBNF?


I'm trying to write a parser for Cisco IOS and ASA configurations, using Grako and Python. I'm trying to figure out how to represent 'scoped' keywords in EBNF - for example, the 'description' keyword must appear inside an interface scope, but there are multiple options for interface and they are all optional (and the order can change between devices, I believe):

interface Vlan1435
 nameif untrust
 description the outside interface for customer X
 bridge-group 1
 security-level 0

The nearest I have found to an example is in a Perl application called Farly that uses the perl Parse::Recdescent module, which appears to be similar to Grako.

From there I have this type of recursive definition:

@@eol_comments :: /!([^\n]*?)\n/
@@whitespace :: /[\t ]+/

start
    =
    file_input $
    ;


file_input
    =
    {NEWLINE | asa_line}
    ;


asa_line
    =
      'names' NEWLINE
    | interface NEWLINE
    ;

interface
    =
    'interface' string NEWLINE interface_options
    ;


interface_options
    =
    if_name | sec_level | if_addr | if_bridgegroup | NEWLINE
    ;


if_bridgegroup
    =
    'bridge-group' digit NEWLINE interface_options
    ;


if_name
    =
    'nameif' string NEWLINE interface_options
    ;


sec_level
    =
    'security-level' digit NEWLINE interface_options
    ;

but it produces a strange nested AST, and it doesn't "reset" to detect the second interface or anything else in the configuration that follows.

How are these kind of scopes normally defined in EBNF? (is there a useful tutorial for this type of thing, too? My google-fu did not turn up anything for Grako or parsers generally)


Solution

  • A trick I use in these situations is to use repetition even if the options can appear only once:

    interface_options
        =
        { @+:(if_name | sec_level | if_addr | if_bridgegroup)  NEWLINE}*
        ;
    

    If necessary, you can use a semantic action to validate that options are not repeated.