Java中ANTLR解析树调用Visitor后无法显示的问题咨询
问题拆解与解决方案
咱先从你遇到的核心问题说起:调用Visitor后解析树无法显示,移除Visitor调用就正常——大概率是Visitor执行过程中抛出了未捕获异常,导致后续的解析树显示代码根本没机会执行!
一、为什么调用Visitor后解析树不显示?
ANTLR的BaseVisitor默认不会修改解析树结构,也不会影响解析树的可用性。但如果你的ExpAnalyserBaseVisitor重写的visit方法里有逻辑错误(比如空指针、类型转换失败、未处理的分支),调用visit(parser.exp())时就会抛出异常,打断代码执行流程,后面的JFrame显示代码自然就跑不起来了。
你可以先给Visitor调用加个try-catch验证一下:
try { Object answer = new ExpAnalyserBaseVisitor<>().visit(parser.exp()); } catch (Exception e) { e.printStackTrace(); // 看看具体是什么异常 } // 再执行解析树显示代码
如果控制台打印了异常信息,那问题根源就找到了——修复Visitor里的逻辑错误即可。
另外还要确认代码顺序:必须先获取解析树节点,再调用Visitor,最后用原始节点显示,比如:
// 1. 先生成解析树(这一步已经把树结构保存下来了) ParserRuleContext parseTree = parser.exp(); // 2. 访问解析树(只是遍历,不会修改原始树) Object answer = new ExpAnalyserBaseVisitor<>().visit(parseTree); // 3. 用原始的parseTree显示,而不是visit的返回值! showParseTree(parseTree);
要是你不小心用了visit的返回值(那是你自定义的计算结果,不是树节点)去显示,肯定出问题。
二、更优的解析树显示方案
除了自己用JFrame+TreeViewer,还有几个更方便的选项:
- 官方TestRig工具:这是ANTLR自带的调试神器,直接命令行就能生成可视化解析树,不用写Java代码。比如:
比如你的语法是java org.antlr.v4.gui.TestRig 你的语法文件名 入口规则名 -gui 输入文件.txtExp.g4,入口规则是exp,输入是input.txt,就跑上面的命令,直接弹出GUI窗口显示解析树,调试语法特别方便。 - Graphviz导出图片:如果需要把解析树保存成图片,可以用ANTLR的
DotTreeGenerator生成DOT格式文件,再用Graphviz渲染成PNG/SVG。示例代码:DotTreeGenerator dotGenerator = new DotTreeGenerator(); String dot = dotGenerator.toDot(parseTree); // 把dot字符串写入文件,然后调用Graphviz命令生成图片 // 比如:dot -Tpng parse_tree.dot -o parse_tree.png - 自定义Swing交互:如果要集成到自己的应用里,可以基于TreeViewer扩展,比如给节点加点击事件、调整字体颜色、折叠/展开分支等,灵活性很高。
三、移除解析树中的规则名称
TreeViewer默认会显示非终结符的规则名称(比如exp、term),要去掉的话,最方便的是自定义TreeCellRenderer,修改节点的显示文本:
TreeViewer viewer = new TreeViewer(Arrays.asList(parser.getRuleNames()), parseTree); // 自定义渲染器,隐藏规则名称 viewer.setCellRenderer(new DefaultTreeCellRenderer() { @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (value instanceof ParseTree) { ParseTree node = (ParseTree) value; if (node instanceof TerminalNode) { // 终结符显示token的实际文本 setText(((TerminalNode) node).getSymbol().getText()); } else { // 非终结符直接清空文本,隐藏规则名称 setText(""); } } return this; } });
这样渲染出来的解析树就只有终结符的文本,规则名称会被隐藏。
内容的提问来源于stack exchange,提问作者AaySquare
相关产品推荐
相关产品推荐

