v1.0.0版本,该版本修复了函数调用时可能会出现的内存泄漏。增加了while...endwhile结构,do...dowhile结构,并利用 SDL游戏引擎开发了图形界面的21点扑克游戏,改进了命令行界面的21点扑克游...

    v1.0.0版本,该版本修复了函数调用时可能会出现的内存泄漏。增加了while...endwhile结构,do...dowhile结构,并利用 SDL游戏引擎开发了图形界面的21点扑克游戏,改进了命令行界面的21点扑克游戏,改进后的命令行21点扑克游戏可以进行上百亿的运算,默认的整数最大 只能表示40多亿,通过模拟数字的加减时的进位和借位运算实现对上百亿甚至是上千亿数据的处理。
   
    在assemble.c中加入了while和dowhile结构的汇编代码的生成。
    在main.c中加入了while,endwhile,do,dowhile四个关键字的token
    在parser.c中加入了while和dowhile结构的AST语法树的生成。
    在gui_21_point里的是图形界面的21点游戏的脚本源代码和相关的图片和字体等资源文件。
    在console_21_point里的是命令行界面的21点游戏的脚本文件。
    其他的改动请用 git log -p 或 gitk 等软件查看。
   
    作者:zenglong
    时间:2012年7月10日
    官方网站:www.zengl.com

    本节v1.0.0的源代码下载地址为:http://pan.baidu.com/share/link?shareid=225317&uk=940392313  (此为百度云盘的共享链接地址) ,访问该地址可以看到四个文件:zengl_lang_v1.0.0_forXP.rar (XP系统下的vs2008解决方案和源代码),release_v1.0.0_forXP.rar (此为可以在没装任何开发工具的XP系统下运行的发布版程式)zengl_language_v1.0.0_forLinux.tar.gz  (Linux系统下的源代码和makefile) ,v1.0.0-v0.0.24-diffs.txt  (v1.0.0和v0.0.24两个版本之间的代码变化情况)。

   
SourceForge.net上的仓库地址为:https://sourceforge.net/projects/zengl/files/   从里面可以看到各个版本的代码压缩包,比如本节的zengl_lang_v1.0.0_forXP.rar release_v1.0.0_forXP.rarzengl_language_v1.0.0_forLinux.tar.gzv1.0.0-v0.0.24-diffs.txt
   
   本节的压缩包都比较大,最大的有10M多,因为21点游戏图形版里的宋体字体文件就有11M ,上面提到的release_v1.0.0_forXP.rar解压后,得到如下界面:

    zengl.exe是编译脚本文件的,zenglrun.exe是运行脚本中间汇编文件的,zengldebug.exe是汇编级调试脚本用的,前面章节都提到过,这里将编译运行都放在批处理文件里,这样只要双击<编译21点扑克游戏图形版.bat>就可以得到中间汇编代码文件了。21点游戏的脚本文件都放在gui_21_point目录中,生成的.zlc中间汇编码文件也在其中。21点语法树.txt文件是生成.zlc时附带生成的,可以打开该文件查看该脚本对应的语法树结构。
    在生成.zlc文件后(压缩包里默认已经包含了该文件),直接双击<点我运行21点扑克游戏图形版.bat>,即可看到如下界面:

    上面的release压缩包解压出来的文件可以直接在任何XP上运行,即使XP上没装Visual Studio之类的调试工具也可以运行,理论上应该也可以直接在win7上运行,请自行测试。

     本节修复的函数调用时可能出现的内存泄漏的BUG主要在run.c源文件中。在run.c中有如下一段代码:
/*
    释放memblock所管理的内存块,先将refcount减一,当refcount小于等于0时,就循环加递归将内存块包括内存块里的子内存块全部释放掉。
*/

void memblock_free(MEM_LIST * memblock,int * index)
{
    int i;
    if(memblock == NULL)
        return;
    memblock->refcount--;
    if(memblock->refcount <= 0)
    {
        for(i=0;i<memblock->size;i++)
        {
            if(memblock->mem_array[i].idtype == IDSTR &&
               memblock->mem_array[i].ptrIndex > 0)
            {
                zl_free_index(memblock->mem_array[i].val.ptr,
                                &memblock->mem_array[i].ptrIndex);
                memblock->mem_array[i].val.ptr = NULL;
            }
            else if(memblock->mem_array[i].idtype == IDMEM_BLOCK &&
                     memblock->mem_array[i].val.memblock != NULL)
            {
                memblock_free((MEM_LIST *)memblock->mem_array[i].val.memblock,
                                 &memblock->mem_array[i].memptrIndex);
                memblock->mem_array[i].val.memblock = NULL;
            }
        }
        zl_free_index(memblock->mem_array,&memblock->ptrIndex); //没有这条就会内存一直增大,导致内存泄漏。。
        zl_free_index(memblock,index);
    }
}

    上面的memblock_free函数会在每次脚本函数调用结束时用来释放局部变量分配的数组内存块的,一个内存块有两部分组成,一个用来存放实际的数据 (就是上面的memblock->mem_array),还有一个用来管理内存块(就是上面代码中的memblock)。之前的版本只释放了 memblock,没有释放memblock下面的mem_array所以导致内存会一直增大从而导致内存泄漏。本版本修复了此BUG,经测试,在虚拟机 运行脚本时,内存会控制在一个合理的值,不会像以前那样一直变大。

    另外在上一个版本的MouseEvent.zl捕获鼠标事件脚本中有一个BUG:脚本在运行时会持续的占用CPU资源,可以将MouseEvent.zl底部的代码改为如下:
.........................//省略N行代码
for(;!gameover;)
    for(;sdlPollEvent(event)!=NoEvent;)
        //myHandleEvent();
        switch(event.type)
        case EvQuit:
            gameover = TRUE;
            break;
        case EvKeyDown:
            switch(event.keytype)
            case KeyEsc:
                gameover = TRUE;
                break;
            endswt
            break;
        endswt
    endfor
    sdlDelay(10); //设置10毫秒延时。防止持续占用CPU资源
    sdlFillRect(screen,NULL,array(0, 0xFF, 0xFF));
    myHandleEvent(); //将myHandleEvent从上面的for循环放到这里也可以减少CPU的资源占用。
    sdlBlitImg(button.image,clip,screen,button.pos);
    sdlShowScreen();
endfor

myCleanUp();

    下面看下本节命令行下的21点如何处理上百亿的数据的,zengl_lang_v1.0.0_forXP.rar解压后在 zengl_lang_v1.0.0项目源代码目录中有个脚本测试目录console_21_point,里面存放的就是命令行模式的21点游戏的脚本文 件,在其中的game_21_fun.zl中,有如下的脚本:
/*
   两个大整数相减,如180,00000000和160,00000000相减,结果存放在c数组里,在本例中a和c对应同一个数
*/

fun myBigIntMinis(a,b,c)
    if(a[0] == 1) //a[0]第一个元素表示a是正还是负数
        if(b[0]==0) //b[0]代表b是正数还是负数
            a[0] = 0;
            myBigIntAdd(a,b,c); //一个负数减去一个正数,则先将两个数相加,结果再设为负数
            a[0] = 1; //结果符号位设为负
            return;
        elif(b[0]==1) //负数和负数相减,相当于两数相减,再根据结果设置符号位
            a[0] = b[0] = 0;
            myBigIntMinis(a,b,c);
            if(a[0]==0)
                a[0] = 1;
            else
                a[0] = 0;
            endif
            return;
        endif
    elif(a[0] == 0 && b[0] == 1) //正数减负数相当于两数相加
        b[0] = 0;
        myBigIntAdd(a,b,c);
        return;
    endif
    for(i=1;i<3;i++) //确定最终的符号位,e代表a,b中较大的一个,f代表a,b中较小的一个。
        if(a[i]>b[i])
            e = &a;
            f = &b;
            c[0] = 0;
            break;
        elif(a[i]<b[i])
            e = &b;
            f = &a;
            c[0] = 1;
            break;
        elif(i==2)
            return c[0] = c[1] = c[2] = 0;
        endif
    endfor
    borrow = 0;
    for(i=2;i>=1;i--) //两数相减,并进行借位运算。
        c[i] = e[i] - f[i] - borrow;
        if(c[i] >= 0)
            borrow = 0;
        else
            c[i] += UnitMoney; //UnitMoney在本例中为1亿
            borrow = 1;
        endif
    endfor
endfun
   
    通过对上面的函数和其他类似的函数就可以实现对上百亿的大整数进行运算,命令行21点的截图如下:

    最后还是老生常谈的话题:
    windowsXP压缩包中的代码
都是采用GBK的编码,Linux压缩包中的代码以及git里的信息都是UTF8的编码,
本节的图形或命令行界面的21点游戏脚本都是UTF8格式的,所以如果哪些地方出现了乱码,请自行调整(像EditPlus,UltraEdit,linux下的kwrite等都可以调整编 码)。

    对于windows用户,请确保在项目属性的配置里,命令行参数配置的是gui_21_point/sdl_21_point.zl(或者是console_21_point/game_21_point.zl)(对于zengl_lang_v1.0.0的项目)或
gui_21_point/sdl_21_point.zlc(或者是console_21_point/game_21_point.zlc)(对于zenglrun的项目)。另外工作目录设置为../zengl_lang_v1.0.0  ,这样在zengl_lang_v1.0.0下修改了.zl脚本并重新生成.zlc后,就无需再将.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的目标程序中加入调 试代码。
    Release版本的程序是采用/MT多线程运行时编译的,所以可以直接在别的没有安装调试工具的电脑上运行。

    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 sdlLib.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 sdlLib.o。(在生成过程中如果出现一些警告,暂不管他)

    最后运行 ./zengl
gui_21_point/sdl_21_point.zl(或者是console_21_point/game_21_point.zl) 查看printASTnodes函数打印抽象语法树节点的结果,以及符号表输出的变量信息以及函数信息还有
类定义和类成员信息等。(例如变量的内存地址,以及在源文件的行列号,函数的唯一标识ID等)。
    接着运行
./zenglrun gui_21_point/sdl_21_point.zlc(或者是console_21_point/game_21_point.zlc)

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

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

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

 

上下篇

下一篇: zengl编程语言 中序

上一篇: zengl编程语言v0.0.24,SDL捕获鼠标事件,BUG修复

相关文章

zengl编程语言v0.0.8第二代语法解析函数

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

zengl v1.4.0 调试接口 zengl_SDL项目

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

zengl v1.2.0 嵌入式编程语言

zengl编程语言 中序