原址: 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.

JavaScript实现ZLOGO: 用语法树实现多层循环的更多相关文章

  1. 2018-01-02 JavaScript实现ZLOGO: 用语法树实现多层循环

    原址: https://zhuanlan.zhihu.com/p/32571516 照例先上演示弱效果图. 演示地址照旧: 代码如下: 开始 循环4次 循环4次 前进50 左转90度 到此为止 右转9 ...

  2. 2018-12-14 JavaScript实现ZLOGO: 前进方向和速度

    系列前文: JavaScript实现ZLOGO子集: 前进+转向 JavaScript实现ZLOGO子集: 单层循环功能 JavaScript实现ZLOGO子集: 测试用例 JavaScript实现Z ...

  3. 2017-12-06 JavaScript实现ZLOGO子集: 单层循环功能

    前文JavaScript实现ZLOGO子集: 前进+转向的示例代码很累赘, 因此尝试实现基本的循环功能, 使得前面的11行代码缩减为7行: 开始 循环4次 前进200 左转144度 到此为止 前进20 ...

  4. 2017-12-09 JavaScript实现ZLOGO子集: 测试用例

    续前文JavaScript实现ZLOGO子集: 前进+转向. 在添加新功能之前, 先添加测试用例, 以应对日益复杂的代码. 选择使用QUnit编写运行测试用例. 暂时对比较复杂和I/O无关的部分进行测 ...

  5. js 对于jquery each 多层循环的问题和原生js多层循环问题

    一.在jquery中,我们使用循环的时候,提供两种方式:jquery.each 和(循环体).each  两种方式不是同. 对于return 在作用这两个的函数的时候需要注意: 首先我们需要知道我们的 ...

  6. 关于python如何简单跳出多层循环

    上述代码的逻辑是,在跳出子循环之前定义一个变量flag为Ture,第一层循环跳出之后,如果要跳出第二个循环,直接调用变量flag,可以直接跳出第二层循环.这里要注意的是缩进,不然会出错误. 如果是多层 ...

  7. 2019-01-20 JavaScript实现ZLOGO: 界面改进与速度可调

    续前文JavaScript实现ZLOGO: 前进方向和速度 在线演示地址: http://codeinchinese.com/%E5%9C%883/%E5%9C%883.html 源码仍在: prog ...

  8. 2019-01-23 JavaScript实现ZLOGO: 性能改进

    主攻前文吴烜:JavaScript实现ZLOGO: 界面改进与速度可调的几个性能问题 在线演示: 圈3 源码仍在: program-in-chinese/quan3 之前是在绘制过程中计算每帧需要绘制 ...

  9. java的break跳出多层循环

    记得大一的时候,语言学的不好,碰到了需要跳出双层循环的时候,就没有了办法.因为老师讲了goto然后说不要用goto...  自己就一直感觉这种跳出多层循环的想法是不可取的(好蠢) 下面用java代码的 ...

随机推荐

  1. php接入支付宝的流程

    php接入支付宝的流程写在这里供像我一样的小白参考. 1.首先要有一个创建一个应用(选好自己想要的功能,关于支付的功能,貌似都需要签约) 2.下载SDK&Dome(网址https://doc. ...

  2. velocity 是如何实现内省 屏蔽反射的

    velocity的标签中支持$abc 这样的语法,如果abc是一个对象,则写模板时就可以利用它来进行反射,调用一些危险的方法,如 $vm.getClass().newInstance() #set ( ...

  3. FreeRTOS--疑难解答

    此章节涉及新手最常遇见的3种问题: 错误的中断优先级设置 栈溢出 不恰当的使用printf() 使用configASSERT()能够显著地提高生产效率,它能够捕获.识别多种类型的错误.强烈建议在开发或 ...

  4. scapy安装及SCTP包分析

    关于Scapy scapy是一个强大的交互式数据包处理程序(使用python编写).它能够伪造或者解码大量的网络协议数据包,能够发送.捕捉.匹配请求和回复包等.它可以很容易地处理一些典型操作,比如端口 ...

  5. Js 调用 WebService 实例

    Html页面代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/ ...

  6. Java设计模式之职责链设计模式

    1.什么是-职责链设计模式 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求 ...

  7. Java爬虫——网易云热评爬取

    爬取目标网址 :   http://music.163.com/#/song?id=409649818 需要爬取信息 :   网易云top13热评 使用之前的 HttpURLConnection 获取 ...

  8. OpenCV探索之路(二十八):Bag of Features(BoF)图像分类实践

    在深度学习在图像识别任务上大放异彩之前,词袋模型Bag of Features一直是各类比赛的首选方法.首先我们先来回顾一下PASCAL VOC竞赛历年来的最好成绩来介绍物体分类算法的发展. 从上表我 ...

  9. XMPP学习及使用1

    XMPP 简单介绍 本小节将简要介绍 XMPP,它的起源.以及为何它是一个适合实时 web 通信的协议.您将检查 XMPP 通信设置的组件,并查看展示这些组件怎样使用的演示样例. Web 标准和 XM ...

  10. Go语言核心之美-必读

    Go语言核心之美开篇了!.不管你是新手还是一代高人,在这个系列文章中.总能找到你想要的! 博主是计算机领域资深专家并且是英语专8水平,翻译标准仅仅有三个:精确.专业.不晦涩,为此每篇文章可能都要耗费数 ...