在上一篇博客实现了简易计算器之后,这次增加一点难度,要实现支持
2e10
2的10次方这种科学计数法的计算器。
项目准备
已经阅读过上一篇的可以跳过这部分
- 创建Maven项目
- 引入依赖
<!-- https://mvnrepository.com/artifact/org.antlr/antlr4-runtime -->
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.7</version>
</dependency>
- 安装antlr插件并重启idea,我这里已经安装过了
生成Java
- 编写
Calculator.g4
文法,比起上一篇这里的规则更加简单,因为引入了词法fragment。
grammar Calculator;
line : expr EOF ;
expr : '(' expr ')' # parenExpr
| expr '^' expr # power
| expr ('*'|'/') expr # multOrDiv
| expr ('+'|'-') expr # addOrSubtract
| DOUBLE # double ;
WS : [ \t\n\r]+ -> skip ;
DOUBLE : DIGIT + '.' DIGIT* EXPONENT?
| '.' DIGIT+ EXPONENT?
| DIGIT+ EXPONENT? ;
fragment DIGIT : '0'..'9';
fragment EXPONENT : ('e'|'E') ('+'|'-')? DIGIT+ ;
-
配置ANTLR:右键单击文件->Configure ANTLR
设置路径、包名,注意勾选生成visitor
-
生成Java代码:右键单击文件->Generate ANTLR
生成的代码中,我们主要关心的是CalculatorVisitor
,但它是一个接口,CalculatorBaseVisitor
是它的简单实现。
编写测试
- 继承
CalBaseVisitor
,我们需要完善计算器的功能,为每个规则实现一个方法。
Calculator.g4中需要我们实现的有这四条规则:parenExpr、multOrDiv、addOrSubtract、float
import output.CalculatorBaseVisitor;
import output.CalculatorParser;
public class MyCaculatorVisitor extends CalculatorBaseVisitor<Double> {
@Override
public Double visitAddOrSubtract(CalculatorParser.AddOrSubtractContext ctx) {
Double num1 = ctx.expr(0).accept(this);
Double num2 = ctx.expr(1).accept(this);
if("+".equals(ctx.getChild(1).getText())) return num1 + num2;
else if("-".equals(ctx.getChild(1).getText())) return num1 - num2;
return 0.0;
}
@Override
public Double visitMultOrDiv(CalculatorParser.MultOrDivContext ctx) {
Double num1 = ctx.expr(0).accept(this);
Double num2 = ctx.expr(1).accept(this);
if("*".equals(ctx.getChild(1).getText())) return num1 * num2;
else if("/".equals(ctx.getChild(1).getText())) {
if(num2==0.0) throw new ArithmeticException("除数不能为0");
return num1 / num2;
}
return 0.0;
}
@Override
public Double visitParenExpr(CalculatorParser.ParenExprContext ctx) {
return ctx.expr().accept(this);
}
@Override
public Double visitDouble(CalculatorParser.DoubleContext ctx) {
return Double.parseDouble(ctx.getText());
}
}
- 测试自己的代码
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import output.*;
public class Driver {
public static void main(String[] args) {
String query = ".125*(1.2e1-4.0)";
CalculatorLexer lexer = new CalculatorLexer(new ANTLRInputStream(query));
CalculatorParser parser = new CalculatorParser(new CommonTokenStream(lexer));
CalculatorVisitor visitor = new MyCaculatorVisitor();
System.out.println(visitor.visit(parser.expr()));
}
}
最终得到的结果
功能扩展
科学计算中乘幂、对数这些都是常用的计算方式。要扩展起来也不麻烦,这里以乘幂为例
- 新增
| expr '^' expr # power
规则在parenExpr之后 - 重新生成Java代码
- 重写
visitPower
方法
@Override
public Double visitPower(CalculatorParser.PowerContext ctx) {
Double num1 = ctx.expr(0).accept(this);
Double num2 = ctx.expr(1).accept(this);
return Math.pow(num1, num2);
}
- 测试
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import output.*;
public class Driver {
public static void main(String[] args) {
String query = ".125*(1.2e1-2.0^2)";
CalculatorLexer lexer = new CalculatorLexer(new ANTLRInputStream(query));
CalculatorParser parser = new CalculatorParser(new CommonTokenStream(lexer));
CalculatorVisitor visitor = new MyCaculatorVisitor();
System.out.println(visitor.visit(parser.expr()));
}
}