Как я могу получить доступ к альтернативным меток в ANTLR4, в то время как в общем случае просматривает дерево разбора?
Как я могу получить доступ к альтернативным меток в ANTLR4, в то время как в общем случае просматривает дерево разбора? Или, альтернативно, есть ли способ воспроизвести функциональность оператора ^
ANTLR3, поскольку это могло бы сделать трюк.
Я пытаюсь написать симпатичный принтер AST для любой грамматики ANTLR4, придерживающейся простой методологии (например, названия игр с альтернативными метками). Я хотел бы иметь возможность напечатать такой термин, как 3 + 5
как (int_expression (plus (int_literal 3) (int_literal 5)))
, или что-то подобное, учитывая грамматику следующим образом:
int_expression
: int_expression '+' int_expression # plus
| int_expression '-' int_expression # minus
| raw_int # int_literal
;
raw_int
: Int
;
Int : [0-9]+ ;
Я не могу эффективно указывать имена в продуктах plus
и minus
, потому что вытаскивание их в собственное производство заставляет инструмент жаловаться на то, что правила взаимно левые рекурсивны. Если я не смогу вытащить их, как я могу назвать эти названия?
Примечание 1: Я смог избавиться от аргумента +
методологически, положив "хорошие" терминалы (например, Int
выше) в специальные произведения (производные, начиная со специального префикса, например raw_
), Затем я мог печатать только те терминалы, чьи родительские постановки называются "raw_
..." и elide все остальные. Это отлично работало для избавления от +
, сохраняя 3
и 5
в выходе. Это можно сделать с помощью !
в ANTLR3.
Примечание 2: Я понимаю, что я мог написать специализированный симпатичный принтер или использовать действия для каждого выпуска данного языка, но я бы хотел использовать ANTLR4 для анализа и создания АСТ для разных языков, и это похоже на Я должен был бы написать такой простой симпатичный принтер в целом. С другой стороны, я забочусь только о получении АСТ, и я бы предпочел не обременять каждую грамматику специальным принтером, чтобы получить АСТ. Возможно, мне стоит вернуться к ANTLR3?
Ответы
Ответ 1
Я предлагаю реализовать симпатичный принтер как реализацию listener с вложенным классом посетителей, чтобы получить имена различных объектов контекста.
private MyParser parser; // you'll have to assign this field
private StringBuilder builder = new StringBuilder();
@Override
public void enterEveryRule(@NotNull ParserRuleContext ctx) {
if (!builder.isEmpty()) {
builder.append(' ');
}
builder.append('(');
}
@Override
public void visitTerminalNode(@NotNull TerminalNode node) {
// TODO: print node text to builder
}
@Override
public void visitErrorNode(@NotNull TerminalNode node) {
// TODO: print node text to builder
}
@Override
public void exitEveryRule(@NotNull ParserRuleContext ctx) {
builder.append(')');
}
protected String getContextName(@NotNull ParserRuleContext ctx) {
return new ContextNameVisitor().visit(ctx);
}
protected class ContextNameVisitor extends MyParserBaseVisitor<String> {
@Override
public String visitChildren() {
return parser.getRuleNames()[ctx.getRuleIndex()];
}
@Override
public String visitPlus(@NotNull PlusContext ctx) {
return "plus";
}
@Override
public String visitMinus(@NotNull MinusContext ctx) {
return "minus";
}
@Override
public String visitInt_literal(@NotNull MinusContext ctx) {
return "int_literal";
}
}