Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need an ANTLR4 tree walking clue

Tags:

antlr4

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?

like image 956
kerlyn Avatar asked Nov 13 '22 12:11

kerlyn


1 Answers

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.

like image 193
Onur Avatar answered Dec 19 '22 18:12

Onur