It's been many years since my (one) compiler course so forgive me if this question is ill-posed. I am also new to ANTLR and a C, not Java, coder. What I'd like to do is describe my problem and then seek advice on the best technique to employ.
I am trying to translate ASN.1 productions into a ML. For example,
Foo ::= ENUMERATED {
bar (0), -- some comment 0
baz (1) -- some comment 1
}
into
<Enumerated name="Foo">
<NamedValues>
<Unsigned name="bar" value="0" comment="some comment 0"/>
<Unsigned name="baz" value="1" comment="some comment 1"/>
</NamedValues>
</Enumerated>
My (simplified) ASN1 grammar is:
assignment : IDENTIFIER typeAssignment ;
typeAssignment : '::=' type ;
type : builtinType ;
builtinType : enumeratedType ;
enumeratedType : 'ENUMERATED' '{' enumerations '}' ;
...
Several of the examples in "The Definitive ANTLR4 Reference" demonstrate
overriding some enterNode or exitNode method in the BaseListener and
everything needed is in the Node's context. My problem is that I'd like
to override enterTypeAssignment
and exitTypeAssignment
, but some of the
info I need is in nodes higher (e.g. assignment) or lower (e.g. enumerations)
on the parse tree.
Is there enough description here to ask whether I should use a visitor or listener pattern? Any advice on which book example(s) to focus on would be greatly appreciated.
I'm having some luck with a brute force approach:
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.misc.Interval;
public class MylListener extends ASN1BaseListener {
ASN1Parser parser;
String id = "";
String assignedType = "";
public MyListener(ASN1Parser parser) {this.parser = parser;}
@Override
public void enterAssignment(ASN1Parser.AssignmentContext ctx) {
id = ctx.IDENTIFIER().getText();
}
/** Listen to matches of typeAssignment **/
@Override
public void enterTypeAssignment(ASN1Parser.TypeAssignmentContext ctx) {
if ( ctx.type() != null ) {
if ( ctx.type().builtinType() != null ) {
if ( ctx.type().builtinType().enumeratedType() != null ) {
assignedType = "Enumerated";
System.out.println("");
System.out.println("<Enumerated name=\""+id+"\">");
...
}
}
}
}
@Override
public void exitTypeAssignment(ASN1Parser.TypeAssignmentContext ctx) {
if (assignedType.length() > 0) {
System.out.println("</"+assignedType+">");
assignedType = "";
}
}
}
but there's probably a more elegant solution...
UPDATE: I'm getting the result I want by saving TerminalNodes in global vars on my way down the tree and having those vars accessible to override methods in the Listener further down the tree. Is there a better way to access parent or grandparent context from a given node?
I'd first parse the thing and then use a visitor. The advantage over using a Listener is that you have all the information available.
You can also use the C# target which may be easier to use with a C background.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With