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

如何用ANTLR4的BaseListener类实现中缀转后缀表达式并输出到文件

没问题!用ANTLR的BaseListener来实现中缀转后缀(逆波兰表达式)完全可行,核心思路就是利用运算符栈来处理优先级,遇到操作数直接输出,遇到运算符则按规则入栈/出栈。我给你一步步拆解实现方法,结合代码示例说明:

1. 先给Listener加核心成员变量

首先,你需要继承ExpAnalyserBaseListener,并添加几个关键变量:

  • 一个运算符栈:用Deque<String>(推荐LinkedList)来存储待处理的运算符,用来管理优先级
  • 一个结果容器:用StringBuilder或者List<String>来拼接后缀表达式
  • 一个优先级映射表:定义每个运算符的优先级(比如*//优先级高于+/-)

2. 重写Listener的关键方法

这一步要完全对应你的ExpAnalyser.g4语法规则,我先假设你的语法是最常见的结构(如果你的规则名不同,直接替换就行):

// 示例ExpAnalyser.g4核心规则
expr: addSub;
addSub: mulDiv ( addOp mulDiv )*;
mulDiv: primary ( mulOp primary )*;
primary: NUMBER | '(' expr ')';
addOp: '+' | '-';
mulOp: '*' | '/';

针对这个语法,我们重写以下方法:

  • 处理操作数:进入NUMBER规则时,直接把数字加到结果里
  • 处理运算符:进入addOp/mulOp规则时,先弹出栈中优先级>=当前运算符的所有运算符到结果,再把当前运算符压栈
  • 处理括号:左括号直接压栈;右括号则弹出运算符直到遇到左括号(左括号弹出但不加入结果)
  • 收尾处理:退出顶层expr规则时,把栈中剩余的所有运算符弹出到结果

3. 完整的Listener实现代码

import org.antlr.v4.runtime.tree.ParseTreeListener;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
import java.io.FileWriter;
import java.io.IOException;

public class PostfixConverterListener extends ExpAnalyserBaseListener {
    // 运算符栈
    private final Deque<String> operatorStack = new LinkedList<>();
    // 存储后缀表达式的字符串构建器
    private final StringBuilder postfixResult = new StringBuilder();
    // 运算符优先级定义:数字越大优先级越高
    private static final Map<String, Integer> OP_PRIORITY = Map.of(
        "+", 1,
        "-", 1,
        "*", 2,
        "/", 2
    );

    // 遇到数字时,直接追加到结果
    @Override
    public void enterNUMBER(ExpAnalyserParser.NUMBERContext ctx) {
        postfixResult.append(ctx.getText()).append(" ");
    }

    // 处理加法/减法运算符
    @Override
    public void enterAddOp(ExpAnalyserParser.AddOpContext ctx) {
        processOperator(ctx.getText());
    }

    // 处理乘法/除法运算符
    @Override
    public void enterMulOp(ExpAnalyserParser.MulOpContext ctx) {
        processOperator(ctx.getText());
    }

    // 遇到左括号,直接压栈
    @Override
    public void enterPrimary(ExpAnalyserParser.PrimaryContext ctx) {
        if (ctx.LPAREN() != null) {
            operatorStack.push("(");
        }
    }

    // 遇到右括号,弹出运算符直到左括号
    @Override
    public void exitPrimary(ExpAnalyserParser.PrimaryContext ctx) {
        if (ctx.RPAREN() != null) {
            while (!operatorStack.isEmpty() && !"(".equals(operatorStack.peek())) {
                postfixResult.append(operatorStack.pop()).append(" ");
            }
            operatorStack.pop(); // 弹出左括号,不加入结果
        }
    }

    // 退出顶层表达式时,清空栈中剩余运算符
    @Override
    public void exitExpr(ExpAnalyserParser.ExprContext ctx) {
        while (!operatorStack.isEmpty()) {
            postfixResult.append(operatorStack.pop()).append(" ");
        }
    }

    // 通用运算符处理逻辑
    private void processOperator(String currentOp) {
        // 弹出所有优先级 >= 当前运算符的栈顶元素
        while (!operatorStack.isEmpty() && !"(".equals(operatorStack.peek())) {
            String topOp = operatorStack.peek();
            if (OP_PRIORITY.getOrDefault(topOp, 0) >= OP_PRIORITY.getOrDefault(currentOp, 0)) {
                postfixResult.append(operatorStack.pop()).append(" ");
            } else {
                break;
            }
        }
        // 当前运算符压栈
        operatorStack.push(currentOp);
    }

    // 将结果写入文件的方法
    public void writeToFile(String filePath) throws IOException {
        // 去掉末尾多余的空格,写入文件
        try (FileWriter writer = new FileWriter(filePath)) {
            writer.write(postfixResult.toString().trim());
        }
    }

    // 获取后缀表达式字符串(用于调试)
    public String getPostfix() {
        return postfixResult.toString().trim();
    }
}

4. 调用Listener完成转换

接下来写主程序,生成解析器并遍历语法树:

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

public class ExpConverterMain {
    public static void main(String[] args) throws IOException {
        // 你的中缀表达式输入
        String infixInput = "10 + 2 * (3 - 4) / 5";
        // 1. 创建输入流
        CharStream inputStream = CharStreams.fromString(infixInput);
        // 2. 词法分析器
        ExpAnalyserLexer lexer = new ExpAnalyserLexer(inputStream);
        // 3. 词法符号流
        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
        // 4. 语法分析器
        ExpAnalyserParser parser = new ExpAnalyserParser(tokenStream);
        // 5. 获取顶层语法树节点
        ParseTree parseTree = parser.expr();
        // 6. 创建自定义Listener
        PostfixConverterListener listener = new PostfixConverterListener();
        // 7. 遍历语法树,触发Listener的回调
        ParseTreeWalker.DEFAULT.walk(listener, parseTree);
        // 8. 输出到文本文件
        listener.writeToFile("postfix_output.txt");
        // 控制台打印验证结果
        System.out.println("转换后的后缀表达式:" + listener.getPostfix());
    }
}

关键注意事项

  • 匹配你的语法规则:如果你的ExpAnalyser.g4里的规则名不同(比如数字规则叫integer,运算符规则叫operator),一定要修改Listener中重写的方法名(比如enterInteger)
  • 优先级要一致:OP_PRIORITY的优先级必须和你语法中定义的运算符优先级对应,否则转换结果会出错
  • 特殊情况处理:如果需要支持负数、取模、幂运算等,要扩展优先级映射和Listener的逻辑
  • 资源关闭:写入文件时用try-with-resources自动关闭流,避免资源泄漏

内容的提问来源于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:18:24