15

如何在一般遍历解析树时访问 ANTLR4 中的备用标签?或者,是否有任何方法可以复制^ANTLR3 运算符的功能,因为这样可以解决问题。

我正在尝试为遵循简单方法的任何 ANTLR4 语法编写一个 AST 漂亮的打印机(例如使用替代标签命名产品)。我希望能够漂亮地打印一个像3 + 5as(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]+ ;

我无法有效地为plusandminus产品命名,因为将它们拉出到自己的产品中会导致工具抱怨规则是相互左递归的。如果我不能把它们拔出来,我怎么能给这些作品起名字呢?

注 1:我能够通过将“好”终端(例如,上述)放入特殊产生式(产生式以特殊前缀开头,如),从而在方法上摆脱这个+论点。然后我可以只打印其父产品名为“ ...”的终端,而忽略所有其他终端。这对于摆脱, 同时保持和在输出中非常有效。这可以通过ANTLR3 中的 a 来完成。Intraw_raw_+35!

注意 2:我知道我可以为给定语言的每种产品编写专门的漂亮打印机或使用操作,但我想使用 ANTLR4 来解析和生成各种语言的 AST,看起来我应该是一般能写出这么简单漂亮的打印机。换句话说,我只关心获得 AST,我宁愿不必为了获得 AST 而使用定制的漂亮打印机来阻碍每个语法。也许我应该回到ANTLR3?

4

2 回答 2

0

我建议将漂亮的打印机实现为具有嵌套访问者类的侦听器实现,以获取各种上下文对象的名称。

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";
    }
}
于 2014-01-09T03:09:04.777 回答
0

API 不包含访问备用标签的方法。

但是,有一种解决方法。ANTLR4 使用备用标签生成 java 类名,并且可以在运行时访问这些 java 类。

例如:要访问 ANTLR4 中的备用标签,同时一般遍历解析树(使用侦听器),您可以使用以下函数:

// Return the embedded alternate label between
// "$" and "Context" from the class name
String getCtxName(ParserRuleContext ctx) {
    String str = ctx.getClass().getName();
    str = str.substring(str.indexOf("$")+1,str.lastIndexOf("Context"));
    str = str.toLowerCase();
    return str;
}

示例使用:

@Override
public void exitEveryRule(ParserRuleContext ctx) {
    System.out.println(getCtxName(ctx));
}
于 2021-01-18T19:23:21.330 回答