一个语言的诞生(Prologue)
一直琢磨着自己实现一个脚本语言。因为懒,直到最近才动手。
构想中的语言语法类似ActionScript,支持GC、闭包、Lambda表达式、协程、尾递归优化、迭代器/生成器、允许(编译期)强类型绑定的可嵌入式动态脚本语言。基础类型支持NULL、布尔型、32位整型、64位长整型、双精度浮点数、元组、列表、字典、闭包、协程、对象、用户数据等。
一些动态脚本语言已经舍弃了整数类型,所有数学计算都是用双精度浮点数实现,比如 Javascript和Lua。不过我觉得有时候整型在数据处理的时候还是有必要的,特别是64位长整型,如果用double表示的话可能会造成精度丢失。所以最后决定保留两种整数类型。另外,几乎所有的动态脚本语言都不支持无符号整型,甚至连JVM都不支持无符号整型。虽然在加减法操作和位运算操作时,有无符号并没有什么影响,但是乘除法的运算指令是区分有无符号的,分别是 mul、div 和 imul、idiv。所以最后还是决定向C#学习,引入无符号整型。
虚拟机决定采用寄存器式的。类似Lua,而不是传统CPython、JVM那种堆栈式的。虽然实现难度大,但是寄存器式虚拟机效率要比堆栈式的高出不知道多少。
GC决定采用Mark-Compact算法。该算法的堆内存是紧凑分布的,空间线性递增分配的,比一般的HEAP实现要高效很多,还能实现对象分代。.NET、JVM、V8都使用了类似的算法。缺点是必须事先申请一个超大的内存保留空间,这个大小就是虚拟机堆可用空间的最大尺寸,如果使用的内存超过了这个大小就“out of memory”了。虽然可以往大了申请,但是如果进程内存申请不到那么大连续空间就完蛋了。而且作为一种可嵌入式的语言,申请那么大一块连续空间那简直是不给宿主留活路啊。现在想到两种解决方案:一种是提供两套HEAP实现和GC算法;一种是提供一个参数来控制申请最大保留内存的大小。考虑到实现难度,采用第二种方案。
再说一下之前提到的“(编译期)强类型绑定”。所谓的动态脚本语言不都是弱类型的么,哪来什么“强类型绑定”啊。其实这里的“强类型绑定”并不是像JVM和.NET那样静态类型,对象模型仍然是动态类型的,只不过会在编译期进行类型检查而已。之所以如此设计是因为我个人对动态语言代码中无法避免的频繁类型深检查感到恶痛绝。另外有一个好处就是对于编译期确定类型的变量运算可以进行指令优化。比如两个整型相加可以直接使用iadd指令,对于两个浮点数相加可以直接使用fadd指令。