一般遍历解析树的同时访问 ANTLR4 中的备用标签? 或者,有没有办法复制ANTLR3的^
运算符的功能,因为这可以解决问题。
我正在尝试为遵循简单方法的任何 ANTLR4 语法编写一台漂亮的 AST 打印机(例如使用备用标签命名作品)。 我希望能够漂亮地打印一个像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:我能够通过在特殊作品(以特殊前缀开头的作品,如raw_
)中放置"好"终端(例如上面的Int
)在方法上摆脱+
论点。 然后我只能打印那些父产品被命名为"raw_
..."并消除所有其他。 这对于摆脱+
非常有效,同时在输出中保持3
和5
。 这可以通过ANTLR3中的!
来完成。
注2:我知道我可以编写一个专门的漂亮打印机或为给定语言的每种产品使用操作,但我想使用ANTLR4来解析和生成各种语言的AST,似乎我应该能够编写如此简单的漂亮打印机一般。 换句话说,我只关心获得 AST,我宁愿不必为了获得 AST 而用量身定制的漂亮打印机来阻碍每个语法。 也许我应该回到ANTLR3?
我建议将漂亮的打印机实现为具有嵌套访问者类的侦听器实现,以获取各种上下文对象的名称。
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";
}
}
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));
}