antlr - ANTLR4 change listener during parse -


i have antlr4 listener handles standard , well-formed grammar, struggling how deal non-standard implementations. although of variants go through lexer without problems parse stage lot trickier.

a traditional way of doing like

// header of document variant = standard; if (header.indexof("microsoft") != -1) {   variant = microsoft; } else if (header.indexof("google") != -1) {   variant = google; }  ...  // parsing particular element if (variant.equals(microsoft)) {   // microsoft-specific stuff } else if (variant.equals(google)) {   // google-specific stuff } else {   // standard stuff } 

but becomes unmaintainable. obvious solution have parsetreelistener standard implementation , subclass each variant, don't know variant until i've started parse.

so how can either switch 1 listener part-way through parse, or restart parse new listener once know variant i'm dealing with?

if these variants occur frequently, might want consider embedding custom code handle context sensitive parsing using predicates (the {...}? construct in following pseudo grammar):

rule  : { boolean-expression-a }? a-alternative  | { boolean-expression-b }? b-alternative  | /* fall through */        not-a-or-b-alternative  ; 

let's want parse file containing chunks. chunk consists of header , data row. in header can set variant. data of normal variant contains 3 numbers, google's variant contains 2 numbers , microsoft's variant contains single number. example of such file this:

header: none data: 1 2 3  header: google data: 4 5 header: microsoft data: 6 

and here's demo of context sensitive antlr v4 grammar able parse this:

grammar t;  @parser::members {    enum variant {       google,      microsoft,      other;       public static variant tryvalueof(string name) {       try {         return variant.valueof(name.touppercase());       }       catch(exception e) {         return other;       }     }   }     private variant variant = variant.other; }  parse  : chunk+ eof  ;  chunk  : header data  ;  header  : k_header colon name {variant = variant.tryvalueof($name.text);}  ;  data  : {variant == variant.microsoft}? k_data colon number               #microsoftdata  | {variant == variant.google}?    k_data colon number number        #googledata  |                                 k_data colon number number number #otherdata  ;  k_data   : 'data'; k_header : 'header'; name     : [a-za-z]+; number   : [0-9]+; colon    : ':'; space    : [ \t\r\n] -> skip; 

resulting in following parse:

enter image description here


Comments