中文编程知乎专栏原文地址

例程(更多测试用例在):

基数=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×年增长率×年增长率

最后语法树如下:

![2018-01-12-antlr4赋值quan2]({{ "/assets/2018-01-12-antlr4赋值_quan2.png" | absolute_url }})

"年增长率"应该提前求值, 以省去最后的多次计算(避免对变量重复求值 · 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添加中文变量赋求值,括号,各种问题的更多相关文章

  1. static 和 final 关键字 对实例变量赋初始值的影响

    static 和 final 关键字 对实例变量赋初始值的影响 最近一直在看<深入理解Java虚拟机>,在看完了对象内存分配.Class文件格式之后,想深扒一下实例变量是如何被赋上初始值的 ...

  2. sql server 添加字段并且赋默认值和说明

    select soct.Captcha,CreateOn,* from SceneryOrderCheckTicket soctright join (SELECT Captcha,convert(c ...

  3. typedef声明变量也是一种求值过程

    前言: 什么叫做:声明变量是求值过程?请看下面的声明, int i; 很简单,声明了个整型变量i,再看如下声明, int *p; 也很简单,立刻反应出来它是指向整型的指针,但是具体如何推倒出来的呢?其 ...

  4. 好用的wget命令从下载添加环境变量到各参数详解

    本文是因为(笔者使用的windows系统)使用过好几次wget后,始终存在各种细节问题,于是下定决定细致的研究一下,并记录下其中细节. 下载与安装 第一步:下载wget,网络地址:http://dow ...

  5. 新手C#SQLServer在程序里实现语句的学习2018.08.12

    从C#中连接到SQL Server数据库,再通过C#编程实现SQL数据库的增删改查. ado.net提供了丰富的数据库操作,这些操作可以分为三个步骤: 第一,使用SqlConnection对象连接数据 ...

  6. Linux下查看和添加环境变量

    转自:http://blog.sina.com.cn/s/blog_688077cf01013qrk.html $PATH:决定了shell将到哪些目录中寻找命令或程序,PATH的值是一系列目录,当您 ...

  7. dedecms后台添加新变量和删除变量的方法

    下面由做网站为大家来介绍dedecms后台添加新变量和删除变量的方法 添加新变量是做什么用的?答:可以在模板内调用的东东. 一.进入网站织梦(Dedecms)后台(以dede5.5为例),依次打开系统 ...

  8. 软件 利用 win+R 快速启动(无需添加环境变量)

    前言:以 "Typora" 软件 为例 ,无需添加环境变量,实现键盘快速启动 第一步 找到 为知笔记的快捷方式 打开文件位置 鼠标右击该软件的桌面快捷方式 复制该软件的快捷方式 第 ...

  9. php 添加环境变量

    1.php添加环境变量主要为了能在 cmd和软件的客户端用php来运行 首先我们要做的第一步: 添加环境变量(记住php.exe的路径,然后再环境变量中编辑path 多个用逗号分隔开,保存重启电脑) ...

随机推荐

  1. 微信小程序开发资源汇总 💯

    目录 官方文档 工具 插件 讨论 教程 视频教程 文章 代码 轮子 置顶 WePY:组件化的小程序开发框架 

  2. hibernate多表查询封装实体

    以前用sql实现联合查询 是非常简单的事,只需要写sql语句就可以,第一次遇到hibernate要实现多表联合查询的时候还楞了一下.最后看了下资料,才恍然大悟,hibernate实现多表联合查询跟SQ ...

  3. maven中pom文件配置解决资源文件的编码问题

    <build> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId& ...

  4. SSM-SpringMVC-06:SpringMVC关于静态资源无法展示的问题

     ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 按照之前的那种方式一路走下来,或许你没发觉有问题,只是你没有使用到而已 css,js,图片等无法正常使用怎么 ...

  5. form 表单提交返回值问题

    form不比ajax,即使后台返回值后,在页面也不知道怎么去取值判断提交状态.所以前几天结合网上资料整了一个小案例,需要用到ajaxSubmit,即通过ajax来提交表单,好处在于可以在任何情况下进行 ...

  6. Java判断字符串是否为数字的自定义方法

    //方法一:用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = str.length();--i>=0;){ ...

  7. 干货 | Java中获取类名的3种方法!

    获取类名的方法 Java 中获取类名的方式主要有以下三种. getName() 返回的是虚拟机里面的class的类名表现形式. getCanonicalName() 返回的是更容易理解的类名表示. g ...

  8. testng生成自定义html报告

    转自:https://blog.csdn.net/kdslkd/article/details/51198433 testng原生的或reportng的报告总有些不符合需要,尝试生成自定义测试报告,用 ...

  9. 聊聊Socket、TCP/IP、HTTP、FTP及网络编程

    1 这些都是什么 既然是网络传输,涉及几个系统之间的交互,那么首先要考虑的是如何准确的定位到网络上的一台或几台主机,另一个是如何进行可靠高效的数据传输.这里就要使用到TCP/IP协议. 1.1 TCP ...

  10. js默认参数实现方法

    function simue (){ var a = arguments[0] ? arguments[0] : 1; var b = arguments[1] ? arguments[1] : 2; ...