Прошло много лет с тех пор, как я (один) курс компилятора, так что простите меня, если этот вопрос некорректен. Я также новичок в 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 в глобальных переменных на моем пути вниз по дереву и имея доступ к этим переменным для переопределения методов в прослушивателе ниже по дереву. Есть ли лучший способ получить доступ к родительскому или прародительскому контексту из данного узла?