2018-01-02 JavaScript实现ZLOGO: 用语法树实现多层循环
原址: https://zhuanlan.zhihu.com/p/32571516
照例先上演示弱效果图. 演示地址照旧:

代码如下:
开始
循环4次
循环4次
前进50
左转90度
到此为止
右转90度
到此为止
结束
如上文JavaScript实现ZLOGO子集: 测试用例末尾所言, 此文用Antlr进行代码分析生成语法树. 再通过语法树生成p5js绘制代码.
Antlr支持两种代码分析方法, Visitor(监听者)和Visitor(访问者). SO上的问答Antlr4 Listeners and Visitors - which to implement?大致说明了区别. 基于有限的实践, 用Visitor方法生成语法树似乎在实现上更加方便. 尤其相比Creating a simple parser with ANTLR一文中使用监听者+栈来构建语法树.
Antlr生成工具默认不生成Visitor, 添加-visitor参数后可以生成:
java -cp "antlr-4.7-complete.jar:$CLASSPATH" org.antlr.v4.Tool -Dlanguage=JavaScript -visitor 圈3.g4
下面是"定制访问器.js"中构建语法树的部分, 看起来比实现前想的简单. 默认生成的'圈3Visitor'中, visitXX方法实现都是"this.visitChildren(ctx)", 但那样会把所有的子节点返回值放进数组, 形成(至少这里是)多余的层次:
定制访问器.prototype.visit程序 = function(上下文) {
语法树 = {子节点: this.visit(上下文.声明())};
return 语法树;
};
定制访问器.prototype.visit循环 = function(上下文) {
return {
类型: '循环',
次数: parseInt(上下文.T数().getText()),
子节点: this.visit(上下文.声明())};
};
定制访问器.prototype.visit声明 = function(上下文) {
return this.visit(上下文.getChild(0));
};
定制访问器.prototype.visit转向 = function(上下文) {
var 方向 = 上下文.T转向().getText();
var 角度 = parseInt(上下文.T数().getText()) * (方向 === "左" ? 1 : -1);
return {类型: '转向', 参数: 角度};
};
定制访问器.prototype.visit前进 = function(上下文) {
return {类型: '前进', 参数: parseInt(上下文.T数().getText())};
};
上面的源码生成语法树大致如下所示. 实现上还有很多需要改进的, 比如'前进'和'转向'现在是两种'类型', 但应该是一种; 根节点类型不应为空; 等等:

下面是"编译.js"中基于语法树生成指令列表的方法, 之后就与之前一样根据指令列表生成p5js绘制函数(代码也不用修改).
function 生成指令序列(节点) {
var 指令序列 = [];
// TODO: 根节点类型不应为空
if (!节点.类型) {
var 声明节点 = 节点.子节点;
for (var i = 0; i < 声明节点.length; i++) {
Array.prototype.push.apply(指令序列, 生成指令序列(声明节点[i]));
}
} else if (节点.类型 == "循环") {
var 指令序列 = [];
for (var i = 0; i < 节点.次数; i++) {
Array.prototype.push.apply(指令序列, 生成指令序列({子节点: 节点.子节点}));
}
} // TODO: 修改类型统一为'指令'
else if (节点.类型 == "前进" || 节点.类型 == "转向") {
return [{名称: (节点.类型 == "前进" ? 常量_指令名_前进 : 常量_指令名_转向), 参数: 节点.参数}];
}
return 指令序列;
}
修改相应测试用例, 以及清理不再使用的监听器代码后. 代码已从visitor分支(program-in-chinese/quan3)合并到master.
2018-01-02 JavaScript实现ZLOGO: 用语法树实现多层循环的更多相关文章
- JavaScript实现ZLOGO: 用语法树实现多层循环
原址: https://zhuanlan.zhihu.com/p/32571516 照例先上演示弱效果图. 演示地址照旧: 代码如下: 开始 循环4次 循环4次 前进50 左转90度 到此为止 右转9 ...
- Delphi及C++Builder经典图书一览表(持续更新中2018.01.02)
序号 书名 原版书名 作者 译者 出版社 页数 年代 定价 备注 1 C++Builder 5程序设计大全 C++Builder 5 Developer's Guide Jarrod Hollingw ...
- 2018-12-14 JavaScript实现ZLOGO: 前进方向和速度
系列前文: JavaScript实现ZLOGO子集: 前进+转向 JavaScript实现ZLOGO子集: 单层循环功能 JavaScript实现ZLOGO子集: 测试用例 JavaScript实现Z ...
- javaScript系列 [02]-javaScript对象探析
[02]-javaScript对象探析 题记:多年前,以非常偶然的方式关注了微信公众号“面向对象”,本以为这个公众号主要以分享面向对象编程的干货为主,不料其乃实实在在的猿圈相亲平台.通过查看公开资料, ...
- 2019-01-20 JavaScript实现ZLOGO: 界面改进与速度可调
续前文JavaScript实现ZLOGO: 前进方向和速度 在线演示地址: http://codeinchinese.com/%E5%9C%883/%E5%9C%883.html 源码仍在: prog ...
- 2017-12-09 JavaScript实现ZLOGO子集: 测试用例
续前文JavaScript实现ZLOGO子集: 前进+转向. 在添加新功能之前, 先添加测试用例, 以应对日益复杂的代码. 选择使用QUnit编写运行测试用例. 暂时对比较复杂和I/O无关的部分进行测 ...
- 2019-01-23 JavaScript实现ZLOGO: 性能改进
主攻前文吴烜:JavaScript实现ZLOGO: 界面改进与速度可调的几个性能问题 在线演示: 圈3 源码仍在: program-in-chinese/quan3 之前是在绘制过程中计算每帧需要绘制 ...
- 2018.12.02 Socket编程之初识Socket
Socket编程主要分为TCP/UDP/SCTP三种,每一种都有各自的优点,所以会根据实际情况决定选用何种Socket,今天开始我将会逐步学习Socket编程,并将学习过程记录于此. 今天学习的是TC ...
- linux下生成00 01 02..99的这些数
[root@localhost ~]# seq -s " " -w 9901 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 ...
随机推荐
- swiper里面几个有用的参数
概述 这是我自己用swiper和看别人官网源码用swiper总结出来的,供以后开发时参考,相信对其他人也有用. observeParents 有时我们会改变swiper的父级元素,比如页面的resiz ...
- RISC-V踩坑记----__builtin_clz((x)库函数的应用
RISC-V的确是个好东西,可是,免费的东西往往需要付出代价才能得到了,最近遇到了一个算法中的问题,追了好久,最终追到了这个库函数中,没想到,这个库函数居然还隐藏着一些猫腻.值得记下来啊. 首先上一个 ...
- java中根据key获取resource下properties资源文件中对应的参数
properties资源文件是放在resource目录下的: 新建工具类: package com.demo.utils; import java.io.InputStream; import jav ...
- 性能瓶颈之Mapping
如果Source和Target都不存在性能上的瓶颈,则问题可能会出在Mapping 如何判定Mapping存在性能瓶颈 1) 在session log中读取thread statistics和wor ...
- Java 11 正式发布,这 8 个逆天新特性教你写出更牛逼的代码
美国时间 09 月 25 日,Oralce 正式发布了 Java 11,这是据 Java 8 以后支持的首个长期版本. 为什么说是长期版本,看下面的官方发布的支持路线图表. 可以看出 Java 8 扩 ...
- 在Go语言中基础的Redis操作
在Go语言中基础的Redis操作 需要先安装redigo go get "github.com/garyburd/redigo/redis" Go语言Redis客户端的简单示例 连 ...
- 签名时出错: 未在路径 C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin
在运行winform程序时,由于清理解决方案等缘故,出现了下面的情况 解决办法:项目-属性-签名-取消勾选“为ClickOne清单签名” 问题完美解决
- python基础-字符串(6)
一.引言 当打来浏览器登录某些网站的时候,需要输入密码,浏览器把密码传送到服务器后,服务器会对密码进行验证,其验证过程是把之前保存的密码与本次传递过去的密码进行对比,如果相等,那么就认为密码正确,否则 ...
- Linux下安装、启动、停止mongodb
1.下载完安装包,并解压 tgz(以下演示的是 64 位 Linux上的安装) curl .tgz # 下载 tar .tgz # 解压 mv mongodb/ /usr/local/mongodb ...
- Virtual Hosts
虚拟主机就是一组资源,就是资源的一个逻辑分组 虚拟主机提供对资源的逻辑分组和隔离 虚拟主机有一个名字.当客户端连接到RabbitMQ的时候,客户端指定一个虚拟主机的名字来连接到它.如果认证成功,并且用 ...