概述
通过简单示例,说明antlr4的用法,目标需要达到如下效果:
1+2 => 1+2=3
1+2*4 => 1+2*4=9
1+2*4-5 => 1+2*4-5=4
1+2*4-5+20/5 => 1+2*4-5+20/5=8
(1+2)*4 => (1+2)*4=12
处理流程如下
通用词法规则文件,CommonLexerRules.g4
// 定义词法规则
lexer grammar CommonLexerRules;
//////// 定义此法
// 匹配ID
ID : [a-zA-Z]+ ;
// 匹配INT
INT : [0-9]+ ;
// 匹配换行符
NEWLINE: '\n'('\r'?);
// 跳过空格、跳格、换行符
WS : [ \t\n\r]+ -> skip;
//////// 运算符
DIV:'/';
MUL:'*';
ADD:'+';
SUB:'-';
EQU:'=';
语法文件,LibExpr.g4
// 定于语法规则
grammar LibExpr;
// 导入词法规则
import CommonLexerRules;
// 词法根
prog:stat+ EOF?;
// 定义声明
stat:expr (NEWLINE)? # printExpr
| ID '=' expr (NEWLINE)? # assign
| NEWLINE # blank
;
// 定义表达式
expr:expr op=('*'|'/') expr # MulDiv
|expr op=('+'|'-') expr # AddSub
|'(' expr ')' # Parens
|ID # Id
|INT # Int
;
逻辑实现类,LibExprVisitorImpl.java
// 导入Map包
import java.util.Map;
import java.util.HashMap;
/**
* 重写访问器规则,实现数据计算功能
* 目标:
* 1+2 => 1+2=3
* 1+2*4 => 1+2*4=9
* 1+2*4-5 => 1+2*4-5=4
* 1+2*4-5+20/5 => 1+2*4-5+20/5=8
* (1+2)*4 => (1+2)*4=12
*/
public class LibExprVisitorImpl extends LibExprBaseVisitor<Integer> {
// 定义数据
Map<String,Integer> data = new HashMap<String,Integer>();
// expr (NEWLINE)? # printExpr
@Override
public Integer visitPrintExpr(LibExprParser.PrintExprContext ctx) {
System.out.println(ctx.expr().getText()+"="+visit(ctx.expr()));
return visit(ctx.expr());
}
// ID '=' expr (NEWLINE)? # assign
@Override
public Integer visitAssign(LibExprParser.AssignContext ctx) {
// 获取id
String id = ctx.ID().getText();
// // 获取value
int value = Integer.valueOf(visit(ctx.expr()));
// 缓存ID数据
data.put(id,value);
// 打印日志
System.out.println(id+"="+value);
return value;
}
// NEWLINE # blank
@Override
public Integer visitBlank(LibExprParser.BlankContext ctx) {
return 0;
}
// expr op=('*'|'/') expr # MulDiv
@Override
public Integer visitMulDiv(LibExprParser.MulDivContext ctx) {
// 左侧数字
int left = Integer.valueOf(visit(ctx.expr(0)));
// 右侧数字
int right = Integer.valueOf(visit(ctx.expr(1)));
// 操作符号
int opType = ctx.op.getType();
// 调试
// System.out.println("visitMulDiv>>>>> left:"+left+",opType:"+opType+",right:"+right);
// 判断是否为乘法
if(LibExprParser.MUL==opType){
return left*right;
}
// 判断是否为除法
return left/right;
}
// expr op=('+'|'-') expr # AddSub
@Override
public Integer visitAddSub(LibExprParser.AddSubContext ctx) {
// 获取值和符号
// 左侧数字
int left = Integer.valueOf(visit(ctx.expr(0)));
// 右侧数字
int right = Integer.valueOf(visit(ctx.expr(1)));
// 操作符号
int opType = ctx.op.getType();
// 调试
// System.out.println("visitAddSub>>>>> left:"+left+",opType:"+opType+",right:"+right);
// 判断是否为加法
if(LibExprParser.ADD==opType){
return left+right;
}
// 判断是否为减法
return left-right;
}
// '(' expr ')' # Parens
@Override
public Integer visitParens(LibExprParser.ParensContext ctx) {
// 递归下调
return visit(ctx.expr());
}
// ID # Id
@Override
public Integer visitId(LibExprParser.IdContext ctx) {
// 获取id
String id = ctx.ID().getText();
// 判断ID是否被定义
if(data.containsKey(id)){
// System.out.println("visitId>>>>> id:"+id+",value:"+data.get(id));
return data.get(id);
}
return 0;
}
// INT # Int
@Override
public Integer visitInt(LibExprParser.IntContext ctx) {
// System.out.println("visitInt>>>>> int:"+ctx.INT().getText());
return Integer.valueOf(ctx.INT().getText());
}
}
语法集成类,LibExprPrint.java
用于获取输入流和集成访问器
//导入antlr runtime包
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
//导入ioException包
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 打印语法树
*/
public class LibExprPrint{
// 打印语法树 input -> lexer -> tokens -> parser -> tree -> print
public static void main(String args[]){
// 判断是否传入文件名
if(args.length>0){
printTree(args[0]);
}else{
printTree(null);
}
}
/**
* 打印语法树 input -> lexer -> token -> parser -> tree
* @param fileName
*/
private static void printTree(String fileName){
// 定义输入流
ANTLRInputStream input = null;
// 判断文件名是否为空,若不为空,则读取文件内容,若为空,则读取输入流
if(fileName!=null){
try{
input = new ANTLRFileStream(fileName);
}catch(FileNotFoundException fnfe){
System.out.println("文件不存在,请检查后重试!");
}catch(IOException ioe){
System.out.println("文件读取异常,请检查后重试!");
}
}else{
try{
input = new ANTLRInputStream(System.in);
}catch(FileNotFoundException fnfe){
System.out.println("文件不存在,请检查后重试!");
}catch(IOException ioe){
System.out.println("文件读取异常,请检查后重试!");
}
}
// 定义词法规则分析器
LibExprLexer lexer = new LibExprLexer(input);
// 生成通用字符流
CommonTokenStream tokens = new CommonTokenStream(lexer);
// 语法解析
LibExprParser parser = new LibExprParser(tokens);
// 生成语法树
ParseTree tree = parser.prog();
// 打印语法树
// System.out.println(tree.toStringTree(parser));
// 生命访问器
LibExprVisitorImpl visitor = new LibExprVisitorImpl();
visitor.visit(tree);
}
}
测试脚本,testCase.txt
验证输出效果
1+2
1+2*4
1+2*4-5
1+2*4-5+20/5
(1+2)*4
执行命令,测试效果
# 下载jar包
cd /usr/local/lib
curl -O https://www.antlr.org/download/antlr-4.8-complete.jar
# 添加环境变量
export CLASSPATH=".:/usr/local/lib/antlr-4.8-complete.jar:$CLASSPATH"
# 设置命令别名
alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.8-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
# 生成解析器,指定访问者模式
antlr4 -no-listener -visitor LibExpr.g4
# 编译java文件
javac *.java
# 执行测试
java LibExprPrint testCase.txt
测试结果:
$ antlr_libexpr % java LibExprPrint testCase.txt
1+2=3
1+2*4=9
1+2*4-5=4
1+2*4-5+20/5=8
(1+2)*4=12