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
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
阿里云栖开发者沙龙PHP技术专场-深入浅出网络编程与Swoole内核
摘要:在阿里云PHP技术沙龙专场中,邀请到php-nsq作者,pecl、Swoole开发组成员吴振宇分享了Swoole进程模型的原理与Swoole协程实现的原理。并结合具体开发案例讲解了Swoole在网络编程中的应用。 本次直播视频精彩回顾,戳这里!直播回顾:https://yq.aliyun.com/live/965PPT分享:https://yq.aliyun.com/download/3528 以下内容根据演讲嘉宾视频分享以及PPT整理而成。 Socket编程 网络编程又可称为Socket编程。编程分为基于Server端开发与基于Client端开发两部分。基于Server端的编程由四大步骤组成,开发者首先创建Socket,利用bind与listen函数绑定监听地址及相应的端口,最后使用accept函数接受来自监听端的请求。Cli
- 下一篇
Antlr4: 修改语法规则更接近普通BNF格式
经 @沈默 在上文Antlr4添加中文变量赋求值,括号,各种问题评论中指出, 语法规则描述依赖于Antlr4生成的语法分析器的默认分析方法, 比如运算符的左联系, 以及优先级处理等等. 于是将语法修改为下面(源码版本号: program-in-chinese/quan5): 表达式: 求积表达式 (('+'|'-') 求积表达式)*; 求积表达式: 最小表达式 (('*'|'/'|'×'|'÷') 最小表达式)*; 最小表达式 : 字面量 | '(' 表达式 ')' ; 字面量 : T数 | T变量名 ; 这样做的结果是, Antlr4会分析生成一个多叉树. 比如 1+2-3, 生成树如下: 于是在生成抽象语法树时手动转换为二叉树. 代码如下: private 节点 构建二叉树(List<ParseTree> 子节点) { if (子节点.isEmpty()) { return null; } else if (子节点.size() == 1) { return visit(子节点.get(0)); } else { ParseTree 最后运算符节点 = 子节点.get(...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Mario游戏-低调大师作品
- CentOS关闭SELinux安全模块
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Red5直播服务器,属于Java语言的直播服务器
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS6,CentOS7官方镜像安装Oracle11G