参考连接
Antlr4简明使用教程-云社区-华为云 (huaweicloud.com)
入门 · ANTLR 4简明教程 (gitbooks.io)
下载工具包
antlr-4.7.1-complete.jar
编辑Hello.g4文件, 同时更具idea提示安装下插件(重启激活)
grammar Hello; // 定义文法的名字, 必须和文件名称一样
s: 'Hello' ID; // 匹配关键字Hello和关键字ID
ID: [a-zA-Z]+; // 定义关键字ID是字母
WS: [ \t\r\n\u000c]+ -> skip; // 跳过空格, 制表符, 回车符, 换行符, 换页符
java -cp antlr-4.7.1-complete.jar org.antlr.v4.Tool Hello.g4
会生成后准为tokens,interp和java的8个文件
javac -cp .;antlr-4.7.1-complete.jar Hello*.java
java -cp .;antlr-4.7.1-complete.jar org.antlr.v4.gui.TestRig Hello s -tokens
Hello World # 输入并回车
EOF # Linux系统输入Ctrl+d, windows系统输入ctrl+z, 并回车
实例:
hello-world>java -cp .;antlr-4.7.1-complete.jar org.antlr.v4.gui.TestRig Hello s -tokens
Hello World
^Z
[@0,0:4='Hello',<'Hello'>,1:0]
[@1,6:10='World',,1:6]
[@2,13:12='',,2:0]
以LISP风格的文本信息查看记号
java -cp .;antlr-4.7.1-complete.jar org.antlr.v4.gui.TestRig Hello s -tree
示例:
hello-world>java -cp .;antlr-4.7.1-complete.jar org.antlr.v4.gui.TestRig Hello s -tree
Hello World
^Z
(s Hello World)
最直观的就是以可视化的方式查看语法分析树
java -cp .;antlr-4.7.1-complete.jar org.antlr.v4.gui.TestRig Hello s -gui
实例:
hello-world>java -cp .;antlr-4.7.1-complete.jar org.antlr.v4.gui.TestRig Hello s -gui
Hello World
^Z
以下是TestRig可用的所有参数:
src\main\resources\calculator目录下创建
grammar Calculator;
@header {
package len.hgy.antlr4.calculator;
}
// stmt是语法树的根
stmt: expr;
expr: '(' expr ')' # Bracket // 表达式可以被 括号包裹
| expr MUL expr # Mul // 表达式可以是表达式和表达式运算
| expr DIV expr # Div
| expr ADD expr # Add
| expr MIN expr # Min
| INT # Int
;
INT: DIGIT+; // 定义一个数字类型, 提高可读性和简化定义
DIGIT: [0-9];
MUL: '*';
DIV: '/';
ADD: '+';
MIN: '-';
WS: [ \t\r\n\u000c]+ -> skip; // 跳过的字符其中 \u000c是换页符
SHEBANG: '#' '!' ~('\n'|'\r')* -> channel(HIDDEN); // 不需要处理的放入隐藏通道
依赖
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.9.3</version>
</dependency>
插件
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.9.3</version>
<executions>
<execution>
<id>antlr</id>
<goals>
<goal>antlr4</goal>
</goals>
<phase>generate-resources</phase>
</execution>
</executions>
<configuration>
<sourceDirectory>${basedir}/src/main/resources/calculator</sourceDirectory>
<!-- 注意这里和phase的那个不太一样, 这里有一个d结尾-->
<outputDirectory>${project.build.directory}/generated-resources/calculator/len/hgy/antlr4/calculator</outputDirectory>
<listener>true</listener>
<visitor>true</visitor>
<treatWarningsAsErrors>true</treatWarningsAsErrors>
</configuration>
</plugin>
// 重写观察方法
public class MyCalculatorBaseVisitor extends CalculatorBaseVisitor {
@Override
public Object visitDiv(CalculatorParser.DivContext ctx) {
List<Double> lrValues = getLrValues(ctx.expr(0), ctx.expr(1));
return lrValues.get(0) / lrValues.get(1);
}
@Override
public Object visitAdd(CalculatorParser.AddContext ctx) {
List<Double> lrValues = getLrValues(ctx.expr(0), ctx.expr(1));
return lrValues.get(0) + lrValues.get(1);
}
@Override
public Object visitBracket(CalculatorParser.BracketContext ctx) {
// 去掉括号, 取中间的一个孩子
return this.visit(ctx.getChild(1));
}
@Override
public Object visitMin(CalculatorParser.MinContext ctx) {
List<Double> lrValues = getLrValues(ctx.expr(0), ctx.expr(1));
return lrValues.get(0) - lrValues.get(1);
}
@Override
public Object visitMul(CalculatorParser.MulContext ctx) {
List<Double> lrValues = getLrValues(ctx.expr(0), ctx.expr(1));
return lrValues.get(0) * lrValues.get(1);
}
@Override
public Object visitInt(CalculatorParser.IntContext ctx) {
return Double.parseDouble(ctx.getText());
}
public List<Double> getLrValues(CalculatorParser.ExprContext left, CalculatorParser.ExprContext right) {
List<Double> lrValues = new ArrayList<>();
double leftValue = getValue(left);
double rightValue = getValue(right);
lrValues.add(leftValue);
lrValues.add(rightValue);
return lrValues;
}
private double getValue(CalculatorParser.ExprContext context) {
if (context.getChildCount() > 1) {
return Double.parseDouble(this.visit(context).toString());
} else {
return Double.parseDouble(context.getText());
}
}
}
class MyCalculatorBaseVisitorTest {
@Test
public void testVisit() {
CharStream input = CharStreams.fromString("12 * (2 + 12) / 7");
CalculatorLexer lexer = new CalculatorLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CalculatorParser parser = new CalculatorParser(tokens);
CalculatorParser.ExprContext tree = parser.expr();
MyCalculatorBaseVisitor myCalculatorBaseVisitor = new MyCalculatorBaseVisitor();
System.out.println(myCalculatorBaseVisitor.visit(tree));
}
}
用例输出
24.0