记一个脚本解释器的开发
最近可以有1个月左右的空闲,可以稍微整理一下这个脚本解释器的开发过程。
一、缘由
2014年左右,我们使用AIR技术,开发了一个3D战争类型的手游。那时候手游开发技术主要是cocos2d,unity,Air稍微小众一些,但是也有。那个时候正是AS3走下坡路的时候,BOSS耳软心活,一会要改用cocos,一会要改用unity,于是萌生了一个自己写一个as 3.0脚本解释器的想法。
二、关于actionscript3。
As3脚本语言,实际上就是ecmascript 262 V4的加强版,也就是说基本上js有的它都有,另外还有java的特性,包含完整的类继承,接口系统,还可以使用js的prototype原型链继承,2方面互不干扰,又可以互为补充,灵活又不失严谨。当年adobe和Mozilla提议将as3作为ecmascript 262 v4,但是受到了巨头公司(主要是微软)的反对,最终ecma没有发布 EcmaScript V4,而是发布了一个和谐版 V3.1。但是V4仍然保留了下来。当然如今已经是ecmascript 已经是6了,中间发生了苹果,安卓的崛起,wp的衰落,年年都是h5游戏元年这些事情大家都知道就不谈了。
a) As3的类继承 见代码,一看就懂吧,都不用解释,和c#基本没区别
package {[Doc]public class FuncTest{public function FuncTest() ;} } /* 类是唯一可实现接口的 ActionScript 3.0 语言元素。在类声明中使用 implements 关键字可实现一个或多个接口。 下面的示例定义两个接口 IAlpha 和 IBeta 以及实现这两个接口的类 Alpha: */ interface IAlpha { function foo(str:String):String; } interface IBeta { function bar():void; } class Alpha implements IAlpha, IBeta { public function foo(param:String):String { trace("foo", param); return null; } public function bar():void { trace("bar");} }var a=new Alpha();var alpha:IAlpha = a; var beta:IBeta = IBeta(alpha);alpha.foo("call foo"); beta.bar();
b) As3的原型链 见代码,会js的一看就明白,不用解释了吧.
package {[Doc]public class FuncTest{public function FuncTest() {}} } /* 类继承 -- 是主要的继承机制,并支持固定属性的继承。固定属性是声明为类定义一部分的变量、常量或方法。现在,可通过存储相关类信息的特殊类对象表示每个类定义。 原型继承 -- 每种类都有一个关联的原型对象,而原型对象的属性由该类的所有实例共享。 在创建一个类实例时,它具有对其类的原型对象的引用,这将作为实例及与其关联的类原型对象间的链接。 运行时,如果在类实例中找不到某属性, 则会检查委托(该类的原型对象)中是否有该属性。 如果原型对象不包含这种属性, 此过程会继续在层次结构中连续的更高级别上对原型对象进行委托检查,直到找到该属性为止。 */ //类继承和原型继承可同时存在,如下例所示: class A {var x = 1public function A(){A.prototype.px = 2}}dynamic class B extends A {var y = 3public function B(){B.prototype.py = 4}}var b = new B()trace(b.x) // 1 via class inheritancetrace(b.px) // 2 via prototype inheritance from A.prototypetrace(b.y) // 3trace(b.py) // 4 via prototype inheritance from B.prototype B.prototype.px = 5trace(b.px) // now 5 because B.prototype hides A.prototype b.px = 6trace(b.px) // now 6 because b hides B.prototypevar b2=new B()trace(b2.px) // ==5
三、龙书。
编译原理号称有龙,虎,鲸三本圣经。我参考的是龙书。要写脚本解释器,网上确实有许多参考文章,但是大多都是简单的告诉你怎么用简单的技巧去人肉写代码解析,再或者就是叫你去用类似yacc这样的工具,我买了2本书,一本叫“自制编程语言”,一本叫“两周自制脚本语言”。这两本书我读了一下,确实可以自制语言,但是肯定是无法自制如as3这样的大型的语言的。我也尝试使用人肉代码解析,发现这根本就没办法进行下去,稍有地方出错,就要大量修改然后自己也搞不清了。因此,最后我决定怼龙书。在这里,我就直接说出我怼龙书的心得了
a) 龙书有中文版pdf。内容非常丰富,文字也易懂,我个人感觉,值得一读不愧圣经之名
b) 对于脚本解释器而言,只要看到LL(1)就行了。龙书提供了一个极度详细的算法,详细到几乎是一步一步的指导你构建一个First和Follow翻译算法。有了这个算法就可以自己构建文法分析器!
c) 关于LL(1)文法。确实LL(1)文法有许多限制的地方,比如左递归,二义性等,但是这些都是可以解决的,左递归手工慢慢消除,二义性书里也介绍了解决的方案,只要尝试一下,就可以过去。
d) 做出文法分析工具后,就只要不断的尝试去写文法说明,最终就能得到目标语言的文法分析器,进而生成语法树!这就是看龙书的收获
四、从语法树到运行时
我用了3个月的时间,做到了可以解析几乎任何as3代码的语法树。从一般意义上说,这时候只要顺着语法树执行,就可以跑起代码来了。但事实是,做到这一步后,发现后面还有一个更大的坑在等着:自动垃圾收集。大家都知道js也好.net也好,都有垃圾收集器的,那么我们如果要自己实现完整的as3,势必也要自己实现垃圾收集器。这一步我想了很长时间,也没想出太好的办法,除非自己撸个垃圾收集器。。。。。当时BOSS要求用cocos开发新的项目,用C++的话,自动垃圾收集这个麻烦实在太大了。
但是时隔不久,cocos项目做了一半,BOSS突发奇想,又决定用Unity山寨某世面热门游戏一款。于是解释器暂停了,我们全力进行Unity的开发。一年后,游戏全部开发完成,稍有空闲,于是我准备继续将这个解释器进行完成。回到垃圾收集的问题,这个最简单的就是直接用C#的垃圾收集器代劳。因此,说干就干,解释器使用纯.net2.0开发,不用任何3.5开始的语法和类库,比如linq啊,hashset啊 这样可以,嗯,避免将来不必要的麻烦,懂得自然懂:)有了C#的高生产力,奋斗了几个月,解释器大致出炉了
五、解释器的能力
a) 编译时类型检查。对象访问权限控制,包括public ,private,protected等。如果使用类继承,或者编码时指定了变量类型,就能拥有编译时检查。行为和Adobe AIR编译器保持一致。
b) 原型链继承。和js类似,行为与Adobe AIR保持一致。对于封闭的类,可以使用原型链进行扩展。非常类似.net的扩展方法(真的非常像)
c) 闭包。任何函数都是一等对象,所以闭包支持顺理成章。
d) 完整的类继承,接口系统和AIR编译器完全一致。对于类的成员method,使用function.apply不能改变this指针。而其他的函数,则使用apply和call和js一致,和AIR编译器保持一致。
e) 完整的语法支持。支持除了 with {} 和 namespace 之外的所有语法。(namespace不是C#的namespace, as3中类似的是package。)因为with实在是没法搞,玩js的大家都知道蛤蛤。
f) IDE。由于语法等和AIR完全一致,所以大体上可以直接使用flash develop。
g) 扩展语法。扩展as3的语法,加入了yield 也就是说,同样试用yield就可以直接返回一个ienumerator,和C#学的:)
h) 支持结构体。准确的说,是可以将.net的结构体对象链接过来在脚本中使用。大致上是一个nullable的结构体。
i) 操作符重载。为了更好的链接.net的一些类库,特制作操作符重载。
六、还未完成的部分:
a) 目前需要手工将.net类链接到脚本对象,这部分的代码生成器还需开发
b) 目前没有将编译的结果序列化 / 反序列化。这部分工作难度不大,但需要细心和时间。完成后,就可以将编译和执行分离了,每次执行只需加载二进制字节码执行即可,不必编译。
七、解释器能干什么
嗯,这还用问吗?纯.net2.0,连linq都没有使用,不依赖任何第三方库的脚本解释器,自然是可以嵌入Unity了,而且有静态编译检查,还特意加入了yield和结构体,就是为这个做准备的
八、游戏项目从开发到跑路
我们项目开全部完成了,除了UI改了一遍又一遍。。我们满心期待的等着项目上线,总算可以看到结果了。然后端午节过后的中午,BOSS召集我们宣布,他关门了!跑路了!跑路了,跑路了 其实我当时心里想的是,好吧,历经数年没日没夜的加班日子,我终于可以休息了。
九、休息中
。。写点什么吧。嗯。正好又一段时间休息,继续完善脚本解释器。展示一些执行结果
下面展示的是和现有IDE的结合。
下面展示的是yield语句。
下面是结构体TimeSpan的一些链接:展示了操作符重载
下面展示的是getter,setter。没错as3是支持属性的
package {[Doc]public class FuncTest{} } //下例定义 Team 类。Team 类包括用于在该类内检索和设置属性的 getter 和 setter 方法: class Team { var teamName:String; var teamCode:String; var teamPlayers:Array = new Array(); public function Team(param_name:String, param_code:String) { teamName = param_name; teamCode = param_code; } public function get name():String { return teamName; } public function set name(param_name:String):void { teamName = param_name; }} //在脚本中输入下面的代码: var giants:Team = new Team("San Fran", "SFO"); trace(giants.name); giants.name = "San Francisco"; trace(giants.name); /* San Fran San Francisco */
十、最后
a) 解释器目前进度的代码地址:https://github.com/asheigithub/ASTool 欢迎测bug
b) 也可直接下载编译好的demo
c) 有心情的话,后续记录一些开发中的心得。嘛,看找工作的情况了,如果一直失业的话恐怕也不会太有心情哈哈
转载于:https://www.cnblogs.com/ashei/p/6944483.html
记一个脚本解释器的开发相关推荐
- 记一个简单的保护if 的sh脚本
记一个简单的保护if 的sh脚本 真是坑爹,就下面的sh,竟然也写了很久! if [ `pwd` != '/usr/xx/bin/tomcat' ] thenecho "rstall is ...
- 记一个绘制态密度与能带的matlab脚本
题记 记一个能带与态密度绘制的脚本,用于处理p4VASP提取的数据后的数据(需要p4vasp的可以在我博客里找) 用法 能带数据需保存在名为band.dat的文件里 态密度数据保存在名为dos.dat ...
- 《游戏脚本的设计与开发》-1.1 读取和解析一个脚本文件
上一篇<游戏脚本的设计与开发>-序中我介绍了游戏脚本的基本概念和准备工作,本篇来说说具体如何解析一个脚本 所谓解析脚本,就是按照自己定义的语法,将每一个脚本命令还原成不同的代码逻辑进行执行 ...
- 开发环境 -- 在linux中写一个脚本拷机
遇到一个问题,x86板卡通过PCIE外接网卡芯片82599,某一块板子过一会出现网络不通的问题,排查发现某一时刻系统出现如下异常: [ 1250.888189] Uhhuh. NMI received ...
- 一个WEB应用的开发流程
转载:http://www.51testing.com/html/56/n-3721856.html 先说项目开发过程中团队人员的分工协作. 一.人员安排 毕业至今的大部分项目都是独立完成,虽然也有和 ...
- python 工业软件开发_记一次工业软件开发经历
项目概述 项目背景:工厂表面处理产线项目 b司接了a司一条表面处理产线的项目,包含硬件及软件,由于现在b司做的软件难用且数据难以查找,a司不满意验收不通过,款项没有结清.所有b司找到我们,希望我们能帮 ...
- 蹭一波热度:记微信跳一跳辅助线开发
蹭一波热度:记微信跳一跳辅助线开发 场景 前两天下午三点多时候,老板突然一个电话过来:"那个小甘啊,最近微信跳一跳小游戏很火啊,我们现在已经有了全自动脚本了,现在需要一个提升用户参与感的辅助 ...
- 1.一个WEB应用的开发流程
先说项目开发过程中团队人员的分工协作. 一.人员安排 毕业至今的大部分项目都是独立完成,虽然也有和其他同事协作的时候,但自认为对团队协作的了解和认知都还有所欠缺.很清楚团队协作的重要性,但尚未有很好的 ...
- 如何使用c语言写脚本解释器,脚本解释器编写思路
[背景] 平时喜欢写游戏脚本,经常用按键精灵,在物理机上用还是很方便好用的,但有时多开挂机,游戏又有驱动保护时,就得在虚拟机里面挂.这时按键的资源占用大问题就很突出.在VM里面运行按键经常是超慢镜头, ...
最新文章
- Luogu5369 [PKUSC2018]最大前缀和
- 基于暗通道优先算法的去雾应用Matlab
- java 比较算法_JAVA排序算法实现和比较:冒泡,桶,选择,快排,归并
- activex控件 新对象 ocx 初始化_Office已经支持64位的树控件Treeview了
- C语言综合期末作业,内蒙古农业大学2010年期末c语言综合作业.doc
- atm取款机的简单程序代码_LeNet:一个简单的卷积神经网络PyTorch实现
- kubernetes pv-controller 解析
- 基于matlab的简易诊断系统,基于matlab的图像识别
- php汉字笔顺生成,【已测源码】php开发汉字笔顺字帖在线生成器程序源码
- 网络RJ45接口详解
- 中国节能装备与产品市场“十四五”规划及2035年远景目标建议报2022-2028年
- MATLAB中Spline插值使用记录
- 原生安卓X86 TV安装配置
- 论文阅读--异常检测中实时大数据处理的研究挑战
- 千万别惹程序员,否则会在代码注释里,告诉这家公司有多坑
- html入门、结构、标签、列表、表格
- 程序员如何防止加班猝死
- 每天一道Java编程01-厚度为0.0001米的纸折叠多少次可以折成珠穆朗玛峰8848米的高度?
- MathorCup大数据挑战赛第一届A题-移动通信基站流量预测赛题解析
- 专访SegmentFault开发团队:垂直问答社区的架构升级
热门文章
- 【CXY】JAVA基础 之 List
- 手游联运平台是怎样开发的?
- 初识AS3(十)——加载外部文件进度…
- Unable to negotiate with 192.168.XX.XX port XXXX: no matching key exchange method found.
- “大数据应用场景”之隔壁老王(连载四)
- gif一键抠图 在线_remove.bg 自动抠图、一键去除图片背景的免费在线工具
- 如何用Python检验线性回归的假设是否满足
- creo绘图属性模板_creo制作工程图模板教程
- STM32驱动12bit AD TLC2543(I/O模拟方式)
- 免费微信小程序,发一发打造微信小程序Saas营销服务移动工具箱