如何用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
相关产品推荐
相关产品推荐

