ANTLR4 Java:如何将解析树以可读格式打印至标准输出?
如何将ANTLR4生成的解析树以可读格式打印到标准输出?
嘿,我来帮你搞定这个问题!你现在用toStringTree(parser)得到的是紧凑的单行括号格式,确实不太好读。下面给你几个实用的方法,让解析树输出更清晰:
方法1:使用ANTLR自带的TreeViewer(快速格式化输出)
ANTLR的TreeViewer类本来是用来生成可视化解析树的,但它也能直接输出带缩进的层级文本,不需要自己写太多代码。不过要注意它默认依赖Swing,但如果只是打印文本的话完全没问题:
import org.antlr.v4.runtime.tree.Tree; import org.antlr.v4.runtime.tree.TreeViewer; import java.util.Arrays; // 你的原有代码保持不变 CharStream input = CharStreams.fromFileName("testdata/test.txt"); MyLexer lexer = new MyLexer(input); CommonTokenStream tokens = new CommonTokenStream(lexer); MyParser parser = new MyParser(tokens); parser.setBuildParseTree(true); RuleContext tree = parser.record(); // 生成格式化文本并打印 TreeViewer viewer = new TreeViewer(Arrays.asList(parser.getRuleNames()), tree); viewer.setShowRoot(false); // 可选:如果不想显示根节点可以打开这个设置 viewer.print(System.out); // 直接打印到标准输出
这样输出会是带缩进的层级结构,每个节点单独换行,结构一目了然。
方法2:自定义递归遍历(最灵活的方案)
如果不想依赖Swing,或者需要完全定制输出格式,自己写个递归遍历解析树的方法是最佳选择。比如下面这个例子,会给不同层级的节点添加缩进,叶子节点还会显示具体的token文本:
// 自定义打印方法 private static void printFormattedParseTree(Tree tree, Parser parser, int indentLevel) { // 打印当前层级的缩进 for (int i = 0; i < indentLevel; i++) { System.out.print(" "); } String nodeName = parser.getRuleNames()[tree.getRuleIndex()]; // 如果是叶子节点(对应具体Token),打印节点名+文本 if (tree.getChildCount() == 0) { System.out.println(nodeName + ": '" + tree.getText() + "'"); return; } // 非叶子节点,打印规则名后递归遍历子节点 System.out.println(nodeName); for (int i = 0; i < tree.getChildCount(); i++) { printFormattedParseTree(tree.getChild(i), parser, indentLevel + 1); } } // 调用自定义方法 printFormattedParseTree(tree, parser, 0);
这个方法可以根据你的需求随意调整,比如修改缩进符号、添加节点类型标识、过滤掉不需要显示的节点等等,完全可控。
方法3:自定义TreeFormatter扩展toStringTree
如果你想基于现有的toStringTree方法做扩展,可以自定义TreeFormatter来控制输出格式,本质上和方法2的思路类似,但可以直接复用toStringTree的调用逻辑:
import org.antlr.v4.runtime.tree.TreeFormatter; // 自定义格式器 TreeFormatter customFormatter = new TreeFormatter() { @Override public String format(Tree tree, Parser parser) { return buildFormattedTreeString(tree, parser, 0); } private String buildFormattedTreeString(Tree tree, Parser parser, int indent) { StringBuilder sb = new StringBuilder(); sb.append(" ".repeat(indent)); if (tree.getChildCount() == 0) { sb.append(parser.getRuleNames()[tree.getRuleIndex()]) .append(": '").append(tree.getText()).append("'\n"); } else { sb.append(parser.getRuleNames()[tree.getRuleIndex()]).append("\n"); for (int i = 0; i < tree.getChildCount(); i++) { sb.append(buildFormattedTreeString(tree.getChild(i), parser, indent + 1)); } } return sb.toString(); } }; // 使用自定义格式器打印 System.out.println(tree.toStringTree(customFormatter));
总的来说,方法2是最推荐的方案——无额外依赖、灵活性拉满,能完美适配你想要的任何可读格式。
内容的提问来源于stack exchange,提问作者JPT
相关产品推荐
相关产品推荐

