模块化编程里,经常需要将一些常用的函数封装在一个文件里,这样其他文件需要调用其中的函数时,直接加载这个文件,就可以调用该文件里的函数了,例如在C 语言里有#include预编译指令可以包含...

    模块化编程里,经常需要将一些常用的函数封装在一个文件里,这样其他文件需要调用其中的函数时,直接加载这个文件,就可以调用该文件里的函数了,例如在C 语言里有#include预编译指令可以包含其他模块的头文件,PHP语言里可以使用include或require函数来加载其他的php脚本,从而实 现代码的重利用。本节v0.0.20版本就通过实现inc指令来加载其他的zengl脚本文件。

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

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

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

    v0.0.20版本,该版本实现了inc加载包含其他代码文件的功能。
   
    在main.c中增加了inc的关键字的token,另外增加了一个FileStackList的栈用于存放各种inc包含的文件信息。当遇到inc关键 字时,会调用push_FileStack将当前的source(该全局变量存放有当前的文件名,文件句柄等信息)以及当前读取的行列号信息压入栈中,然 后根据inc后面的文件名字符串,设置新的文件信息到source中。这样,接下来的getToken函数就会根据新的source信息来读取新加载文件 里面的token,从而实现加载新文件的功能。
    当加载的文件里的token符号信息被解析完后,会调用pop_FileStack将栈中原来的文件信息恢复到source中,并恢复之前的行列号信息。所以就可以继续之前的解析。
    原来的语法树节点信息里只有行列号信息,现在因为会加载新的文件,所以节点信息里面增加了filename字段来保存某节点所在的文件信息。
    symbol.c的insert_LineCol函数在插入行列号的同时,还会插入对应的文件名信息。
    该版本还修复了上一个版本中的def关键字的BUG,将global.h中的DefTable_Member(存放def定义的常量宏信息)的name字 段和valstr字段替换为nameIndex和valIndex,原来的name和valstr直接存放的是stFactors_for_def里的字 符串指针信息,但是当stFactors_for_def空间装满后,发生zl_realloc时,原来name和valstr里存放的指针信息就会变得 无效,随时会被别的数据覆盖掉。
    所以使用nameIndex和valIndex保存字符串信息在stFactors_for_def里的索引信息,当需要时,再通过get_factor_ptr_ForDef函数和索引信息来获取具体的字符串信息。
    为了测试inc,将21点游戏game_21_point.zl里的use和def信息保存在game_21_def.zl里,将函数保存在 game_21_fun.zl里,然后在game_21_point.zl中通过inc来加载game_21_fun.zl,并在 game_21_fun.zl中通过inc来加载game_21_def.zl,从而将原程序细分为3个文档。
    inc加载文档的路径信息是相对于原文档的,例如如果test.zl和test2.zl在同一个目录中,那么,test.zl要加载test2.zl就直 接inc 'test2.zl';就可以了,如果test2.zl在test.zl所在目录里的test子目录中时就需要inc 'test/test2.zl';
   
    其他详情请用 git log -p 或 gitk 来查看。
   
    作者:zenglong
    时间:2012年5月7日
    官方网站:www.zengl.com

    下面是本节的game_21_point.zl文件的代码:

inc 'game_21_fun.zl'; //加载游戏函数脚本

android[Name]='Android';
user[Name]='User';
user[Money] = android[Money] = 2000;
print 'user:'+user[Name];
turn_num = 0;
input = 's';
blfirst = TRUE;

for(;(!blfirst && input=='r') || (blfirst && input=='s');)
    if(blfirst)
        printf('if you want start ,type "s" \r\n\t');
    else
        printf('if you want play again ,type "r" \r\n\t');
    endif
    input = read();
    if((!blfirst && input=='r') || (blfirst && input=='s'))
        myGameStart(); //调用game_21_fun.zl里的游戏执行函数。
        if(blfirst)
            input = 'r';
            blfirst = FALSE;
        endif
    endif
endfor //end program!

    下面是
game_21_fun.zl中和inc相关的部分代码:

inc 'game_21_def.zl'; //加载def宏定义文件

/*
  myAddPoker函数定义,给玩家和机器人发牌的函数。
*/

fun myAddPoker(array,poker)
    count = &array[PokerCnt];
    return array[TotalPoker] += array[Pokers,count++] = poker;
endfun
..................................... //省略N行代码


    下面是game_21_def.zl文件:

use builtin; //加载内建函数模块。
def TRUE 1;
def FALSE 0;
def Name 0;//用户或android庄家的昵称。
def PokerCnt 1; //用户或android获取的扑克牌数目。
def Pokers 2;//存放获取的扑克牌数组。
def TotalPoker 3;//扑克牌的总点数。
def Money 4;//用户或android的当前资金。

    文件加载原理的简单图例如下:

    从上图可以看出game_21_point.zl在准备inc加载game_21_fun.zl前,先将自己的文件信息 (game_21_point.zl的文件名,文件指针,当前文件的行列号等)压入FileStackList全局变量所代表的堆栈中,然后词法扫描器进 入game_21_fun.zl文件进行词法扫描,在game_21_fun.zl中inc准备加载game_21_def.zl前,同理先将 game_21_fun.zl压入堆栈,然后进入game_21_def.zl进行扫描,当game_21_def.zl扫描结束后,将栈中保持的 game_21_fun.zl的文件信息以及之前的行列号信息弹出,从而恢复game_21_fun.zl的扫描,该文件扫描结束后,最后将 game_21_point.zl文件信息弹出,从而恢复game_21_point.zl的扫描,这样就将三个文件都扫描完毕。

    至于本节的21点游戏的玩法和脚本的解析在前面的章节中已讲解过,这里就不多说了。

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

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

    对于windows用户,请确保在项目属性的配置里,命令行参数配置的是game_21_point.zl(对于zengl_lang_v0.0.20的项目)或game_21_point.zlc(对于zenglrun的项目)。
    另外对于vs2008的用户,我在项目属性里:[配置属性>>>>C/C++ >>>> 高级] 部分设置了禁用特定警告:4013,4715,4996 ,这几个警告会显示一些某某函数是非安全的函数,或者函数没有返回值等,这里禁用掉,防止出现过多的警告。另外还有个警告是显示某某变量没被使用过的, 这个警告我没禁用,可以不用管它。我最开始是
在Linux系统中使用eclipse+CDT插件以及gcc等开发的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 builtin.o文件
删除。

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

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

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

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

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

上下篇

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

上一篇: zengl编程语言v0.0.19 def宏定义

相关文章

zengl v1.3.2 编译静态库 Bug和向下兼容处理

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

zengl编程语言v1.0.0图形版21点扑克游戏,do,while循环结构

zengl编程语言v0.0.6创建小型计算器

zengl v1.4.1 及zengl_SDL新增的Android工程

zengl v1.9.0 增加self特殊类名,增加zenglApi_SetDefLookupHandle等接口