6.1 制作Diksam Ver 0.1语言的基本部分
此前的章节中,我们已经制作了一个无变量类型、通过分析树执行的语言crowbar。从本章开始,我们来制作一个静态类型并通过字节码执行的语言Diksam。
Diksam这个名字的由来可不是“像Perl一样”的双关语,而是我钟爱的一种红茶的名字。既然有了咖啡语言Java,再来一个红茶语言也无伤大雅吧。Diksam是一种口味浓郁的阿萨姆红茶,一般都用来做成奶茶。我很喜欢直接去感受它的那份浓郁。
6.1.1 Diksam的运行状态
前面已经提到,Diksam是通过字节码执行的语言。
说起通过字节码执行的语言,以Java为例再合适不过了 [1]。在编写Java代码时,要先编写源程序,再通过 javac编译成保存着字节码的 class文件 。
Diksam语言不会生成像class文件那样的字节码文件。编译器解析源代码并生成分析树后,直接在内存中生成字节码。这些在内存中生成的字节码,会由Diksam的虚拟机DVM(Diksam Virtual Machine)运行。
Diksam不生成字节码文件的做法,免去了考虑文件格式和文件编码的麻烦。当然,除了我的个人考量外,对于用户来说也省去了逐个编译源文件的工夫。这种做法在代码量非常巨大的情况下,每次启动程序时花费在编译上的时间都会让你等得不耐烦。当然,程序不是很大的时候还是很方便的。
另外,在实现中,DVM将完全信任编译器生成的字节码。所谓“完全信任”就是说,即使是含有恶意代码的字节码DVM也会加载执行,不过这可能会使DVM崩溃。
Diksam是一种静态类型语言,在编译时就已经决定了所有变量和表达式的数据类型。因此,在运行时就不需要再检查变量的数据类型了。比如,为了在堆中存储一个字符串, string型变量会(间接的)保存指向该字符串的指针,但如果存储的整数型局部变量被当作string型引用时,DVM就会因为找不到正确的引用地址而崩溃。
但也是有相应对策的,比如在网页中运行的Java Applet,由于必须执行来自外部的(不能被信任的)字节码,因此绝对不允许发生上述情况。所以Java在加载字节码的时候会执行 验证器 (verifier)程序,以验证加载的字节码是否正确。
但是,制作验证器非常复杂,而Diksam还只是直接在内存中执行字节码的语言,所以……不好意思,这里我要跳过啦。
6.1.2 什么是Diksam
首先,我们制作的Diksam是Diksam book_ver.0.1版本,实例请参考代码清单6-1。
代码清单6-1 fizzbuzz_0_1.dkm
int print(string str);
int i;
for(i = 1; i <= 100; i++){
if(i % 15 == 0){
print("FizzBuzz\n");
}elsif(i % 3 == 0){
print("Fizz\n");
}elsif(i % 5 == 0){
print("Buzz\n");
}else{
print("" + i + "\n");
}
}
6.1.3 程序结构
与crowbar相同,Diksam中也有顶层结构,并且允许在函数外编写代码。程序从顶层结构的开头开始执行。
Diksam用下面的方式定义函数。与C语言基本相同。
int func(int a, double b){
int local_varible;//声明局部变量
local_varible = a + b;
print("local_varible.." + local_varible + "\n");
return local_varible;
}
6.1.4 数据类型
Diksam book_ver.0.1可以使用以下四种数据类型。
boolean(布尔)型。可以赋 true或 false值。
int(数字)型。
double(浮点)型。 int和 double混合进行运算时,参与运算的 int类型会自动扩展为 double类型。
string(字符串)型。当字符串在 +号左边时,右边的部分会自动转换为 string型。
double这个关键字在C语言中的意思是 双精度 浮点数,这里的double以 float(单精度浮点数)的存在为前提。但是,Diksam 中并没有 float,尽管它叫作 double 型,也纯粹是为了照顾C 和Java 语言的使用者而已。
6.1.5 变量
如前所述,Diksam是静态类型的语言,变量必须事先声明,声明方式和C语言一样,如下所示:
int a;
在函数内声明的局部变量只可以在声明变量的程序块内引用,函数外声明的变量是全局变量 A。
现阶段在Diksam中变量声明也是一种语句,可以写语句的地方就可以声明变量。
在C语言中变量必须声明在程序块的开头部分,C++和Java取消了这个限制。虽然这被大多数人认为是个改进,而且Diksam也是这么做的,但是老实说我不喜欢这个改进。我认为,在代码的任意位置都可以声明变量会使一个函数渐渐变得冗长。因此,也许可以趁现在 [2]改正过来。
Diksam中声明变量的方式和C等语言相同,采用类型 变量名;的形式。这种形式对于处理数组和函数类型的变量来说很麻烦。如果引入预声明关键字var,那么像下面这样把数据类型写在后面的语法(Pascal、Ada、ActionScript都是这种方式)处理起来会简单一点。
var a:int;
但是,习惯了C或者Java语言的程序员肯定会说,还是类型 变量名;这样的方式写着更顺手。为了照顾大家,Diksam还是采用了大家比较习惯的方式。实际上,在拙著《征服C指针》(人民邮电出版社,吴雅明译)中,整本书都在表达对C语言变量声明语法结构的不满。在那本书中写了那么多不满,现在却变成了墙头草,肯定要被嘲笑了。
言归正传,类型 变量名;形式的语法如果包含了数组和类(class),制作语法分析器的时候就会出现各种问题,幸亏Diksam在语法规则上花了不少功夫才避免了这些问题。这个话题我们在后面的章节中详细说明。
变量 初始化语句(initializer)的方式也与C语言相同。
int a = 10;
6.1.6 语句和流程控制
流程控制语句有: if(elseif、 else)、 while、 for、 break、 continue、return。
含义和crowbar相同,花括号也不可以省略。另外, break和 continue也可以使用标签(和Java一样)。
6.1.7 表达式
Diksam中可以使用的运算符及其优先级,如表6-1所示。
表6-1 在Diksam中可以使用的运算符
函数调用被定义为运算符,说明函数也是一种表达式。
这就意味着,在crowbar中可以像C语言的函数指针那样把函数赋值给变量,但是,实际上crowbar并不能声明保存函数的变量,因此也就没办法把函数赋值给变量。
6.1.8 内建函数
Diksam在一开始就准备了内建函数。Diksam book_ver.0.1中的内建函数一览表如下所示。
完毕!
只有一行的一览表,很糗是吧。
6.1.9 其他
注释可以使用C 风格的 /~/,也可以使用C++ 从 // 开始到本行结束的风格。