Search code examples
javaantlrsuperclass

ANTLR @header, @parser, superClass option and basic file io (Java)


I want to use parser actions with basic file io (Java), e. g. PrintWriter in an ANTLR grammar. Must I use the superClass option or can I use @header? In both cases how can I declare the PrintWriter-object and how must I handle the exceptions?


Solution

  • The option superClass=... is used to let your Parser extend a custom class. So, I don't think that is what you're after.

    Everything inside the @header section will be placed at the start of your Parser class. This is used to import classes:

    @header {
      import java.io.PrintWriter;
    }
    

    Note that @header {...} is short for @parser::header {...}. You can also define: @lexer::header {...} for your lexer.

    And inside @member {...} (or: @parser::member {...}, @lexer::member {...}) sections, you can add instance variables and methods that can be used inside either the Parser or Lexer:

    @header {
      import java.io.PrintWriter;
    }
    
    @members {
      PrintWriter writer;
    }
    

    A small demo of a grammar whose parser will write the parsed numbers to a specific writer:

    grammar T;
    
    @header {
      import java.io.PrintWriter;
    }
    
    @members {
      PrintWriter writer;
    
      public TParser(TokenStream input, String fileName) {
        super(input);
        try {
          writer = new PrintWriter(fileName);
        } catch(Exception e) {
          e.printStackTrace();
        }
      }
    }
    
    parse
      :  numbers EOF
      ;
    
    numbers
      :  (NUM {
                writer.println("parsed: " + $NUM.text);
                writer.flush();
              }
         )+
      ;
    
    NUM : '0'..'9'+;
    WS  : ' ' {skip();};
    

    which can be tested with:

    import java.io.File;
    import org.antlr.runtime.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        String source = "42 500000000 666";
        TLexer lexer = new TLexer(new ANTLRStringStream(source));
        TParser parser = new TParser(new CommonTokenStream(lexer), "log.txt");
        parser.parse();
      }
    }
    

    If you run the class above, a file called log.txt has been created containing:

    parsed: 42
    parsed: 500000000
    parsed: 666
    

    Note that there is a strict order of all these @... and options {...} etc. instances:

    1. grammar definition
    2. options block (no @ sign!)
    3. tokens block (no @ sign!)
    4. @header block
    5. @members block


    grammar T;
    
    options {
      // options here
    }
    
    tokens {
      // imaginary tokens here
    }
    
    @header  { 
      // ... 
    }
    
    @members { 
      // ... 
    }
    

    EDIT

    ANTLRStarter wrote:

    How can I add code which is executed at the end of the the lexer/parser class?

    There's no built-in functionality for such a thing. But you can easily create a custom method wrapUp() in your parser:

    @members {
    
      // ...
    
      private void wrapUp() {
        // wrap up your parser in here
      }
    }
    

    and then automatically call that method from the entry point of your grammar like this:

    parse
    @after {this.wrapUp();}
      :  numbers EOF
      ;
    

    Any code placed in the @after {...} block of a rule is executed when everything in the rule is properly matched.