本文介绍使用Antlr4实现一个支持四则运算的程序,关于Antlr4的安装可以参考上一篇文章。
工具:Intellij IDEA + antlr4 plugin
1. 编辑antlr4的语法文件
grammar LabelExpr;
/** 起始规则 语法分析器起点 */
prog: stat+ ;
stat: expr NEWLINE # printExpr
| ID '=' expr NEWLINE # assign
| NEWLINE # blank
;
expr: expr op=('*'|'/') expr # MulDiv
| expr op=('+'|'-') expr # AddSub
| INT # int
| ID # id
| '(' expr ')' # parens
;
ID : [a-zA-Z]+ ; // 匹配标识符
INT : [0-9]+ ; // 匹配整数
NEWLINE : '\r'? '\n' ; // 新行 即语句终止标志
WS : [ \t]+ -> skip ; // 丢弃空白字符
MUL : '*' ;
DIV : '/' ;
Add : '+' ;
SUB : '-' ;
在IDEA中可以直接使用ANTLR Preview窗口测试语法文件是否正确
然后生成Java的解析代码,继承其中的额LabelExprBaseVisitor类,实现处理逻辑
package labelExpr;
import java.util.HashMap;
import java.util.Map;
public class MyEvalVisitor extends LabelExprBaseVisitor<Integer> {
/**
* 保存运行中的结果
*/
Map<String, Integer> memory = new HashMap<>();
@Override
public Integer visitAssign(LabelExprParser.AssignContext ctx) {
String id = ctx.ID().getText();
int value = visit(ctx.expr());
memory.put(id, value);
return value;
}
@Override
public Integer visitPrintExpr(LabelExprParser.PrintExprContext ctx) {
Integer value = visit(ctx.expr());
System.out.println(value);
return 0;
}
@Override
public Integer visitInt(LabelExprParser.IntContext ctx) {
return Integer.valueOf(ctx.INT().getText());
}
@Override
public Integer visitId(LabelExprParser.IdContext ctx) {
String id = ctx.ID().getText();
if (memory.containsKey(id)) {
return memory.get(id);
}
return 0;
}
@Override
public Integer visitMulDiv(LabelExprParser.MulDivContext ctx) {
int left = visit(ctx.expr(0));
int right = visit(ctx.expr(1));
if (ctx.op.getType() == LabelExprParser.MUL) {
return left * right;
} else {
return left / right;
}
}
@Override
public Integer visitAddSub(LabelExprParser.AddSubContext ctx) {
int left = visit(ctx.expr(0));
int right = visit(ctx.expr(1));
if (ctx.op.getType() == LabelExprParser.Add) {
return left + right;
} else {
return left - right;
}
}
@Override
public Integer visitParens(LabelExprParser.ParensContext ctx) {
return visit(ctx.expr());
}
}
测试内容如下:
193
a = 5
b = 6
a + b * 3
1 + 2 * 3
下面是测试代码和结果
package labelExpr;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import java.io.IOException;
import java.io.InputStream;
public class Main {
public static void main(String[] args) throws IOException {
InputStream is = Main.class.getClassLoader().getResourceAsStream("expr1.txt");
LabelExprLexer lexer = new LabelExprLexer(CharStreams.fromStream(is));
CommonTokenStream tokens = new CommonTokenStream(lexer);
LabelExprParser parser = new LabelExprParser(tokens);
ParseTree tree = parser.prog();
MyEvalVisitor visitor = new MyEvalVisitor();
visitor.visit(tree);
is.close();
}
}