5

I am using ANTLR 4 to create a parser, and I have completed my grammar. I need to inject some Java code into the resulting parser file that ANTLR auto-generates for me.

If I want to include a method in the resulting parser, I can add this to the ANTLR grammar:

@parser::members
{
  @Override
  public CGrammarParser.CSnippetContext call()
  {
    return cSnippet();
  }
}

If I want to include some import statements, I can add this to the grammar:

@header
{
  import java.lang.Thread;
  import java.lang.InterruptedException;
  import java.util.concurrent.Callable;
}

If I want to modify the class declaration so that it implements an interface, how do I do that? In other words, this is what ANTLR auto-generates:

public class CGrammarParser extends Parser 
{
  ...
}

But this is what I want it to generate:

public class CGrammarParser extends Parser implements Callable<CGrammarParser.CSnippetContext> 
{
  ...
}
james.garriss
  • 12,959
  • 7
  • 83
  • 96

2 Answers2

3

No, not like you describe (via interface(s)). However, you can define a super class your parser should extend from. This super class should, of course, extend ANTLR's Parser class. In your own (abstract) parser class, you then define the interfaces you want to implement.

Here's how that could work:

CallableParser

import java.util.concurrent.Callable;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.TokenStream;

public abstract class CallableParser extends Parser implements Callable<CGrammarParser.CSnippetContext>
{
    public CallableParser(TokenStream input)
    {
        super(input);
    }
}

CGrammar.g4

grammar CGrammar;

options
{
  superClass = CallableParser;
}

@header
{
  import java.lang.Thread;
  import java.lang.InterruptedException;
  import java.util.concurrent.Callable;
}

@parser::members
{
  @Override
  public CGrammarParser.CSnippetContext call()
  {
    return cSnippet();
  }
}

cSnippet
 : ANY*? EOF
 ;

ANY
 : .
 ;
Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
  • You can also do this the other way around: you can inherit from the generated parser and implement what you need in the derived class (and that's cleaner than using the `@parser::members` construct IMO, since there's more separation between grammar and logic). And just for the record, the answer to this question is even simpler for C# parsers: these are `partial` classes. – Lucas Trzesniewski Jan 08 '16 at 20:24
  • I finally got around to implementing this, but it doesn't work. It throws an `ExceptionInInitializerError`. If I manually add in the extends and implements that I want (see my question), it works fine. I feel like this answer is very close, but ANTLR's not happy about it. – james.garriss Jun 21 '16 at 14:28
  • I tried your answer, @LucasTrzesniewski, and it works! If you want to add an answer to the question, I will accept it as the correct answer. Thank you. – james.garriss Jun 21 '16 at 14:38
0

Credit for this answer goes to Lucas Trzesniewski.

To have your ANTLR parser implement an interface, no Java is required in the grammar file (.g4) itself. Simply do the following:

1) Create an callable parser class that extends the ANTLR parser and implements the interface in question, something like:

public class CallableParser extends CGrammarParser implements Callable<CGrammarParser.CSnippetContext>
{
    public CallableParser(TokenStream input)
    {
        super(input);
    }

    @Override
    public CGrammarParser.CSnippetContext call()
    {
        return cSnippet();
    }
}

2) Instead of calling your ANTLR parser, call the CallableParser instead, like this:

CharStream in = new ANTLRInputStream(input);
CGrammarLexer lexer = new CGrammarLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
// Instead of doing this...
// CGrammarParser parser = new CGrammarParser(tokens);
// Do this...
CallableParser parser = new CallableParser(tokens);
james.garriss
  • 12,959
  • 7
  • 83
  • 96