switch...case结构是c语言中比较常用的结构,和if条件选择语句一样,也是用于多条件进行分支判断用的,不过它的效率比if语句要高些,因 为if语句是一个一个条件的依次进行判断,当需要比较的条...

    switch...case结构是c语言中比较常用的结构,和if条件选择语句一样,也是用于多条件进行分支判断用的,不过它的效率比if语句要高些,因 为if语句是一个一个条件的依次进行判断,当需要比较的条件过多时效率就会下降,switch...case则可以快速找到要执行的代码块,所以本节 v0.0.22版本就为zengl脚本语言实现了switch...case结构。

    本节v0.0.22版本的源代码的下载地址为:http://pan.baidu.com/share/link?shareid=217687&uk=940392313  (此为百度云盘的共享链接地址) ,访问该地址可以看到三个文件:zengl_lang_v0.0.22_forXP.rar (XP系统下的vs2008解决方案和源代码), zengl_language_v0.0.22_forLinux.tar.gz  (Linux系统下的源代码和makefile) ,v0.0.22-v0.0.21-diffs.txt  (v0.0.22和v0.0.21两个版本之间的代码变化情况)。

   
SourceForge.net上的仓库地址为:https://sourceforge.net/projects/zengl/files/   从里面可以看到各个版本的代码压缩包,比如本节的zengl_lang_v0.0.22_forXP.rar zengl_language_v0.0.22_forLinux.tar.gzv0.0.22-v0.0.21-diffs.txt

   
先来看下本版本的描述(在linux代码包里的usage.txt里有这段描述,在目前几个带有git版本的....-diffs.txt和git log中也有这段描述):

    v0.0.22版本,该版本实现了switch case的结构。
   
    在main.c中添加了switch,case,default,endswt四个和switch结构有关的token,还添加了switch case会用到的':'的token。
    parser.c中增加了switch case结构的语法树解析,解析过程和if,for,fun的差不多,同时修复了原来的if,for,fun解析的BUG,原来的解析过程无法对空语句的 分号进行解析,例如test=2;;第二个分号就无法解析成功,现在采用新的解析方式就可以跳过空语句。
    ld.c中也相应添加了switch所需的相关链接替换的操作。
    assemble.c将switch转化为对应的汇编指令,LONG指令用于存储数据用的,SWITCH指令则根据switch的操作数转化为跳转地址,跳转到目标代码去。
    在run.c中switch case结构会为每个case创建一个跳转表项,当case的值连续时,那么查找的数就会直接转为表项的索引,并从该索引对应的表项中获取目标代码的地 址,并跳转执行。当case的值非连续的时候,会采用一种算法来快速比较表项的值,从而找到需要的地址,例如:如果case的值为 1,3,7,8,9,10,100,53的值时,会创建一个1,3,7,8,9,10,53,100这样的从小到大排列的一系列表项,如果此时 switch要找的值为10时,就会先将10和中间的8比较,结果比8大,就再和8与100中间的10比较,比较结果相等再从10对应的表项中提取出地址 值,这样就找到需要的地址了,这种方法就不需要一个一个的比较。如果是1,2,3,4,5,6这样连续的值,那么如果switch要找的是3,就直接和1 相减得到表项的索引,这时无须比较就可以得到地址。得到地址后,直接跳到相应地址执行即可。
    在assemble.c中完善了break关键字的代码生成,当break在switch中时,会生成跳转到endswt的指令,当break在for中时则跳转到for的后面。continue还是跳转到最近的for语句的开头。
   
    其他的改动请用git log -p 或 gitk等软件来查看。
   
    作者:zenglong
    时间:2012年5月20日
    官方网站:www.zengl.com


    先来看下本节的测试脚本test.zl (下面代码中,棕色的注释是这里额外添加的,绿色的注释才是原文件中有的。) :


use builtin;  //加载内建模块

print 'please input a num for test!';
test = read(); //read()函数获取用户的输入
switch(test) //测试switch...case
case 1: //如果test的值是1,则执行下面的代码
    ;
    print 'test is one';
    ;; //测试空语句
    print 'test is one end';
    break;  //break会直接跳到endswt后继续执行。
case 7:
    print 'in case seven!';
    break;
case 2:  //如果test的值是2或3,则都会执行下面的代码
case 3:
    print 'test is three';
    break;
default: //如果test值既不是1,7也不是2和3,那么就执行下面的打印语句
    print 'your input '+test + ' is invalid!';
endswt

print 'please input a num for input';
input = read();
if(input < 3)    //if后面可以直接接elif,即表示if和elif之间是空语句。
elif(input==3)
    ;; //测试空语句
    print 'you input is three';
    ;
    print 'input three end';
else
    print 'it\'s last else';
endif

for(i=1;i<3;i++)  //测试for循环中的空语句。
    ; //测试空语句
    print 'for i :'+i;
    ; print 'for i:'+i+'end';
endfor

fun test()  //测试fun函数定义中的空语句
    print 'i in test()';
    ;; //测试空语句
    print 'test() end';
    ; //测试空语句
endfun


    再来看下test.zl对应的汇编代码文件test.zlc中和本节switch...case相关的代码片段(下面的代码中棕色的注释是这里为了说明,额外添加的,绿色的注释才是原文件中有的):

........................................  //省略N行代码
9 CALL "read";  //调用read函数读取用户输入
10 MOV (1) AX; //将输入的结果设置到全局内存1即test变量中。
11 MOV AX (1); // <test.zlc> 5  //将test值赋值给AX,准备进行switch...case的比较工作。
12 SWITCH 29 22; 
//SWITCH指令后第一个数是switch...case跳转地址表的汇编代码位置,如下面的29 LONG 15 1就是跳转地址表,所以是29 ,后面的22指如果case判断都不匹配的时候,将要跳转的默认地址,即下面的22 MOV AX "your input "; 汇编代码处。
13 LONG 4 4;  //SWITCH指令后第一个LONG后面的两个4指在29位置处的跳转地址表里一共有四个元素即1,2,3,7四项。
14 LONG 1 7;  //SWITCH指令后第二个LONG后面,第一个数1指case中最小的值为1,最大的值为7.
15 print "test is one"; // <test.zlc> 8   //第一个case需要执行的指令
16 print "test is one end"; // <test.zlc> 10
17 JMP 33; // <test.zlc> 11  //第一个case执行完后的break,直接JMP到地址表后即endswt后。
18 print "in case seven!"; // <test.zlc> 13  //第二个case需要执行的指令
19 JMP 33; // <test.zlc> 14 //第二个case的break语句。
20 print "test is three"; // <test.zlc> 17  //第三个case需要执行的指令
21 JMP 33; // <test.zlc> 18  //第三个case的break语句。
22 MOV AX "your input "; // <test.zlc> 20  //当所有的case都不匹配时要执行的default指令。
23 MOV BX (1);
24 PLUS;
25 MOV BX " is invalid!";
26 PLUS;
27 print AX;
28 JMP 33;  //default执行完后也需要JMP跳过地址表后继续执行。
29 LONG 15 1;  //当前switch...case的跳转地址表的起始项,LONG后的15指如果test要比较的值为后面的1的话,就跳转到15处的汇编代码处执行,即第一条case需要执行的汇编代码。
30 LONG 20 2;  //当test的值为2时,则跳转到20处的汇编代码执行。
31 LONG 20 3;  //同上
32 LONG 18 7; 
//同上
........................................  //省略N行代码

    具体的C文件代码部分,请结合源代码中的注释,再加上git工具以及vs2008或者eclipse+CDT插件或者gcc,gdb等工具进行分析。

   
最后还是老生常谈的话题:
    windowsXP压缩包中的代码包括test.zl等
测试脚本都是采用GBK的编码,Linux压缩包中的代码包括测试文件以及git里的信息都是UTF8的编码,所以如果哪些地方出现了乱码,请自行调整。

    对于windows用户,请确保在项目属性的配置里,命令行参数配置的是test.zl(对于zengl_lang_v0.0.22的项目)或test.zlc(对于zenglrun的项目)。
    另外对于vs2008的用户,我在项目属性里:[配置属性>>>>C/C++ >>>> 高级] 部分设置了禁用特定警告:4013,4715,4996 ,这几个警告会显示一些某某函数是非安全的函数,或者函数没有返回值等,这里禁用掉,防止出现过多的警告。另外还有个警告是显示某某变量没被使用过的, 这个警告我没禁用,可以不用管它。我最开始是
在Linux系统中使用eclipse+CDT插件以及gcc等开发的zengl ,在我的GCC下面并没有显示过这些讨厌的警告,所以就没处理,不过还好这些警告都无关痛痒,无需理会。
    还有一个地方:VS2008项目中,在[配置属性>>>> C/C++ >>>> 预处理器] 部分都设置了预处理器定义的宏:OS_IN_WINDOWS,另外在zenglrun开启命令行调试配置中还增加了__ZENGL_DEBUG的宏,主要针对zenglrun的项目 ,之所以需要OS_IN_WINDOWS,是因为源代码既要在WINDOWS下编译,又要在LINUX下编译,所以需要通过这个宏来告诉编译器当前的环境 是windows还是linux,在 windows下面,在程序结束时会执行system ("pause");这条语句(vs2008下为了能看到结果,需要暂停,否则就一闪而过,什么都看不到咯。) 而linux系统主要在bash终端下执行,不需要这条语句,而__ZENGL_DEBUG宏主要用来判断是否需要在zenglrun的目标程序中加入调 试代码。

    linux系统下的用户请结合usage.txt的说明,先运行make clean 将原来生成的zengl zenglrun zengldebug和 main.o parser.o assemble.o ld.o func.o run.o debug.o symbol.o builtin.o文件
删除。

    再运行make all (单纯的make只能生成zengl,所以需要make all来生成所有的目标)

    生成zengl zenglrun
zengldebug 和 main.o parser.o assemble.o ld.o func.o run.o debug.o symbol.o  builtin.o。(在生成过程中如果出现一些警告,暂不管他)

    最后运行 ./zengl test.zl
查看printASTnodes函数打印抽象语法树节点的结果,以及符号表输出的变量信息以及函数信息等。(例如变量的内存地址,以及在源文件的行列号,函数的唯一标识ID等)。
    接着运行./zenglrun test.zlc

    如果要调试脚本则使用./zengldebug test.zlc(注意是.zlc结尾的文件名,因为zenglrun虚拟机和zengldebug调试器只能运行.zlc里的汇编代码)。

    zengl语言涉及到的很多高级的编译原理都可以在《龙书》中找到。

    最后的最后,如果转载请注明来源 http://www.zengl.com   , OK , 先到这里,休息,休息一下 O(∩_∩)O~

上下篇

下一篇: zengl编程语言v0.0.23,SDL游戏开发,类定义

上一篇: zengl编程语言v0.0.21汇编级调试

相关文章

zengl编程语言v0.0.3构建抽象语法树

zengl编程语言v0.0.20 inc加载脚本

zengl v1.9.1 函数参数可以使用负数作为默认值

zengl v1.2.1 修复函数调用BUG

zengl编程语言v0.0.11实现循环控制结构

zengl编程语言v0.0.5构建符号表汇编代码和虚拟机