前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >仅200行代码实现科学计算器,Antlr太强大了

仅200行代码实现科学计算器,Antlr太强大了

作者头像
麒思妙想
发布2021-11-30 13:36:48
8360
发布2021-11-30 13:36:48
举报
文章被收录于专栏:麒思妙想麒思妙想

由于最近参加的Talent Plan,需要自己实现一个基于Raft的KV引擎,所以之前说的分布式事务的内容,还要再鸽一段时间,所以为了补偿大家,我们来学学antlr吧,这次我们不在外部维护变量表,而是通过设置一个特殊的变量类型,由其自身来维护一个静态变量表,从而大大简化了程序逻辑,仅仅通过200行代码,就实现了一个mini版的科学计算器。Let's GO!!!

定义语法

定义语法文件,首先通过 prog: stat+ ;允许多条语句, 然后定义规则,支持打印表达式expr NEWLINE,表达式赋值expr NEWLINE, 清理内存表CLEANMEM,以及换行。前面提到,我们使用了一张静态表存放数据,所以,这里设计了一个清理内存表的命令,用于将变量表清空。

接下来定义表达式 expr 这里我们支阶乘、乘方以及普通四则运算。

语法文件如下:

代码语言:javascript
复制
grammar CalExpr;

prog:   stat+ ;

stat:   expr NEWLINE                # printExpr
    |   ID '=' expr NEWLINE         # assign
    |   CLEANMEM                    # cleanmem
    |   NEWLINE                     # blank
    ;

expr:   expr '!'                    # fac
    |   expr '^' expr               # pow
    |   expr op=('*'|'/') expr      # MulDiv
    |   expr op=('+'|'-') expr      # AddSub
    |   NUMBER                      # number
    |   ID                          # id
    |   '(' expr ')'                # parens
    ;

CLEANMEM: 'CLEANMEM';
FAC :   '!' ;
POW :   '^' ;
MUL :   '*' ;
DIV :   '/' ;
ADD :   '+' ;
SUB :   '-' ;
DOT :   '.' ;
ID  :   [a-zA-Z]+ ;
NUMBER :   [0-9]+ (DOT)? [0-9]*;
NEWLINE:'\r'? '\n' ;
WS  :   [ \t]+ -> skip ;

定义数据类型

根据我们的文法定义,我们将数据分为两类,数值和变量。由变量自己维护一个静态变量表,来维护值,从而简化处理流程。

我们定义整个数据类型的接口

代码语言:javascript
复制
package wang.datahub.datatype;

public interface IType {
    /**
     * 获取当前对象的值
     * */
    Double getValue();

    /**
     * 设置当前值
     * */
    void setValue(Double d);


    /**
     * 判断是否为值类型
     * */
    Boolean isValue();


    /**
     * 是否是变量
     * */
    Boolean isVarb();
}

定义值类型

代码语言:javascript
复制
package wang.datahub.datatype;

public class CalNumber implements IType{
    private Double _value;

    public CalNumber(String str){
        _value = Double.valueOf(str);
    }
    public void setValue(Double d){
        _value = d;
    }



    @Override
    public Double getValue() {
        return _value;
    }


    @Override
    public Boolean isValue() {
        return true;
    }

    @Override
    public Boolean isVarb() {
        return false;
    }

    @Override
    public String toString() {
        return "CalNumber{" +
                "_value=" + _value +
                '}';
    }
}

定义变量类型

代码语言:javascript
复制
package wang.datahub.datatype;

import java.util.Hashtable;
import java.util.Map;

public class CalVarb implements IType{
    private String _name;
    private static Map<String, CalNumber> _map  = new Hashtable<>();

    public CalVarb(String str){
        _name = str;

    }

    public static void cleanmem(){
//        System.out.println("clean");
        _map.clear();
        _map = new Hashtable<>();
    }

    @Override
    public Double getValue() {
//        System.out.println(_map);
        return _map.get(_name).getValue();
    }

    @Override
    public void setValue(Double d) {
        _map.put(_name,new CalNumber(d.toString()));
    }

    @Override
    public Boolean isValue() {
        return false;
    }

    @Override
    public Boolean isVarb() {
        return true;
    }

    @Override
    public String toString() {
        return "CalVarb{" +
                "_name='" + _name + '\'' +
                '}';
    }
}


遍历AST

现在有了语法文件,有了数据类型,我们只需要再完成vistor的编写,就搞定了。那么如何来撰写vistor呢?个人建议按照语法文件的格式,从下网上实现,

本文就先完成id,number的获取,再完成赋值和打印方法,就可以通过简单测试了。

代码语言:javascript
复制
    @Override
    public T visitId(CalExprParser.IdContext ctx) {
        CalVarb calVarb = new CalVarb(ctx.ID().getText());
        return (T)calVarb;
    }

    @Override
    public T visitNumber(CalExprParser.NumberContext ctx) {
        CalNumber number = new CalNumber(ctx.getText());
        return (T) number;
    }

这里,我们就是完成变量和值类型的初始化。

代码语言:javascript
复制
    @Override
    public T visitPrintExpr(CalExprParser.PrintExprContext ctx) {
        IType iType = ctx.expr().accept(this);
        if(iType.isValue()){
            System.out.println(iType.getValue());
        }else  if(iType.isPointer()){
            System.out.println(ctx.getText().trim()+" = "+iType.getValue());
        }
        return (T)iType;
    }

    @Override
    public T visitAssign(CalExprParser.AssignContext ctx) {
        CalVarb calVarb = new CalVarb(ctx.ID().getText());
        IType iType = ctx.expr().accept(this);
        calVarb.setValue(iType.getValue());
        return (T)calVarb;
    }

这里我们就完成了赋值和打印变量。

代码语言:javascript
复制
 @Override
    public T visitMulDiv(CalExprParser.MulDivContext ctx) {
        CalExprParser.ExprContext left = ctx.expr().get(0);
        CalExprParser.ExprContext right = ctx.expr().get(1);
        IType leftItype = left.accept(this);
        IType rightItype = right.accept(this);
        Double temp = 0d;
        if(ctx.MUL()!=null){
            temp = leftItype.getValue()*rightItype.getValue();
        }
        if(ctx.DIV()!=null){
            temp = leftItype.getValue()/rightItype.getValue();
        }

        return (T)new CalNumber(temp.toString());
    }

获取左右表达式的值,并且进行运算。

完整vistor代码如下:

代码语言:javascript
复制
package wang.datahub;

import org.apache.commons.math3.util.ArithmeticUtils;
import wang.datahub.datatype.CalNumber;
import wang.datahub.datatype.CalVarb;
import wang.datahub.datatype.IType;


public class MyCalVistor<T extends IType> extends CalExprBaseVisitor<T>{

    @Override
    public T visitCleanmem(CalExprParser.CleanmemContext ctx) {
        CalVarb.cleanmem();
        return super.visitCleanmem(ctx);
    }

    @Override
    public T visitPrintExpr(CalExprParser.PrintExprContext ctx) {
        IType iType = ctx.expr().accept(this);
        if(iType.isValue()){
            System.out.println(iType.getValue());
        }else  if(iType.isPointer()){
            System.out.println(ctx.getText().trim()+" = "+iType.getValue());
        }
        return (T)iType;
    }

    @Override
    public T visitAssign(CalExprParser.AssignContext ctx) {
        CalVarb calVarb = new CalVarb(ctx.ID().getText());
        IType iType = ctx.expr().accept(this);
        calVarb.setValue(iType.getValue());
        return (T)calVarb;
    }

    @Override
    public T visitFac(CalExprParser.FacContext ctx) {
        IType iType = ctx.expr().accept(this);
        Double l = ArithmeticUtils.factorialDouble(iType.getValue().intValue());
        return (T) new CalNumber(l.toString());
    }

    @Override
    public T visitPow(CalExprParser.PowContext ctx) {
        CalExprParser.ExprContext left = ctx.expr().get(0);
        CalExprParser.ExprContext right = ctx.expr().get(1);
        IType leftItype = left.accept(this);
        IType rightItype = right.accept(this);

        Integer l = ArithmeticUtils.pow(leftItype.getValue().intValue(),rightItype.getValue().intValue());
        return (T) new CalNumber(l.toString());
    }

    @Override
    public T visitParens(CalExprParser.ParensContext ctx) {
        IType iType = ctx.expr().accept(this);
        return (T)iType;
    }

    @Override
    public T visitMulDiv(CalExprParser.MulDivContext ctx) {
        CalExprParser.ExprContext left = ctx.expr().get(0);
        CalExprParser.ExprContext right = ctx.expr().get(1);
        IType leftItype = left.accept(this);
        IType rightItype = right.accept(this);
        Double temp = 0d;
        if(ctx.MUL()!=null){
            temp = leftItype.getValue()*rightItype.getValue();
        }
        if(ctx.DIV()!=null){
            temp = leftItype.getValue()/rightItype.getValue();
        }

        return (T)new CalNumber(temp.toString());
    }

    @Override
    public T visitAddSub(CalExprParser.AddSubContext ctx) {
        CalExprParser.ExprContext left = ctx.expr().get(0);
        CalExprParser.ExprContext right = ctx.expr().get(1);
        IType leftItype = left.accept(this);
        IType rightItype = right.accept(this);
        Double temp = 0d;
        if(ctx.ADD()!=null){
            temp = leftItype.getValue()+rightItype.getValue();
        }
        if(ctx.SUB()!=null){
            temp = leftItype.getValue()-rightItype.getValue();
        }
        return (T)new CalNumber(temp.toString());
    }

    @Override
    public T visitId(CalExprParser.IdContext ctx) {
        CalVarb calVarb = new CalVarb(ctx.ID().getText());
        return (T)calVarb;
    }

    @Override
    public T visitNumber(CalExprParser.NumberContext ctx) {
        CalNumber number = new CalNumber(ctx.getText());
        return (T) number;
    }
}

测试

我们来构建一个测试类

代码语言:javascript
复制
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 wang.datahub.CalExprLexer;
import wang.datahub.CalExprParser;
import wang.datahub.MyCalVistor;
import wang.datahub.datatype.IType;


public class Test {
    public static void main(String[] args) {
        String expr = "a=1 \n" +
                "b=2 \n" +
                "c=(a+b)/2 \n" +
                "c\n" +
                "d=a\n" +
                "f=d\n" +
                "f\n" +
                "11\n" +
                "f=a+b+c\n" +
                "f\n" +
                "CLEANMEM \n" +
                "a=4\n" +
                "a!+(2^2)+1\n";

        System.out.println(expr);

        CharStream stream= CharStreams.fromString(expr);
        CalExprLexer lexer=new CalExprLexer(stream);
        CalExprParser parser = new CalExprParser(new CommonTokenStream(lexer));

        ParseTree parseTree = parser.prog();
        MyCalVistor<IType> parseTreeWalker = new MyCalVistor();

        String res = parseTree.toStringTree(parser);
        System.out.println(res);


        IType value = parseTreeWalker.visit(parseTree);
        System.out.println(value.getValue());
    }
}

执行结果,完全符合预期

代码语言:javascript
复制
a=1 
b=2 
c=(a+b)/2 
c
d=a
f=d
f
11
f=a+b+c
f
CLEANMEM 
a=4
a!+(2^2)+1

(prog (stat a = (expr 1) \n) (stat b = (expr 2) \n) (stat c = (expr (expr ( (expr (expr a) + (expr b)) )) / (expr 2)) \n) (stat (expr c) \n) (stat d = (expr a) \n) (stat f = (expr d) \n) (stat (expr f) \n) (stat (expr 11) \n) (stat f = (expr (expr (expr a) + (expr b)) + (expr c)) \n) (stat (expr f) \n) (stat CLEANMEM) (stat \n) (stat a = (expr 4) \n) (stat (expr (expr (expr (expr a) !) + (expr ( (expr (expr 2) ^ (expr 2)) ))) + (expr 1)) \n))
c = 1.5
f = 1.0
11.0
f = 4.5
29.0
29.0

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 麒思妙想 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定义语法
  • 定义数据类型
  • 遍历AST
  • 测试
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档