MainLangFile.g4
grammar MainLangFile;
import Child1LangFile, Child2LangFile;
parse:
A (child1 | child2) EOF
;
A: 'A';
B: 'B';
C: 'C';
Child1LangFile.g4
grammar Child1LangFile;
child1:
CHILD1
;
CHILD1: 'CHILD1';
Child2LangFile.g4
grammar Child2LangFile;
child2:
CHILD2
;
CHILD2: 'CHILD2';
My Java application:
public static class MyApp {
public void execute(String query) {
MainLangFileLexer lexer = new MainLangFileLexer(CharStreams.fromString(query));
MainLangFileParser parser = new MainLangFileParser(new CommonTokenStream(lexer));
MainLangFileParser.ParseContext parse = parser.parse();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(new Child1Listener(), parse);
walker.walk(new Child2Listener(), parse);
}
static class Child1Listener extends MainLangFileBaseListener {
@Override
public void enterChild1(MainLangFileParser.Child1Context ctx) {
}
}
static class Child2Listener extends MainLangFileBaseListener {
@Override
public void enterChild2(MainLangFileParser.Child2Context ctx) {
}
}
}
I'm trying to have a multi-grammar structure. The solution I've above works perfectly in the sense that each ChildXListener
is properly invoked based on the query. It would be the same as just having a single listener and implementing both methods.
The issue begins where there is a enterCommon()
method invoked, it is invoked for both listeners even though I "forked" the language and went only on one specific direction.
I need a clever way of selecting which listener should be used by ParseTreeWalker
.
Also, I'd rather have each listener, extending their respective grammars, i.e.: Child1Listener
should extends Child1LangFileBaseListener
, by doing so I have a clear scope of what methods I can safely override as only Child1LangFile.g4
rules will be available.
I couldn't figure out a way of doing this without initializing Child1Lexer
, Child1Parser
and parsing everything again, either way, this brings me back to the previous issue, how do I know which lexer/parser to use?
Any thoughts? Thanks in advance.
There is no way to tell up front which parser rule(s) are going to be invoked for a given input source.
Since a visitor will not cause the entire parse tree to be visited (you determine that by overriding only the method you wish to visit), you could start off by writing a visitor that can be used up until the rule that contains the (child1 | child2)
and inside that visit...
method, invoke the specific listener.
That could look something like this:
class MainVisitor extends MainLangFileBaseVisitor<Object> {
@Override
public Object visitParse(MainLangFileParser.ParseContext ctx) {
if (ctx.child1() != null) {
ParseTreeWalker.DEFAULT.walk(new Child1Listener(), ctx);
}
else {
ParseTreeWalker.DEFAULT.walk(new Child2Listener(), ctx);
}
return null;
}
}
class Child1Listener extends Child1LangFileBaseListener {
// ...
}
class Child2Listener extends Child2LangFileBaseListener {
// ...
}