1.eclipse 或idea安装antlr4插件(以eclipse 为例 )
windows->Eclipse Marketplace->搜索antlr
搜索到antlr 4 ide 进行安装
2.插件安装完成后新建antlr4 项目
file ->new ->other->antlr4项目
填写项目名->完成
3.将项目转化为maven项目
右键项目-->configure->转化为maven项目
方便管理
加入如下依赖
<dependencies>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.7.2</version>
</dependency>
</dependencies>
注意maven里的依赖必须和eclipse里配置的antrl jar版本一致
如果不一致请按如下下载对应的jar配置到eclispe里
如果版本不匹配需要官网下载和mavne匹配的版本 我这里下载的是antlr-4.7.2-complete.jar
https://www.antlr.org/download.html 进入主页
找到ANTLR tool and Java Target标签
下载第一个即可.
eclipse配置最新的jar
windows->preferences-->antlr4->tool 配置最新的jar
4.四则运算编写解释
内容如下:
grammar Math;
@header{package com.zetyun.aiops.core.math;} #生成包的路径
prog : stat+;# 定义一个语法prog 他由一个或多个语法stat组成
stat: expr NEWLINE # printExpr #语法stat 可以是expr语法 (注意“#printExpr" 可以标志生成对应的监听器或者访问器的方法 )
| ID '=' expr NEWLINE # assign #也可以是赋值语句
| NEWLINE # blank #也可空行
;
expr: expr op=('*'|'/') expr # MulDiv #语法expr可以如下表示
| expr op=('+'|'-') expr # AddSub
| INT # int
| ID # id
| '(' expr ')' # parens
;
MUL : '*' ; // assigns token name to '*' used above in grammar #词法
DIV : '/' ;
ADD : '+' ;
SUB : '-' ;
ID : [a-zA-Z]+ ;
INT : [0-9]+ ;
NEWLINE:'\r'? '\n' ;
WS : [ \t]+ -> skip;
1.语法说明:
一般我们解析语言分为,词法解析和语法解析
词法解析根据用户输入生成一系列token序列(即一个关键字,操作数,操作方法等的列表)
语法解析根据token生成语法树(这里就是根据编写的g4 文件生成对应的树状结构及树状结构的值)
2.g4 语法说明:
antrl4 分为词法规则和文法规则
文法(语法)规则一般对应非叶子节点,
词法一般对应叶子节点
词法规则一般以大写字母组成
文法规则有小写字母组成
为了避免歧义antrl定义了一套匹配规则
文法规则优先词法规则,
字符串常量属于词法规则
字符串常量优先其他的词法规则
相同优先级采用自上而下匹配即按申明顺序优先匹配
3.最左匹配原则
如果如下G4语法
expr: expr op=('*'|'/') expr # MulDiv
| expr op=('+'|'-') expr # AddSub
| INT # int
| '(' expr ')' # parens
;
INT : [0-9]+ ;
则1+2*3可以解析为 如下两种
expr(1)+exp(2*3)
和exp(1+2)*exp(3)
antrl 是怎样解决如上歧义的呢
是根据申明匹配位置(即优先匹配最左)
expr: expr op=('*'|'/') expr # MulDiv
| expr op=('+'|'-') expr # AddSub
| INT # int
| ID # id
| '(' expr ')' # parens
;
我们看到 expr op=('*'|'/') expr 是在expr op=('+'|'-') expr 的前面(即左边)
所以语法解析为expr(1)+exp(2*3)
如果换成
expr op=('+'|'-') expr # AddSub
|expr: expr op=('*'|'/') expr # MulDiv
| INT # int
| ID # id
| '(' expr ')' # parens
则·会·匹配exp(1+2)*exp(3)
5.通过visit模式实战四则运算
右键Math.g4
Run as >external Tools configurations-> 将tool 的标签下的arguments 改为 -listener -visitor -encoding UTF-8 生成访问者模式的基类
然后 Run as >generate ANTLR recognizer
生成MathVisitor 接口和MathBaseVisitor 的默认实现
我们继承MathBaseVisitor 实现打印四则运算中的表达式
package com.zetyun.aiops.core.math;
import java.util.HashMap;
import java.util.Map;
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 com.zetyun.aiops.core.math.MathParser.AddSubContext;
import com.zetyun.aiops.core.math.MathParser.AssignContext;
import com.zetyun.aiops.core.math.MathParser.BlankContext;
import com.zetyun.aiops.core.math.MathParser.IdContext;
import com.zetyun.aiops.core.math.MathParser.IntContext;
import com.zetyun.aiops.core.math.MathParser.MulDivContext;
import com.zetyun.aiops.core.math.MathParser.ParensContext;
import com.zetyun.aiops.core.math.MathParser.PrintExprContext;
import com.zetyun.aiops.core.math.MathParser.ProgContext;
public class MathVis extends MathBaseVisitor<Integer>
{
private static Map<String,Integer> assignMap = new HashMap<>();
@Override
public Integer visitProg(ProgContext ctx)
{
// TODO Auto-generated method stub
return super.visitProg(ctx);
}
@Override
public Integer visitPrintExpr(PrintExprContext ctx)
{
Integer value=visit(ctx.expr());
System.out.println(value);
return 0;
}
@Override
public Integer visitAssign(AssignContext ctx)
{
String id = ctx.ID().getText();
Integer value=visit(ctx.expr());
assignMap.put(id, value);
// TODO Auto-generated method stub
return 0;
}
@Override
public Integer visitBlank(BlankContext ctx)
{
return 0;
}
@Override
public Integer visitParens(ParensContext ctx)
{
// TODO Auto-generated method stub
return visit(ctx.expr());
}
@Override
public Integer visitMulDiv(MulDivContext ctx)
{
if(ctx.op.getType()==MathParser.MUL)
{
return visit(ctx.expr(0))*visit(ctx.expr(1));
}
else
{
return visit(ctx.expr(0))/visit(ctx.expr(1));
}
}
@Override
public Integer visitAddSub(AddSubContext ctx)
{
if(ctx.op.getType()==MathParser.ADD)
{
return visit(ctx.expr(0))+visit(ctx.expr(1));
}
else
{
return visit(ctx.expr(0))-visit(ctx.expr(1));
}
}
@Override
public Integer visitId(IdContext ctx)
{
String id = ctx.ID().getText();
if(assignMap.containsKey(id))
{
return assignMap.get(id);
}
return 0;
}
@Override
public Integer visitInt(IntContext ctx)
{
return Integer.valueOf(ctx.INT().getText().trim());
}
public static void main(String[] args)
{
CharStream input =CharStreams.fromString("a=3 \n b=4 \n a*b \n 4*5 \n");
MathLexer lexer = new MathLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
MathParser parser = new MathParser(tokens);
//parser.resginsetList();
ParseTree tree = parser.prog();
MathVis mathVis=new MathVis();
mathVis.visit(tree);
}
}
运行main 函数打印出运算结果 12 和20