Antlr - 使用antlr4实现一个计算器,配合变量可以实现程序里的复合指标运算
目录开发环境准备idea项目配置antlr配置文件代码编写测试zookeeper下载开发环境准备idea我使用idea开发,所以使用一idea作为环境参考,idea版本是打开preferences,选择plugins输入antlr,没有安装过,点击下面进入repositories点击install,等待一会安装完成重启idea,看此处插件的antlr版本是4....
·
开发环境准备
idea
-
我使用idea开发,所以使用一idea作为环境参考,idea版本是
-
打开preferences,选择plugins
-
输入antlr,没有安装过,点击下面进入repositories
-
点击install,等待一会
-
安装完成重启idea,看此处插件的antlr版本是4.7.2
项目配置
- pom文件,配置antlr版本和插件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4</artifactId>
<version>4.7.2</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.7.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.7.2</version>
<executions>
<execution>
<id>antlr</id>
<goals>
<goal>antlr4</goal>
</goals>
<phase>none</phase>
</execution>
</executions>
<configuration>
<outputDirectory>src/test/java</outputDirectory>
<listener>true</listener>
<treatWarningsAsErrors>true</treatWarningsAsErrors>
</configuration>
</plugin>
</plugins>
</build>
</project>
antlr配置文件
- 创建配置文件Calculate.g4
grammar Calculate;
prog : stat+;
# 定义语句
stat :expr NEWLINE #printExpr
|ID '=' expr NEWLINE #assign
|NEWLINE #blank
;
# 定义表达式
expr : expr op=('*'|'/') expr # MulDiv
| expr op=('+'|'-') expr # AddSub
| ID # id
| INT # int
| '(' expr ')' # parens
;
# 通过正则定义词法
ID : [a-zA-Z]+ ;
INT : [0-9]+('.'([0-9]+)?)?
| [0-9]+;
NEWLINE : '\r' ? '\n';
WS : [ \t]+ -> skip;
# 定义运算符
ADD : '+' ;
SUB : '-' ;
MUL : '*' ;
DIV : '/' ;
- 具体语法内容大家可以自行百度
- 配置antlr工具,选择项目代码位置和生成后的包名
- 在g4文件右键,生成java代码
- 生成完成
日志
2019-08-10 15:06:05: antlr4 -o /Users/ruiliu/IdeaProjects/test-java/src/main/youling/studio/antlr/gencode -package youling.studio.antlr.gencode -listener -visitor -lib /Users/ruiliu/IdeaProjects/test-java/src/main/youling/studio/antlr /Users/ruiliu/IdeaProjects/test-java/src/main/youling/studio/antlr/Calculate.g4
代码编写
- 编写CalculateBaseVisitor实现类,实现计算逻辑
package youling.studio.antlr;
import youling.studio.antlr.gencode.CalculateBaseVisitor;
import youling.studio.antlr.gencode.CalculateLexer;
import youling.studio.antlr.gencode.CalculateParser;
import java.util.HashMap;
import java.util.Map;
/**
* @author liurui
* @date 2019/8/10 下午4:22
*/
public class DoubleVisitor extends CalculateBaseVisitor<Double> {
Map<String,Double> map=new HashMap<String,Double>();
@Override
public Double visitParens(CalculateParser.ParensContext ctx) {
return super.visit(ctx.expr());
}
@Override
public Double visitBlank(CalculateParser.BlankContext ctx) {
return super.visitBlank(ctx);
}
@Override
public Double visitAddSub(CalculateParser.AddSubContext ctx) {
Double left=visit(ctx.expr(0)); //获取左边表达式最终值
Double right=visit(ctx.expr(1)); //获取右边表达式最终值
if(ctx.op.getType()== CalculateLexer.ADD) return left+right; //如果是加法
else return left-right; //如果是减法
}
@Override
public Double visitMulDiv(CalculateParser.MulDivContext ctx) {
Double left=visit(ctx.expr(0)); //获取左边表达式最终值
Double right=visit(ctx.expr(1)); //获取右边表达式最终值
if(ctx.op.getType()==CalculateLexer.DIV) return left/right; //如果是除法
else return left*right; //如果是乘法
}
@Override
public Double visitId(CalculateParser.IdContext ctx) {
String key=ctx.ID().getText();
if(map.containsKey(key)){ //如果变量被赋值
return map.get(key);
}
return 0d;
}
@Override
public Double visitInt(CalculateParser.IntContext ctx) {
return Double.parseDouble(ctx.INT().getText());
}
@Override
public Double visitPrintExpr(CalculateParser.PrintExprContext ctx) {
Double value=visit(ctx.expr());
System.out.println(value);
return 0d;
}
@Override
public Double visitAssign(CalculateParser.AssignContext ctx) {
String key=ctx.ID().getText();
Double value=visit(ctx.expr());
map.put(key, value);
return value; // 返回 value :a=b=6 则 a==6
}
}
- 编写入口类实现计算功能
package youling.studio.antlr;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import youling.studio.antlr.gencode.CalculateLexer;
import youling.studio.antlr.gencode.CalculateParser;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* @author liurui
* @date 2019/8/10 下午4:26
*/
public class AntlrTest {
public static void main(String[] args) throws Exception {
// 原子指标池
Map<String,Double> baseMeasure = new HashMap();
baseMeasure.put("sale_amt",12000d); //销售额
baseMeasure.put("order_cnt",120d); //订单量
// 单均销售额 = 销售额/订单量 此处定义符合指标计算逻辑
String avgOrderSaleAmtStr = "{sale_amt}/{order_cnt}\n";
for(String key : baseMeasure.keySet()){
avgOrderSaleAmtStr = avgOrderSaleAmtStr.replace("{"+key+"}",baseMeasure.get(key).toString());
}
CharStream input = CharStreams.fromStream(getStringStream(avgOrderSaleAmtStr));
CalculateLexer lexer = new CalculateLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CalculateParser parser = new CalculateParser(tokens);
// 生成语法树
ParseTree tree = parser.prog();
// 打印语法树
System.out.println(tree.toStringTree(parser));
// 计算符合指标结果
DoubleVisitor visitor = new DoubleVisitor();
Double avgOrderSaleAmt = visitor.visit(tree);
}
/**
* string 转流
* @param sInputString
* @return
*/
public static InputStream getStringStream(String sInputString){
if (sInputString != null && !sInputString.trim().equals("")){
try{
ByteArrayInputStream tInputStringStream = new ByteArrayInputStream(sInputString.getBytes());
return tInputStringStream;
}catch (Exception ex){
ex.printStackTrace();
}
}
return null;
}
}
测试
- 直接执行main方法得到如下结果
(prog (stat (expr (expr 12000.0) / (expr 120.0)) \n))
100.0
Process finished with exit code 0
- 至此大功告成
更多推荐
已为社区贡献7条内容
所有评论(0)