Antlr4入门(三)-制作一个科学计算器


在上一篇博客实现了简易计算器之后,这次增加一点难度,要实现支持 2e102的10次方这种科学计数法的计算器。

项目准备

已经阅读过上一篇的可以跳过这部分

  1. 创建Maven项目
  2. 引入依赖
<!-- https://mvnrepository.com/artifact/org.antlr/antlr4-runtime -->
<dependency>
	<groupId>org.antlr</groupId>
	<artifactId>antlr4-runtime</artifactId>
	<version>4.7</version>
</dependency>
  1. 安装antlr插件并重启idea,我这里已经安装过了
    在这里插入图片描述

生成Java

  1. 编写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+ ;
  1. 配置ANTLR:右键单击文件->Configure ANTLR
    设置路径、包名,注意勾选生成visitor
    在这里插入图片描述

  2. 生成Java代码:右键单击文件->Generate ANTLR
    在这里插入图片描述

生成的代码中,我们主要关心的是CalculatorVisitor,但它是一个接口,CalculatorBaseVisitor是它的简单实现。

编写测试

  1. 继承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());
    }

}


  1. 测试自己的代码
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()));
    }
}

最终得到的结果
在这里插入图片描述

功能扩展

科学计算中乘幂、对数这些都是常用的计算方式。要扩展起来也不麻烦,这里以乘幂为例

  1. 新增| expr '^' expr # power规则在parenExpr之后
  2. 重新生成Java代码
  3. 重写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);
    }
  1. 测试
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()));
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_44112790/article/details/110678425
今日推荐