2018-01-12 Antlr4添加中文变量赋求值,括号,各种问题
例程(更多测试用例在此):
基数=100
基数×(基数+1)÷2
=> 求值为5050
续上文Antlr4实现数学四则运算, 修改的语法规则部分:
程序: 声明+;
声明: 表达式 T新行 #求值
| T变量名 '=' 表达式 T新行 #赋值
| T新行 #空行
;
表达式: 表达式 运算符=('*'|'/'|'×'|'÷') 表达式 #乘除
| 表达式 运算符=('+'|'-') 表达式 #加減
| T数 #数
| T变量名 #变量
| '(' 表达式 ')' #括号
;
T变量名: ('a' .. 'z' | 'A' .. 'Z' | '\u4E00'..'\u9FA5' | '\uF900'..'\uFA2D')+;
T新行: '\r'?'\n';
很明显, 变量名的范围仍需扩展, 比如数字就不支持, 而且这个字符范围应该有些过大(详见Validate a JavaScript function name), 待修正(变量字符范围 · Issue #1 · program-in-chinese/quan5).
定制访问器添加的部分:
private static Map<String, 节点> 变量值表 = new HashMap<>();
// 以下为声明部分
@Override
public 节点 visit赋值(赋值Context 上下文) {
String 变量名 = 上下文.T变量名().getText();
变量值表.put(变量名, visit(上下文.表达式()));
return null;
}
@Override
public 节点 visit求值(求值Context 上下文) {
return visit(上下文.表达式());
}
// 以下为表达式部分
@Override
public 节点 visit变量(变量Context 上下文) {
String 变量名 = 上下文.T变量名().getText();
// TODO: 添加变量检查
return 变量值表.get(变量名);
}
@Override
public 节点 visit括号(括号Context 上下文) {
return visit(上下文.表达式());
}
变量值表采用变量名到节点的映射, 也就是在对包含这个变量的表达式求值时才对变量对应的表达式进行求值. 这里没有对变量赋值表达式进行语法树构建 · Issue #2 · program-in-chinese/quan5, 还需更多工作. 另外一个问题, 最后的表达式求值也会对变量值重复计算. 举例:
利率=1
年增长率=1+利率
1000×年增长率×年增长率
最后语法树如下:

"年增长率"应该提前求值, 以省去最后的多次计算(避免对变量重复求值 · Issue #3 · program-in-chinese/quan5)
后两个问题已初步解决, 通过在"运行器"中保存变量表, 以及将各种节点的求值方法都集中到其中. 想起来在其他有些语言实现里也看到过类似结构(根据不同类型进行求值):
public Object 求值(节点 节点) {
if (节点 instanceof 运算式节点) {
运算符号 运算符 = ((运算式节点)节点).运算符;
Object 左结果 = 求值(((运算式节点)节点).左子节点);
Object 右结果 = 求值(((运算式节点)节点).右子节点);
switch(运算符) {
case 加: return (int)左结果 + (int)右结果;
case 減: return (int)左结果 - (int)右结果;
case 乘: return (int)左结果 * (int)右结果;
case 除: return (int)左结果 / (int)右结果;
case 赋值:
变量值表.put(((变量节点)((运算式节点)节点).左子节点).取变量名(), 右结果);
// 顺延
default:
return null;
}
} else if (节点 instanceof 变量节点) {
return 变量值表.get(((变量节点)节点).取变量名());
} else if (节点 instanceof 数节点) {
return ((数节点)节点).求值();
} else {
for(节点 子节点 : 节点.子节点) {
返回值 = 求值(子节点);
}
return 返回值;
}
}
2018-01-12 Antlr4添加中文变量赋求值,括号,各种问题的更多相关文章
- static 和 final 关键字 对实例变量赋初始值的影响
static 和 final 关键字 对实例变量赋初始值的影响 最近一直在看<深入理解Java虚拟机>,在看完了对象内存分配.Class文件格式之后,想深扒一下实例变量是如何被赋上初始值的 ...
- sql server 添加字段并且赋默认值和说明
select soct.Captcha,CreateOn,* from SceneryOrderCheckTicket soctright join (SELECT Captcha,convert(c ...
- typedef声明变量也是一种求值过程
前言: 什么叫做:声明变量是求值过程?请看下面的声明, int i; 很简单,声明了个整型变量i,再看如下声明, int *p; 也很简单,立刻反应出来它是指向整型的指针,但是具体如何推倒出来的呢?其 ...
- 好用的wget命令从下载添加环境变量到各参数详解
本文是因为(笔者使用的windows系统)使用过好几次wget后,始终存在各种细节问题,于是下定决定细致的研究一下,并记录下其中细节. 下载与安装 第一步:下载wget,网络地址:http://dow ...
- 新手C#SQLServer在程序里实现语句的学习2018.08.12
从C#中连接到SQL Server数据库,再通过C#编程实现SQL数据库的增删改查. ado.net提供了丰富的数据库操作,这些操作可以分为三个步骤: 第一,使用SqlConnection对象连接数据 ...
- Linux下查看和添加环境变量
转自:http://blog.sina.com.cn/s/blog_688077cf01013qrk.html $PATH:决定了shell将到哪些目录中寻找命令或程序,PATH的值是一系列目录,当您 ...
- dedecms后台添加新变量和删除变量的方法
下面由做网站为大家来介绍dedecms后台添加新变量和删除变量的方法 添加新变量是做什么用的?答:可以在模板内调用的东东. 一.进入网站织梦(Dedecms)后台(以dede5.5为例),依次打开系统 ...
- 软件 利用 win+R 快速启动(无需添加环境变量)
前言:以 "Typora" 软件 为例 ,无需添加环境变量,实现键盘快速启动 第一步 找到 为知笔记的快捷方式 打开文件位置 鼠标右击该软件的桌面快捷方式 复制该软件的快捷方式 第 ...
- php 添加环境变量
1.php添加环境变量主要为了能在 cmd和软件的客户端用php来运行 首先我们要做的第一步: 添加环境变量(记住php.exe的路径,然后再环境变量中编辑path 多个用逗号分隔开,保存重启电脑) ...
随机推荐
- Spring MVC 文件上传 & 文件下载
索引: 开源Spring解决方案--lm.solution 参看代码 GitHub: pom.xml WebConfig.java index.jsp upload.jsp FileUploadCon ...
- 深入JVM分析spring-boot应用hibernate-validator
问题 可重现的Demo代码:demo.zip 最近排查一个spring boot应用抛出hibernate.validator NoClassDefFoundError的问题,异常信息如下: Caus ...
- php对接微信小程序支付
前言:这里我就假装你已经注册了微信小程序,并且基本的配置都已经好了.注: 个人注册小程序不支持微信支付,所以我还是假装你是企业或者个体工商户的微信小程序,其他的商户号注册,二者绑定,授权,支付开通,就 ...
- mysql用户链接数
show status like 'Threads_connected'; 当前连接数 show variables like '%max_connections%'; 最大链接数 set GLOB ...
- javascript 数组以及对象的深拷贝(复制数组或复制对象)的方法
前言 for,slice(0),concact() 在js中,数组和对象的复制如果使用=号来进行复制,那只是浅拷贝.如下图演示: 如上,arr的修改,会影响arr2的值,这显然在绝大多数情况下,并不 ...
- 超实用的JavaScript代码段 Item1 --倒计时效果
现今团购网.电商网.门户网等,常使用时间记录重要的时刻,如时间显示.倒计时差.限时抢购等,本文分析不同倒计时效果的计算思路及方法,掌握日期对象Date,获取时间的方法,计算时差的方法,实现不同的倒时计 ...
- 你不知道的JavaScript--Item12 undefined 与 null
当讨论JavaScript中的原始数据类型时,大多数人都知道从String.Number到Boolean的基本知识.这些原始类型相当简单,行为符合常识.但是,本文将更多关注独特的原始数据类型Null和 ...
- CSDN Android客户端的制作 导航帖
弄个导航贴,把相关知识来个汇总. CSDN Android的客户端的效果图: 分别通过以下博客进行详细的讲解: 1.Android 使用Fragment,ViewPagerIndicator 制作cs ...
- nodejs中处理回调函数的异常
如果是使用nodejs+express3这个经典的组合,那么有一种很方面的处理回调函数异常的方法: 1. 安装模块:express-domain-middleware 2. 加入如下的代码: app. ...
- java 中 一个int类型的num,num&1
n&1 把n与1按位与,因为1除了最低位,其他位都为0,所以按位与结果取决于n最后一位,如果n最后一位是1,则结果为1.反之结果为0.(n&1)==1: 判断n最后一位是不是1(可能用 ...