【问题标题】:How do I pretty-print productions and line numbers, using ANTLR4?如何使用 ANTLR4 漂亮地打印产品和行号?
【发布时间】:2013-10-13 21:52:44
【问题描述】:

我正在尝试编写一段代码,它将采用 ANTLR4 解析器并使用它为类似于 grun (misc.TestRig) 上的 -tree 选项给出的输入生成 AST。但是,我还希望输出包含所有行号/偏移信息。

例如,代替打印

(add (int 5) '+' (int 6))

我想要

(add (int 5 [line 3, offset 6:7]) '+' (int 6 [line 3, offset 8:9]) [line 3, offset 5:10])

或类似的东西。

目前还没有大量针对 ANTLR4 的访问者示例,但我很确定我可以通过复制 toStringTree(由 grun 使用)的默认实现来完成大部分工作。但是,我没有看到有关行号或偏移量的任何信息。

我希望能写出这样超级简单的代码:

String visit(ParseTree t) {
    return "(" + t.productionName + t.visitChildren() + t.lineNumber + ")";
}

但似乎没有这么简单。我猜我应该能够从解析器中获取行号信息,但我还没有弄清楚如何去做。如何在遍历中获取此行号/偏移量信息?


为了填补以下解决方案中的几个空白,我使用了:

List<String> ruleNames = Arrays.asList(parser.getRuleNames());
parser.setBuildParseTree(true);
ParserRuleContext prc = parser.program();
ParseTree tree = prc;

获取treeruleNamesprogram 是我语法中最高产生式的名称。

【问题讨论】:

  • 有 2 个toStringTree 方法。一个采用 Parser 实例,而另一个只采用 List&lt;String&gt; 的规则名称。
  • @280Z28:你陈述了一个真实的事实。使用解析器参数调用toStringTree 会导致实现获取规则列表(recog.getRuleNames())并将其传递给采用ListtoStringTree。无论如何,这仍然没有解释如何在编写访问者时获取行号/偏移信息。

标签: pretty-print antlr4 abstract-syntax-tree


【解决方案1】:

Trees.toStringTree 方法可以使用ParseTreeListener 来实现。以下侦听器产生与Trees.toStringTree 完全相同的输出。

public class TreePrinterListener implements ParseTreeListener {
    private final List<String> ruleNames;
    private final StringBuilder builder = new StringBuilder();

    public TreePrinterListener(Parser parser) {
        this.ruleNames = Arrays.asList(parser.getRuleNames());
    }

    public TreePrinterListener(List<String> ruleNames) {
        this.ruleNames = ruleNames;
    }

    @Override
    public void visitTerminal(TerminalNode node) {
        if (builder.length() > 0) {
            builder.append(' ');
        }

        builder.append(Utils.escapeWhitespace(Trees.getNodeText(node, ruleNames), false));
    }

    @Override
    public void visitErrorNode(ErrorNode node) {
        if (builder.length() > 0) {
            builder.append(' ');
        }

        builder.append(Utils.escapeWhitespace(Trees.getNodeText(node, ruleNames), false));
    }

    @Override
    public void enterEveryRule(ParserRuleContext ctx) {
        if (builder.length() > 0) {
            builder.append(' ');
        }

        if (ctx.getChildCount() > 0) {
            builder.append('(');
        }

        int ruleIndex = ctx.getRuleIndex();
        String ruleName;
        if (ruleIndex >= 0 && ruleIndex < ruleNames.size()) {
            ruleName = ruleNames.get(ruleIndex);
        }
        else {
            ruleName = Integer.toString(ruleIndex);
        }

        builder.append(ruleName);
    }

    @Override
    public void exitEveryRule(ParserRuleContext ctx) {
        if (ctx.getChildCount() > 0) {
            builder.append(')');
        }
    }

    @Override
    public String toString() {
        return builder.toString();
    }
}

类可以如下使用:

List<String> ruleNames = ...;
ParseTree tree = ...;

TreePrinterListener listener = new TreePrinterListener(ruleNames);
ParseTreeWalker.DEFAULT.walk(listener, tree);
String formatted = listener.toString();

可以通过更新exitEveryRule 方法修改该类以在您的输出中生成信息:

@Override
public void exitEveryRule(ParserRuleContext ctx) {
    if (ctx.getChildCount() > 0) {
        Token positionToken = ctx.getStart();
        if (positionToken != null) {
            builder.append(" [line ");
            builder.append(positionToken.getLine());
            builder.append(", offset ");
            builder.append(positionToken.getStartIndex());
            builder.append(':');
            builder.append(positionToken.getStopIndex());
            builder.append("])");
        }
        else {
            builder.append(')');
        }
    }
}

【讨论】:

  • 这很好用。我正在更新问题以填补几个空白。
猜你喜欢
  • 2011-07-28
  • 2013-10-03
  • 2011-08-05
  • 2010-12-26
  • 1970-01-01
相关资源
最近更新 更多