You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中可视化渲染ANTLR4生成的无规则名算术表达式解析树?

嘿,刚接触ANTLR4就能搞定Visitor转后缀表达式,已经很棒了!关于解析树可视化且不显示规则名的需求,我刚好有两种实用的实现方式,给你一步步拆解:

方法一:基于ANTLR自带TreeViewer自定义节点显示

ANTLR自带的TreeViewer默认会显示规则名称,但我们可以通过自定义文本提供者来替换成我们想要的内容(比如操作符、数字,而非规则名)。

步骤1:实现自定义ParseTreeTextProvider

这个类负责决定每个节点在可视化时显示的文本:

import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.gui.ParseTreeTextProvider;
import org.antlr.v4.runtime.tree.TerminalNode;

public class CustomTreeTextProvider implements ParseTreeTextProvider {
    @Override
    public String getText(ParseTree node) {
        // 叶子节点(比如数字、操作符)直接显示token文本
        if (node instanceof TerminalNode) {
            return ((TerminalNode) node).getSymbol().getText();
        }
        // 非叶子节点根据上下文类型显示对应的操作符(空字符串代表不显示规则名)
        String nodeClassName = node.getClass().getSimpleName();
        return switch (nodeClassName) {
            case "AddContext" -> "+";
            case "SubContext" -> "-";
            case "MulContext" -> "*";
            case "DivContext" -> "/";
            case "ParenContext" -> "()";
            default -> ""; // 根节点或其他规则节点不显示文本
        };
    }
}

注意:这里的上下文类名(比如AddContext)是ANTLR根据你的语法规则自动生成的,比如你语法里写add : exp '+' exp;,就会生成AddContext,要和你自己的语法对应上哦。

步骤2:用TreeViewer显示自定义解析树

在你的主程序里获取解析树,然后传入自定义文本提供者:

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.gui.TreeViewer;

import javax.swing.*;
import java.util.Arrays;

public class ExpressionVisualizer {
    public static void main(String[] args) {
        // 1. 解析表达式得到ParseTree
        CharStream input = CharStreams.fromString("3+4*(5-2)");
        ExpAnalyserLexer lexer = new ExpAnalyserLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ExpAnalyserParser parser = new ExpAnalyserParser(tokens);
        ParseTree parseTree = parser.exp(); // 这里的exp是你语法里的根规则

        // 2. 初始化自定义文本提供者和TreeViewer
        CustomTreeTextProvider textProvider = new CustomTreeTextProvider();
        TreeViewer viewer = new TreeViewer(Arrays.asList(parser.getRuleNames()), parseTree);
        viewer.setParseTreeTextProvider(textProvider);

        // 3. 用Swing窗口展示
        JFrame frame = new JFrame("中缀表达式解析树");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(viewer);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
方法二:生成Graphviz DOT文件渲染为图片

如果需要把解析树保存为图片或者嵌入到其他应用中,生成DOT文件再转成图片是更灵活的方式。

步骤1:编写解析树转DOT的工具类

import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;

public class ParseTreeToDot {
    private int nodeCounter = 0;
    private final StringBuilder dotBuilder = new StringBuilder();

    public String generateDot(ParseTree tree) {
        dotBuilder.append("digraph ExpressionParseTree {\n");
        dotBuilder.append("    node [shape=ellipse, style=filled, fillcolor=lightcyan];\n");
        traverseTree(tree);
        dotBuilder.append("}\n");
        return dotBuilder.toString();
    }

    private int traverseTree(ParseTree node) {
        int currentNodeId = nodeCounter++;
        String nodeLabel = getNodeLabel(node);
        dotBuilder.append(String.format("    node%d [label=\"%s\"];\n", currentNodeId, nodeLabel));

        // 遍历子节点并添加边
        for (int i = 0; i < node.getChildCount(); i++) {
            ParseTree child = node.getChild(i);
            int childNodeId = traverseTree(child);
            dotBuilder.append(String.format("    node%d -> node%d;\n", currentNodeId, childNodeId));
        }
        return currentNodeId;
    }

    private String getNodeLabel(ParseTree node) {
        if (node instanceof TerminalNode) {
            return ((TerminalNode) node).getSymbol().getText();
        }
        // 同样根据上下文类型返回对应文本
        String className = node.getClass().getSimpleName();
        return switch (className) {
            case "AddContext" -> "+";
            case "SubContext" -> "-";
            case "MulContext" -> "*";
            case "DivContext" -> "/";
            case "ParenContext" -> "()";
            default -> "";
        };
    }
}

步骤2:生成并渲染DOT文件

你可以选择调用本地安装的Graphviz命令,或者用Java库graphviz-java来处理:

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;

import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Paths;

public class DotRenderer {
    public static void main(String[] args) throws IOException, InterruptedException {
        // 1. 获取解析树
        CharStream input = CharStreams.fromString("3+4*(5-2)");
        ExpAnalyserLexer lexer = new ExpAnalyserLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ExpAnalyserParser parser = new ExpAnalyserParser(tokens);
        ParseTree parseTree = parser.exp();

        // 2. 生成DOT内容
        ParseTreeToDot dotGenerator = new ParseTreeToDot();
        String dotContent = dotGenerator.generateDot(parseTree);

        // 3. 保存为DOT文件
        try (FileWriter writer = new FileWriter("expression_tree.dot")) {
            writer.write(dotContent);
        }

        // 方式A:调用本地Graphviz的dot命令生成PNG(需提前安装Graphviz并加入PATH)
        Process process = new ProcessBuilder("dot", "-Tpng", "expression_tree.dot", "-o", "expression_tree.png").start();
        process.waitFor();

        // 方式B:用graphviz-java库(无需本地安装),先添加依赖
        // 比如Maven依赖:<dependency><groupId>com.github.jabrena</groupId><artifactId>graphviz-java</artifactId><version>0.1.7</version></dependency>
        // Graphviz.fromString(dotContent).render(Format.PNG).toFile(Paths.get("expression_tree.png"));
    }
}

小提示

  • 如果你语法里的规则名不同(比如根规则是expression而非exp),记得调整代码里的parser.exp()为parser.expression()。
  • 非叶子节点的文本可以根据你的需求调整,比如括号节点可以选择不显示,或者显示(),完全看你想要的可视化效果。

内容的提问来源于stack exchange,提问作者AaySquare

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:54:24