Нужна подсказка для ходьбы по дереву ANTLR4

Прошло много лет с тех пор, как я (один) курс компилятора, так что простите меня, если этот вопрос некорректен. Я также новичок в ANTLR и кодере C, а не Java. Что я хотел бы сделать, так это описать свою проблему, а затем обратиться за советом по поводу наилучшего метода для использования.

Я пытаюсь перевести продукцию ASN.1 в ML. Например,

Foo ::= ENUMERATED {
  bar  (0),    -- some comment 0
  baz  (1)     -- some comment 1
}

в

<Enumerated name="Foo">
  <NamedValues>
    <Unsigned name="bar" value="0" comment="some comment 0"/>
    <Unsigned name="baz" value="1" comment="some comment 1"/>
  </NamedValues>
</Enumerated>

Моя (упрощенная) грамматика ASN1:

assignment : IDENTIFIER typeAssignment ;
typeAssignment : '::=' type ;
type : builtinType ;
builtinType : enumeratedType ;
enumeratedType : 'ENUMERATED' '{' enumerations '}' ;
...

Несколько примеров в «Полном справочнике по ANTLR4» демонстрируют переопределение некоторых методов enterNode или exitNode в BaseListener, и все необходимое находится в контексте Node. Моя проблема в том, что я хотел бы переопределить enterTypeAssignment и exitTypeAssignment, но некоторая информация, которая мне нужна, находится в узлах выше (например, присваивание) или ниже (например, перечисления) в дереве синтаксического анализа.

Достаточно ли здесь описания, чтобы спросить, следует ли мне использовать шаблон посетителя или слушателя? Мы будем очень признательны за любые советы о том, на каких книжных примерах следует сосредоточиться.

Мне повезло с подходом грубой силы:

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 =  "";
    }
  }
}

но, вероятно, есть более элегантное решение...


ОБНОВЛЕНИЕ: я получаю результат, который хочу, сохраняя TerminalNodes в глобальных переменных на моем пути вниз по дереву и имея доступ к этим переменным для переопределения методов в прослушивателе ниже по дереву. Есть ли лучший способ получить доступ к родительскому или прародительскому контексту из данного узла?


person kerlyn    schedule 26.01.2013    source источник
comment
Я не собираюсь давать это в качестве ответа, так как я не использовал ANTLR 4 и не проверял это. Я сделал много этого с ANTLR 3. Когда у вас есть узел, у него должны быть методы для получения дочерних элементов или родителей, если нет, то вам нужно привести его к типу узла, который это делает. См.: Интерфейс ParseTree. Надеюсь, это поможет вам встать на правильный путь. :)   -  person Guy Coder    schedule 28.01.2013


Ответы (1)


Я бы сначала разобрал вещь, а затем использовал посетителя. Преимущество перед использованием Listener заключается в том, что у вас есть вся доступная информация.

Вы также можете использовать цель C#, которая может быть проще в использовании с фоном C.

person Onur    schedule 11.04.2014