本文介紹使用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();
}
}