ANTLR's AST tree grammar + lists
I've have read a lot trying to find a way to cleanly consume lists in ANTLR's tree grammar. Here is what I have tried and their results (I really hope I'm missing something trivial)...
Using += Syntax
program returns [someInterface result]
: m+=method* EOF {result = new SomeClass(m);};
method returns [SomeMethod result] : <definition here>
This fails with...
rule '+=' list labels are not allowed w/o output option
If I set the output to either "AST" or "template" (the only options) the method signatures of the generated class change. That is, m
will not by a List of SomeMethod(s) but rather a List of Nodes or Templates respectively. I am open to suggestions if there is a way to make this method work.
Using Rule Scopes
program returns [CompilesToJav开发者_如何学编程aByteCode result]
scope {
List<SomeMethod> methods;
}
@init {
$program::methods = new ArrayList<SomeMethod>();
}
: (m=method {$program::methods.add(m);})*
EOF {result = new SomeClass($program::methods);};
This seems to work, though I'll admit that I haven't tested it with nested/recursive cases yet.
The End Goal
I want to build a set of classes that represent my language (Class, Method, Variable, Statement, ect) so that I can do some static analysis and optimization before I generate compiled code. To that end, I need to be able to consume lists. I expected += syntax to "just work" but I might be missing something. The second method works but seem overly verbose and inelegant.
The Question
What is the proper why to consume a list, in ANTLR's tree grammar, to pass to my concrete classes?
You can cut the scope from your example and do it all with local variables.
program returns [CompilesToJavaByteCode result]
@init {
List<SomeMethod> methods = new ArrayList<SomeMethod>();
}
: (m=method { methods.add($m.result); })* EOF
{ $result = new SomeClass(methods); };
This is what we do for this case at work. Another option is to have your method rule handle it:
program returns [CompilesToJavaByteCode result]
@init {
List<SomeMethod> methods = new ArrayList<SomeMethod>();
}
: method[methods]* EOF { $result = new SomeClass(methods); };
method [List<SomeMethod> methods]
: ...
{ methods.add(new SomeMethod(...); };
I don't really like the second option as a method rule probably shouldn't care what is being done with its results like that. But you can imagine a structure where the top rule creates a ClassBeingCompiled
and the rest of the code incrementally fills it out with .addMethod()
.
精彩评论