開發環境準備
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
- 至此大功告成