上一节实现了函数的定义和使用,但是函数中还只能使用局部变量,无法使用函数外的全局变量,而且函数中不能执行到一半就直接返回,这一节通过实现 global关键字来引用函数外的全局变量(这种方...

    上一节实现了函数的定义和使用,但是函数中还只能使用局部变量,无法使用函数外的全局变量,而且函数中不能执行到一半就直接返回,这一节通过实现 global关键字来引用函数外的全局变量(这种方法和php的类似),并且通过实现return关键字来实现在函数执行过程中就可以直接返回,从而实现 更灵活的流程控制结构。

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

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

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

    v0.0.13版本,该版本为fun函数添加了global和return关键字。
   
    通过global关键字可以让函数访问函数外的全局变量(和php类似),通过return可以在函数中直接返回。在parser.c中的 ScanFunArg_Global函数中也同时扫描global标注的全局变量,并将这些变量在函数局部变量的hash表中标注为 LOCAL_GLOBAL,这样在lookupSym查找变量时如果在函数中碰到LOCAL_GLOBAL标记的变量,则在全局变量hash表中查找,找 到就直接返回全局变量字符串,没找到就在全局hash表中加入一个全局变量。
    assemble.c中遇到return关键字时,先将return的子节点对应的表达式转为汇编代码,最后再输出"RET"指令。这样在运行时,碰到return语句时,只要执行完return语句,函数就返回了。
   
    作者:zenglong
    时间:2012年3月22日
    官方网站:www.zengl.com   


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

    下面通过本节的test.zl测试脚本的例子来说明(下面的注释仅在此起说明作用,原文件中并没有,因为注释的功能要在后面的版本中才实现):

fun test(arg)  //定义一个名为test的函数,该函数有一个形参(形参指函数定义时的参数名称,需要跟函数调用时的实参区分开来)
  global glTest,glTest2; //通过global关键字来引用函数外定义的全局变量glTest和glTest2,全局变量可以定义在文件的任意位置,不需要像C语言那样一定要定义在函数前面。
  print 'glTest is ' + glTest;  //将global引用的全局变量glTest打印输出。
  print 'glTest2 is ' + glTest2; //将global引用的glTest2全局变量打印输出。
  if(arg == 'return')  //如果函数的参数是字符串return的话,就打印下面的语句,然后直接return返回。
    print "your arg is 'return' so we'll return immediately!"; //这段英文的意思是"你的参数是return,所以我们将马上从函数中返回。"
    return;  //函数马上返回,而不继续执行下面的语句。
  endif
  print 'your arg is ' + arg + " and we'll exit function normally"; //如果不是return字符串为参数,就打印这条语句,并以常规方式(即遇到endfun时)返回。
endfun

glTest = ' hello world!';  //设置glTest全局变量的值。
glTest2 = ' welcome to zengl.com'; //设置glTest2全局变量的值。
test('return');  //'return'字符串为参数调用test函数。
test('go your own way 
            ----- DANTE ALIGHIERI'); //调用test函数打印自定义的字符串信息,这句英文是诗人但丁的名句:走自己的路。

    test.zl脚本生成的test.zlc汇编文件如下(下面的注释仅在此起说明作用):

0 JMP 24;  //函数定义的部分在前面一节已经说的很详细了,所以重复的部分就不提了,就注释一些和本节有关的地方。
1 FUNARG 1;
2 MOV AX "glTest is ";
3 MOV BX (0);  //因为有global关键字,所以经过语法扫描,汇编输出后,glTest全局变量转为了内存地址(0),没有arg,loc为前缀的内存地址都是全局变量的内存地址。
4 PLUS;
5 print AX;  //将第一个全局变量打印输出。
6 MOV AX "glTest2 is ";
7 MOV BX (1);  //(1)表示glTest2第二个全局变量。
8 PLUS;
9 print AX;  //将第二个全局变量打印输出。
10 MOV AX arg(0); //以arg为前缀,表示函数的参数。
11 MOV BX "return";
12 EQUAL;  //if语句将参数和return字符串进行比较。
13 JE 17; //JE以前提到过,就是当AX为0时,发生跳转,即EQUAL比较结果为FALSE时,也就是参数不为return字符串时。需要和汇编语言的JE区分开。
14 print "your arg is 'return' so we'll return immediately!";
15 RET; //return语句转为RET指令,从而使得函数执行到这里就可以直接返回。
16 JMP 17;  //endif会转为JMP指令。虽然有点多余,因为不会执行这条指令,不过zengl脚本语言前期暂时不会考虑脚本执行的指令优化问题。
17 MOV AX "your arg is ";
18 MOV BX arg(0);
19 PLUS;
20 MOV BX " and we'll exit function normally";
21 PLUS;
22 print AX;  //打印出函数最后一条语句。
23 RET;  //以endfun常规方式结束函数。
24 MOV AX " hello world!";
25 MOV (0) AX;  //给全局变量glTest赋值
26 MOV AX " welcome to zengl.com";
27 MOV (1) AX;  //给全局变量glTest2赋值
28 PUSH ARG;  //从这里开始准备调用test函数
29 PUSH LOC;
30 RESET ARGTMP;
31 MOV AX "return";  //将return字符串作为参数赋值给AX
32 PUSH AX;  //将参数压入栈。
33 PUSH 37;
34 MOV ARG ARGTMP;
35 RESET LOC;
36 JMP 1;     //跳转到test函数执行。
37 PUSH ARG; //以下同理。
38 PUSH LOC;
39 RESET ARGTMP;
40 MOV AX "go your own way 
            ----- DANTE ALIGHIERI";
41 PUSH AX;
42 PUSH 46;
43 MOV ARG ARGTMP;
44 RESET LOC;
45 JMP 1;
46 END;

    以上就是函数中使用global关键字和return关键字的用法。如果熟悉PHP语言的话,应该对global和return很熟悉。

    其他更多的细节部分,请结合源代码,git log -p 和前面提到过的开发调试工具进行分析。

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

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

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

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

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

    最后运行 ./zengl test.zl 查看printASTnodes函数打印抽象语法树节点的结果,以及符号表输出的变量信息以及函数信息等。(例如变量的内存地址,以及在源文件的行列号,函数的唯一标识ID等)。
    接着运行./zenglrun test.zlc (注意是.zlc结尾的文件名,因为zenglrun虚拟机只能运行.zlc里的汇编代码)。

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

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

上下篇

下一篇: zengl编程语言v0.0.14加载模块函数

上一篇: zengl编程语言v0.0.12函数的实现

相关文章

开发自己的编程语言-zengl编程语言v0.0.1词法扫描器的实现

zengl v1.8.3 修复ReUse接口缺陷及段错误,完善语法检测,使用Android Studio开发android项目

zengl编程语言v0.0.18初始化数组函数

zengl v1.8.1 修复bug

zengl编程语言v0.0.4构建多语句抽象语法树

zengl v1.6.0-v1.7.1, zenglServer v0.1.0