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

 

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

定义语法

定义语法文件,首先通过 prog: stat+ ;允许多条语句, 然后定义规则,支持打印表达式expr NEWLINE,表达式赋值expr NEWLINE, 清理内存表CLEANMEM,以及换行。

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

语法文件如下:

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 ;
 

定义数据类型

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

 

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

package wang.datahub.datatype;
​
public interface IType {/*** 获取当前对象的值* */Double getValue();
​/*** 设置当前值* */void setValue(Double d);
​
​/*** 判断是否为值类型* */Boolean isValue();
​
​/*** 是否是变量* */Boolean isVarb();
}
​

定义值类型

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;}
​
​
​@Overridepublic Double getValue() {return _value;}
​
​@Overridepublic Boolean isValue() {return true;}
​@Overridepublic Boolean isVarb() {return false;}
​@Overridepublic String toString() {return "CalNumber{" +"_value=" + _value +'}';}
}
​

定义变量类型

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<>();}
​@Overridepublic Double getValue() {
//        System.out.println(_map);return _map.get(_name).getValue();}
​@Overridepublic void setValue(Double d) {_map.put(_name,new CalNumber(d.toString()));}
​@Overridepublic Boolean isValue() {return false;}
​@Overridepublic Boolean isVarb() {return true;}
​@Overridepublic String toString() {return "CalVarb{" +"_name='" + _name + '\'' +'}';}
}
​

遍历AST

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

 

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

    @Overridepublic T visitId(CalExprParser.IdContext ctx) {CalVarb calVarb = new CalVarb(ctx.ID().getText());return (T)calVarb;}
​@Overridepublic T visitNumber(CalExprParser.NumberContext ctx) {CalNumber number = new CalNumber(ctx.getText());return (T) number;}

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

    @Overridepublic 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;}
​@Overridepublic 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;}
​

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

 @Overridepublic 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代码如下:

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>{
​@Overridepublic T visitCleanmem(CalExprParser.CleanmemContext ctx) {CalVarb.cleanmem();return super.visitCleanmem(ctx);}
​@Overridepublic 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;}
​@Overridepublic 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;}
​@Overridepublic T visitFac(CalExprParser.FacContext ctx) {IType iType = ctx.expr().accept(this);Double l = ArithmeticUtils.factorialDouble(iType.getValue().intValue());return (T) new CalNumber(l.toString());}
​@Overridepublic 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());}
​@Overridepublic T visitParens(CalExprParser.ParensContext ctx) {IType iType = ctx.expr().accept(this);return (T)iType;}
​@Overridepublic 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());}
​@Overridepublic 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());}
​@Overridepublic T visitId(CalExprParser.IdContext ctx) {CalVarb calVarb = new CalVarb(ctx.ID().getText());return (T)calVarb;}
​@Overridepublic T visitNumber(CalExprParser.NumberContext ctx) {CalNumber number = new CalNumber(ctx.getText());return (T) number;}
}
​

测试

我们来构建一个测试类

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());}
}
​

执行结果,完全符合预期

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
​
​

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/64469.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学生专用计算机多少人,怎么使用学生专用计算器?

计算机是大家生活常用的辅助设备&#xff0c;但普通计算器只能够计算加减乘除等基础需要&#xff0c;学生专用计算器是可以满足高难度的计算需求&#xff0c;其中的一些功能可能大家不太清楚&#xff0c;下面就来讲解一下它的是使用方法。 基本按键 学生专用计算器采用的是双行…

小狗为什么敢对大狗挑衅?原来有这几个“原因”

相信很多人都见过小型犬对着大型犬吠叫得很凶&#xff0c;“挑衅”大狗的行为。明明双方都不是一个量级的&#xff0c;难道小狗就没有自知之明&#xff0c;真的敢和大狗开战吗&#xff1f; 其实小狗对大狗凶&#xff0c;是有几个原因的&#xff01;如果没有这些因素了&#xf…

威洛特:狗狗乱咬东西都是有原因的

有的时候&#xff0c;主人回到家中&#xff0c;发现狗狗把沙发、拖鞋都啃了个遍&#xff0c;看到这种场景是不是都想原地爆炸呢&#xff1f;不过狗狗出现这种行为都是有原因的&#xff0c;有些时候主人自身也有责任的。狗狗为什么会喜欢咬东西呢&#xff1f;威洛特这就带你来了…

威洛特:导致狗狗食欲不振的原因有哪些

狗狗食欲不佳可以先观察它的精神状态怎么样&#xff0c;有没有呕吐腹泻的症状&#xff0c;如果除了食欲不好之外没有其他症状表现可以给狗狗喂食益生菌调理一下肠胃。如果有出现呕吐腹泻的现象的话则需要进行对症治疗。接下来可以跟着威洛特一起来了解狗食欲不好的常见原因&…

狗狗出现这五种“现象”,多半是讨厌主人了,而你还不自知?

现在养狗的人很多&#xff0c;有时候在饲养的过程中&#xff0c;主人做了一些让狗狗不喜欢的事情&#xff0c;但是主人还不自知。当狗狗出现这五种“现象”时&#xff0c;多半是讨厌主人了。 第一种&#xff1a;不对你摇尾巴 摇尾巴是狗狗最常见的一种示好方式&#xff0c;尤其…

每日一犬 · 哈瓦那犬

哈瓦那犬 是一种坚强的短腿小型犬 具有柔软而厚密的未经修剪的毛发 它的多毛尾巴弯曲翘向后背 是一种富有感情令人喜欢的犬 哈瓦那犬 身 高&#xff1a;21cm-26cm 体 重&#xff1a;3kg-6kg 寿 命: 10年-14年 习 性&#xff1a;温和、惹人喜爱 喜 食&#xff1a;适当的饲…

小狗7岁了

变量就是在程序的运行过程中数值可变的数据&#xff0c;用来记录运算中间结果或保存数据。程序会为变量在内存中开辟一个存储区域&#xff0c;该区域有自己的名称&#xff08;变量名&#xff09;&#xff0c;类型&#xff08;数据类型&#xff09;&#xff0c;该区域的数据可以…

争当“猫狗”爹妈,“它经济”成流量引擎

随着国民收入水平的逐步提高、消费观念升级以及宠物观念的日益成熟&#xff0c;宠物经济发展迅猛且潜力大。 无论是之前"金毛siri被托运致死“登上各平台热搜&#xff0c;还是疫情封控期间大家分享宠物在家等主人回家的各类视频&#xff0c;都印证了现今宠物地位的大幅度提…

日语中动物的叫声是怎样的-狗子怎么叫的

在日文中动物的一些叫声是怎么样的&#xff0c;用日文怎么形容呢 动物的叫声&#xff08;动物の鸣き声&#xff09; 大象&#xff08;ぞう&#xff09;&#xff1a;パオーンパオーン paon paon 猴子&#xff08;さる&#xff09;&#xff1a;ウキーキー uki kii 熊&#xf…

四、狗狗大全应用实战

传送门 《一、Android Studio的安装和使用》 《二、Android界面开发》 《三、Android网络开发》 《四、狗狗大全应用实战》 视频教程 https://space.bilibili.com/249229063/channel/seriesdetail?sid1930119 学习目标 综合前面学习内容&#xff0c;完成狗狗大全App&…

网络对大学生影响的调查研究报告

作者&#xff1a;倪宇轩&#xff0c;王伟燃&#xff0c;卢锴&#xff0c;徐新顺, 胡大宸&#xff08;排名不分先后&#xff09; 摘要&#xff1a;现在大多数大学生由于在高中时期&#xff0c;对网络接触的十分少&#xff0c;进入大学之后难以端正对待网络的态度&#xff0c;并且…

设计《大学生暑期社会实践调查问卷》

1.设计“大学生暑期社会实践调查问卷”页面&#xff0c;如下图所示。 2&#xff0e;调查表前导语的内容如下所示&#xff1a; 大学生暑期社会实践调查问卷 亲爱的同学&#xff1a;大家好! 为了更好的了解人们对近年来计算机类专业填报火热现象的看法&#xff0c;特制定本问…

关于大学生寝室点外卖的调研报告

关于大学生点外卖的调研报告 调研人&#xff1a;陈劲涛 李清安 潘瑞祥 苏洸远&#xff08;名字不分先后&#xff09; 这次的分析的主要对象是大一学生&#xff0c;课题主要是关于大学生在寝室里点外卖的情况&#xff1b;从数据分析中看到的本质问题&#xff1b;以及出现这些…

大学生对于外卖和食堂之间的抉择的调查报告 新生研讨课校内调查

电子科技大学 张成卓&#xff0c;熊浩宇&#xff0c;郭子畅 2018年末 引言 随着科技的进步&#xff0c;快递行业在电商的刺激下蓬勃发展&#xff0c;一定程度上改变了很多行业的运营情况&#xff0c;其中餐饮业受到的影响可以说是很大的。身边越来越多的人足不出户就能吃到自…

【报告分享】2021大学生消费行为洞察报告-校果研究院(附下载)

摘要:中国在校大学生群体不断扩大&#xff0c;目前大学生人数已达4183万人。同时&#xff0c;随着家庭结构带来消费力的变化。「421」的家庭结构&#xff0c;即双方老人及父母孩子&#xff0c;意味着独一代的大学生将获得更多的“家庭资源”&#xff0c;月均可支配现金超2000。…

UQPSK调研报告

目录 研究现状国外研究现状国内研究现状 基本原理BPSKDQPSKQPSKUQPSK 调制实现QPSK 调制正交调相法相位选择法 QPSK 解调UQPSK 调制UQPSK 解调 技术优点技术局限性 研究现状 数字相位调制又称为相移键控(Phase Shift Keying, PSK)&#xff0c;是一种十分重要的调制技术。PSK 是…

大学生外卖市场需求状况调查数据可视化报告

最近我们被客户要求撰写关于外卖市场需求可视化的研究报告&#xff0c;包括一些图形和统计输出。 随着社会经济的发展&#xff0c;饮食生活已经逐渐融入了我们的日常生活世界&#xff0c;每天都不可避免地在 "吃 "的问题上有更多的考虑&#xff0c;吃好、吃多已经不再…

大学生月生活费调研报告

调研报告 调研人&#xff1a;尉子谦&#xff0c;王奎元&#xff0c;王臻&#xff0c;周路尧 课题&#xff1a;格拉斯哥学院学生月生活费情况 调查形式&#xff1a;线上匿名填写调查问卷现场采访&#xff1b; 任务分配&#xff1a;每人去校园采访至少五人 问卷填写结果&…

#应用统计学: 大学生校园市场饮料产品消费情况的调查报告

目录 第一部分 预习准备... 4 一、实验目的... 4 二、实验要求... 4 三、实验仪器和设备... 4 第二部分 实验过程... 4 一、实验步骤... 4 1.问卷设计&#xff08;原始问卷&#xff09;... 4 2.确定调查对象... 6 3.分发调查问卷... 6 4.回收调查问卷... 6 二、实验…

大学生实习就业调研报告之一 - 调研数据统计和分析

摘要&#xff1a;2014年8月&#xff0c;CSDN高校俱乐部推出面向大学生的《实习就业需求调研》活动。截止10.31日活动结束&#xff0c;共收到几百所高校学生的反馈&#xff0c;汇集当代大学生在实习就业中普遍存在的心态问题及就业期望。为此高校俱乐部整理分析全部调研数据&…