本节v1.0.5的版本改动的地方较多,为了简化操作将编译器做成动态链接库的形式,只保留一个虚拟机的可执行程序,通过命令行参数来控制是否开启虚拟机的调试功能,最后使用本版本开发出俄罗斯方...
本节v1.0.5的版本改动的地方较多,为了简化操作将编译器做成动态链接库的形式,只保留一个虚拟机的可执行程序,通过命令行参数来控制是否开启虚拟机的调试功能,最后使用本版本开发出俄罗斯方块游戏。
本节v1.0.5的版本下载地址为:http://pan.baidu.com/share/link?shareid=394634&uk=940392313 (此为百度云盘的共享链接地址),访问该地址首先看到v1.0.5的文件夹共享链接,点击进去可以看到四个文件(百度盘的操作变得有点繁琐了):zengl_lang_v1.0.5_forXP.rar (XP系统下的vs2008解决方案和源代码),zengl_language_v1.0.5_forLinux.tar.gz (Linux系统下的源代码和makefile) ,release_Game_Tetris_forXP.rar (俄罗斯方块游戏发布版,可以直接运行在没装任何开发调试工具的windows环境下,xp ,win7下都可以运行) ,release_Game_21_Point_forXP.rar (改进过的21点扑克游戏,采用按需求绘制方法,占用CPU资源比原来少很多)。
SourceForge.net上的仓库地址为:https://sourceforge.net/projects/zengl/files/ 从里面可以看到各个版本的代码压缩包,比如本节的 zengl_lang_v1.0.5_forXP.rar , zengl_language_v1.0.5_forLinux.tar.gz , release_Game_Tetris_forXP.rar ,release_Game_21_Point_forXP.rar 。
v1.0.5-v1.0.3-diffs.txt(v1.0.5和v1.0.3版本的代码变化情况)文件放在zengl_lang_v1.0.5_forXP.rar压缩包里,同时zl.stx(本版本的editplus语法高亮文件,其中包含高亮各个模块的函数和关键词等信息)也放在该压缩包中。至于zl.stx的用法请查看前面的"zengl编程语言 中序"的文章。
先来看下本版本的描述(在linux代码包里的usage.txt里有这段描述,v1.0.5-v1.0.3-diffs.txt和git log中也有这段描述):
zengl v1.0.5的版本,该版本将编译器,虚拟机,调试器整合在一起,一个可执行程序即可完成所有的功能,编译器以动态连接库的形式被加载,默认情况下会自动 对脚本进行编译并执行,调试除了可以进行汇编级调试外,还增加了脚本源代码调试功能,完善了SDL模块,为其添加了指针管理系统,无需在脚本中释放SDL 指针,系统会在结束时自动清理,最后使用zengl脚本完成了图形界面的俄罗斯方块游戏。
调试一直以来都是比较头疼的问题,如果将调试信息写入sqlite之类的本地数据库中,那么写入的速度会很慢,尤其是成千上万的符号信息,现在编译器以动 态链接库的形式存在,为源码调试提供了可能,因为做成动态链接库后,zenglrun虚拟机在调用编译器编译完脚本后,所有调试信息都保存在内存中,这样 在虚拟机里调试时就可以通过动态链接库提供的接口访问到所需的调试信息,从而实现源码级调试,而且速度上也不会有太大的影响。
在assemble.c中,通过debugAssemNodeTable动态数组,将汇编代码索引和节点号,函数ID等信息关联起来,这样当虚拟机调试的 时候,就可以由当前调试的汇编代码索引值得到对应的语法树节点号,并通过AST抽象语法树节点里的信息得到源代码中的行列号信息,文件名信息,所在的函数 名信息等。当然前提是虚拟机在命令行运行时提供了-d参数(开启调试功能)。
另外,在assemble.c中还添加了其他的调试功能,比如在虚拟机中调试时可以通过r print test;的形式打印输出test的变量信息,其原理就是将r指令后面的print test;语句写入到临时的调试脚本中,再调用动态连接库里的编译器,编译器将这个临时脚本进行编译汇编输出到临时的.zlc文件中,最后虚拟机再加载并 执行该.zlc,从而完成print test;这样的语句,实现源码级调试功能。这些调试临时文件会在虚拟机退出时自动清理掉。
编译器的其他源文件如main.c parser.c symbol.c ld.c等都添加了和源码调试有关的代码,另外,exportfuns.h头文件是用来定义动态链接库里的导出函数的。因为编译器做成了动态链接库的形 式,所以在main.c里使用了zengl_jump_buffer来作为出错时,统一跳转的位置 , 这是C里面的一种长跳转的方式,如果没有长跳转,那么在编译器编译失败时因为不能像以前那样使用exit直接退出,就很难处理出错的情况。
虚拟机和编译器在启动时都支持一些命令行参数,在不带任何参数的情况下,虚拟机默认会先编译zengl脚本,然后再运行编译生成的.zlc中间码文件。可 以使用-h参数来查看所有完整的可用参数信息,目前虚拟机支持的命令行参数为-c(只编译为.zlc,而不执行),-p参数,该参数只用于windows 系统,一般用在visual studio调试时,因为需要-p参数让脚本执行完后暂停,否则一些很简单的脚本执行时就会一闪而过,如果是普通的命令行状态下或linux命令行下就不 需要这个参数。
-s参数,将编译器生成的语法树节点信息和符号表信息写入到debuglogs.txt日志文件中,该日志除了可以记录符号信息外,还会记录所有编译时的 语法错误信息。-n参数,当编译时,不显示正在编译的信息,默认情况下编译时会显示正在编译和编译完成的提示信息,如果为了发布脚本而不显示这些提示信息 的话就可以使用-n参数。-r参数,只运行.zlc中间码,不编译,前提是脚本对应的.zlc文件已经生成好了,因为-r参数不进行编译,所以执行效率比 较高,一般用在发布脚本的时候,-r不能和-c连用。
-d参数,开启调试模式,只有开启调试模块才能得到正确的源代码的行列号等信息,如果是在开发脚本的阶段建议开启-d参数,在有-d参数的情况下,虚拟机 的运行时错误都可以定位到确切的源文件的行列号,如果没有-d参数,那么运行时错误时,就只能显示汇编代码的索引值。
-futg参数只能用于windows系统中(原因是该功能使用的是windows本地api来实现的,在linux下不支持),因为windows系统 的命令行是gbk编码的,而zengl开发的游戏脚本都是utf8的编码的,所以如果要在windows命令行下调试包含中文的脚本变量的话,可以使用 -futg参数(前提是该脚本是utf8编码的),虚拟机调试在遇到print语句时就会将字符串由utf8编码转为gbk编码,然后输出到cmd命令行 中,这样就可以调试utf8的脚本了,linux下的命令行默认就是utf8的,如果不是请自行调整终端编码。
-v参数可以打印zengl的版本号信息。
在虚拟机中进行调试时相应增加了和源码调试相关的指令,如B指令用于在源文件中下断点用的,r指令用于执行zengl脚本用的,通过r执行一些print语句就可以调试源代码中的变量和数组等信息了。可以使用h指令来查看所有可用的调试指令和相关说明。
本版本中的winfunc.c文件里存放的都是windows平台下的专用函数,例如将utf8转为gbk编码的函数等。
该版本的run.c对数组成员进行严格检测,如果数组索引没有初始化或小于0都会输出运行时异常,如果出现这些异常提示,可以使用-d参数开启调试模式, 然后定位到具体的源文件中,查看是哪个数组没初始化,或者检查数组索引是否大于等于0等。之前的某些版本的测试脚本可能无法通过本版本的检测,可以根据错 误提示进行处理。
因为该版本只有一个可执行文件,所以linux下只需要make就可以生成所有的目标,无需像以前那样make all 了。
另外,需要注意的是在linux系统下make编译时,一定要在root权限下,因为make生成的编译器的动态链接库需要拷贝到/usr/lib目录 中,所以一定要切换到root状态下再make,否则链接不到动态链接库,还有编译的gcc版本需要大于等于4.0的版本,目前大部分linux发行版的 gcc都是大于4.0的,所以一般无需考虑。
俄罗斯方块的zengl脚本和图片,字体等资源都存放在gui_Tetris目录中,tetris.zl是主程序,tetris_def.zl则定义了一些宏和类结构。脚本通过sdl模块和碰撞检测等算法实现了经典的俄罗斯方块游戏。
具体的改动请使用git log -p或gitk等软件来查看。
作者:zenglong
时间:2013年4月10日
官网:www.zengl.com
本节的测试脚本就是俄罗斯方块游戏,在zengl_lang_v1.0.5_forXP.rar压 缩包解压后得到的zenglrun项目里有个gui_Tetris目录,里面存放了俄罗斯方块游戏的zengl脚本和游戏相关的图片,字体等资源,在脚本 中都加了注释,以方便分析脚本,由于俄罗斯方块是个比较经典的游戏,里面涉及到一些碰撞检测的游戏算法(俄罗斯方块游戏主要就是算法),所以会为其开设一 个专门的栏目,然后将俄罗斯方块游戏开发过程中的所有版本都进行分析。只要理解了其中的算法,就可以使用C#,JAVA等任何你熟悉的编程语言来开发出自 己的俄罗斯方块出来。将release_Game_Tetris_forXP.rar压缩包解压,运行"俄罗斯方块双击我运行.bat"就可以直接玩,在里面有个游戏说明.txt,和其他的俄罗斯方块一样的玩法。
在windows下俄罗斯方块的截图如下:
在linux下按P键暂停时的截图如下(linux下的kde截图软件像素不太高):
之前的版本调试时使用的是zengldebug的可执行程序,本版本统一使用zenglrun虚拟机来编译,执行,调试,因为承载了多个功能,所以为 zenglrun添加了命令行参数,这个在上面已经介绍了,如果忘记了参数,可以使用-h参数来查看可用参数列表(-h是help的英文首字母),需要注 意的是所有命令行参数务必放在zenglrun和脚本文件名后面,因为在程序里是固定取zenglrun后面的第一个参数作为脚本文件名的:
在使用zenglrun时,后面直接接.zl结尾的脚本文件名即可,在非-r的参数下,虚拟机会自动对.zl进行编译,然后再加载.zlc执行,如果 接.zl文件名时,使用了-r命令行参数,那么虚拟机就会直接运行之前生成的.zlc文件,前提是该文件存在,当然也可以像以前那样zenglrun后面 接.zlc结尾的文件名,此时虚拟机就会直接运行中间码文件。在没有-d参数的情况下,虚拟机不会生成调试信息,如果需要源码级的调试功能,可以使用-d 参数,并且不可以和-r参数放在一起,也不可以直接接.zlc结尾的文件名,因为使用-d参数要开启调试信息的前提是虚拟机调用编译器编译了源代码脚本 后,才有可能在内存中生成调试用的符号等信息,在使用-r参数或直接接.zlc文件名的情况下,虚拟机根本就不会去编译源代码脚本,也就不会有调试信息 了。在没有调试信息的情况下最多只能看到汇编代码的索引信息,所以开发脚本的过程中最好开启调试。发布脚本的时候,再将-d参数的调试模式去掉。
因为虚拟机添加了源代码调试功能,所以对应增加了和源码调试有关的调试指令,如B指令和r指令,如下图:
如上图,可以使用B指令后接文件名,再接行号如上图的B 'gui_Tetris/tetris.zl' 338, 即表示在该文件的该行进行中断,文件名的写法必须和命令行中或脚本inc里的写法一致,需要用单引号或双引号将文件名或相对路径包起来,并且并不是每个源 代码行都可以中断,因为有的源代码行像endfun之类的都没有对应的汇编代码,当然如果下断点的位置和当前虚拟机正在执行的代码在同一文件中时,可以直 接B指令加行号,如上图的B 342,即表示在当前执行脚本的342行进行中断。
在中断下来后,可以使用r指令来运行zengl脚本语句进行变量等调试,如上图的r print tetris.direct;该语句就是打印出tetris类中的direct成员的值。r后面的语句必须是有效的zengl语句,所以最后的分号不可以少,当然可以输出多个变量信息,只需在r后多写几个print语句即可,r指令的原理上面已经介绍过了,就是将r后的语句写入临时脚本文件zengl_debug.tmp.debug~中,然后虚拟机调用编译器对其进行编译并生成zengl_debug.tmp.debug~c中间码文件,最后虚拟机再加载zengl_debug.tmp.debug~c文件执行,就可以执行r后面的语句了,最好只进行print打印语句,至于赋值语句只有在必要时才用,以免破坏脚本的执行。zengl_debug.tmp.debug~和zengl_debug.tmp.debug~c两个临时文件在虚拟机结束时就会自动清理掉。
p tracert打印堆栈追踪信息时,在调试模式下就会输出源文件名和行列号信息以及函数名,函数ID,对应语法树节点号信息。如上图的 ------ ARG:3 LOC:4 PC:1726 (2017) [12 myTetrisMove] <gui_Tetris/tetris.zl> 342 : 3 ,其中ARG:3 LOC:4 PC:1726和之前的汇编调试是一样的,(2017)是指当前汇编代码对应的语法树节点号,[12 myTetrisMove]里的12是myTetrisMove函数的ID,myTetrisMove则是函数 名,<gui_Tetris/tetris.zl> 342 : 3为当前汇编代码对应的脚本文件名,和行列号信息。
p regs指令在调试模式下同样会输出源代码的文件名,行列号,函数名等信息。
如果忘记了调试指令,可以使用h指令(help的英文首字母):
目前调试指令还没有针对源代码的单步调试,可以使用B指令来处理。以后的版本再考虑加入针对源代码的单步执行指令,目前基本上B指令和r指令就可以完成脚 本的调试了,还可以在源代码脚本中添加print语句和read()语句来辅助调试,如在tetris.zl俄罗斯方块脚本中就有一段调试代码:
..................................//省略N行代码
fun myRemoveMainBgFixTetris()
..................................//省略N行代码
if(gameMainBg.line_count_array[vy+needRemoveNum] > GameMainWidthNum) //这个判断是调试用到。用于判断消除和位移工作是否正常。
print 'debug error pause line '+vy+' count out of range!'; //debug
read(); //debug
endif
..................................//省略N行代码
endfun
..................................//省略N行代码
具体的C文件代码部分,请结合源代码中的注释,再加上git工具以及vs2008或者eclipse+CDT插件或者gcc,gdb等工具进行分析。
本版本的测试脚本都是游戏脚本,所以都是采用UTF8编码的。所以如果哪些地方出现了乱码,请自行调整。
以前的版本中,windows代码包里的解决方案有个zenglrun开启命令行的调试配置,v1.0.5版本因为所有功能都做在了一起,所以就不需要该调试配置了,就是Debug配置和Release配置,在Debug配置下,请确保在zenglrun项目属性的配置里,命令行参数配置的是gui_Tetris/tetris.zl -d -s -p ,在Release配置下,确保在zenglrun项目属性的配置里,命令行参数配置的是gui_Tetris/tetris.zl -n -p ,参数的含义请参考上面的内容,另外对于zengl项目,[配置属性>>>>常规]中的配置类型是动态库(.dll)。
对于vs2008的用户,我在项目属性里:[配置属性>>>>C/C++ >>>> 高级] 部分设置了禁用特定警告:4013,4715,4996 ,这几个警告会显示一些某某函数是非安全的函数,或者函数没有返回值等,这里禁用掉,防止出现过多的警告。另外还有个警告是显示某某变量没被使用过的, 这个警告我没禁用,可以不用管它。我最开始是在Linux系统中使用eclipse+CDT插件以及gcc等开发的zengl ,在我的GCC下面并没有显示过这些讨厌的警告,所以就没处理,不过还好这些警告都无关痛痒,无需理会。
还有一个地方:VS2008项目中,在[配置属性>>>> C/C++ >>>> 预处理器] 部分都设置了预处理器定义的宏:OS_IN_WINDOWS ,之所以需要OS_IN_WINDOWS,是因为源代码既要在WINDOWS下编译,又要在LINUX下编译,所以需要通过这个宏来告诉编译器当前的环境 是windows还是linux,在windows下面,会加入一些和windows平台相关的特性,另外,zenglrun项目里设置了很多SDL相关 的依赖库,头文件等信息。
Release版本的程序是采用/MT多线程运行时编译的,所以可以直接在别的没有安装调试工具的电脑上运行。
linux系统下的用户请结合usage.txt的说明,先运行make clean 将原来生成的libzengl.so zenglrun 和 func.o run.o debug.o builtin.o sdlLib.o文件删除。
再运行make
生成libzengl.so zenglrun 和 func.o run.o debug.o builtin.o sdlLib.o。(在生成过程中如果出现一些警告,暂不管他)
最后运行 ./zenglrun gui_Tetris/tetris.zl 直接编译运行,如果要调试可以在后面加个-d参数。使用-h来查看所有可用的参数,参数都必须放在脚本文件名后面。
最后的最后,如果转载请注明来源 www.zengl.com , OK , 先到这里,休息,休息一下 O(∩_∩)O~