Firewall configuration parser extraneous input

Viewed 58

I'm attempting to write a configuration parser for some firewall equipments. I'm using ANTLR for the first time.

What I want to parse is typically the following sort of text :

config wireless-controller global
    set name ''
    set location ''
    set max-retransmit 3
    set data-ethernet-II disable
    set link-aggregation disable
    set mesh-eth-type 8755
    set fiapp-eth-type 5252
    set discovery-mc-addr 221.0.4.254
    set max-clients 0
    set rogue-scan-mac-adjacency 6
    set ipsec-base-ip 172.252.0.4
    set wtp-share disable
    set ap-log-server disable
    set ap-log-server-ip 0.0.0.0
    set ap-log-server-port 0
end

The input data is "config" blocks with configuration lines. I already came up with these rules :

   1   │ grammar Fortigate ;
   2   │ 
   3   │ /*
   4   │  * Tokens
   5   │ */
   6   │ 
   7   │ WHITESPACE  : (' ' | '\t')+ -> skip ;
   8   │ NEWLINE     : ('\r'? '\n' | '\n' | '\r')+ ;
   9   │ WORD        : ([a-zA-Z0-9] | '.' | [\-_'"])+ ;
  10   │ ENDBLOCK    : 'end' ;
  11   │ EDITSTART   : 'edit' ;
  12   │ NEXTEDIT    : 'next' ;
  13   │ /*
  14   │  * Parser rules
  15   │ */
  16   │ configline  : ('set'|'unset') WORD+ NEWLINE ;
  17   │ startconfigblock  : 'config' WORD+ NEWLINE ;
  18   │ editline    : EDITSTART '"'.+?'"' ;
  19   │ editblock   : editline configline+ NEXTEDIT NEWLINE ;
  20   │ configblock : startconfigblock (editblock | configline)+ ENDBLOCK NEWLINE;
  21   │ 
  22   │ startRule   : configblock+ ;

I still have issues as antlr doesnt seem to like then ending "end\n" of the data to parse : line 12:0 extraneous input 'end' expecting {'set', 'unset', 'end', 'edit'}

However I have quite clean token tree antlr4 Fortigate startRule -gui

Antlr doesnt like the ending 'end' text though its in the configblock rule, and its not consumed by another rule ...

Thanks for your help !

1 Answers

The input end is being tokenised as a WORD. This is because when the lexer can match the same characters for multiple rules, the one defined first "wins". The solution, move the keywords above your WORD rule:

ENDBLOCK    : 'end' ;
EDITSTART   : 'edit' ;
NEXTEDIT    : 'next' ;
WORD        : ([a-zA-Z0-9] | '.' | [\-_'"])+ ;

If you ever want to match end also as a WORD, then introduce a parser rule like this:

word
 : WORD
 | END
 ;

and use this word in your parser rules instead of WORD.

BTW, ([a-zA-Z0-9] | '.' | [\-_'"])+ can be rewritten as [a-zA-Z0-9.\-_'"]+ and (' ' | '\t')+ as [ \t]+.

And finally, it is always a good idea to "anchor" the start rule of your parser with the EOF token: that way you're forcing the parser to consume the entire token stream and not stop halfway.

Related