类是面向对象编程里的一个很常见的概念,所谓类就是一组数据成员和方法(即函数)的集合。类的出现很大程度上提高了代码的重利用率。本节v0.0.23版 本实现了一个简单的类,类里面只有数据成员,...
类是面向对象编程里的一个很常见的概念,所谓类就是一组数据成员和方法(即函数)的集合。类的出现很大程度上提高了代码的重利用率。本节v0.0.23版 本实现了一个简单的类,类里面只有数据成员,还没有类方法,相当于C语言的结构体。另外还引入了SDL游戏引擎模块,最后使用SDL模块里的函数创建了一 个小的图形界面的游戏。
本节v0.0.23版本的源代码的下载地址为:http://pan.baidu.com/share/link?shareid=218459&uk=940392313 (此为百度云盘的共享链接地址) ,访问该地址可以看到三个文件:zengl_lang_v0.0.23_forXP.rar (XP系统下的vs2008解决方案和源代码), zengl_language_v0.0.23_forLinux.tar.gz (Linux系统下的源代码和makefile) ,v0.0.23-v0.0.22-diffs.txt (v0.0.23和v0.0.22两个版本之间的代码变化情况)。
SourceForge.net上的仓库地址为:https://sourceforge.net/projects/zengl/files/ 从里面可以看到各个版本的代码压缩包,比如本节的zengl_lang_v0.0.23_forXP.rar ,zengl_language_v0.0.23_forLinux.tar.gz,v0.0.23-v0.0.22-diffs.txt 。
先来看下本版本的描述(在linux代码包里的usage.txt里有这段描述,在目前几个带有git版本的....-diffs.txt和git log中也有这段描述):
v0.0.23版本,该版本实现了SDL(2D游戏引擎)的库函数,添加了负数,八进制,十六进制,类的定义,类的声明及类的使用。
sdlLib.c和sdlLib.h是SDL库函数的实现过程。结构和builtin.c,builtin.h的结构差不多,里面有SDL库的初始化,窗 口创建,加载位图,显示位图等函数。具体使用方法可以参考test_sdl_class.zl(该程式使用了class类来代替以前的数组的写法)。
以前的版本无法表示负数,现在可以在一个数字前添加单一的减号来表示负数,不过,当负数和其他数在一起运算时,要将数字和数字前的减号用括号括起来,以防 止出现优先级抢夺的问题,例如:-10*5此时没加括号,则会先乘运算再减法运算,所以最好是(-10)*5,这样先减运算得到负数,再乘运算,虽然这里 的结果应该是一样的,但当表达式复杂时,不加括号结果就可能不对了。
另外在main.c的getToken里添加了八进制和十六进制的token的解析,以0e开头的是八进制,e是eight英文8的首字母,0x开头的是 十六进制,十六进制的写法跟c语言和php中的是一样的,只有八进制的写法需要注意。main.c中当获取到八进制的token时会调用 convertOctalToDec函数将八进制转为十进制的token,当获取到十六进制的token时,会调用convertHexToDec将十六 进制转为十进制的token。在十进制整数解析时会调用convertStrToDec将超过2147483647的数以负数的形式保留下来,如 4294967295以-1的形式写入汇编代码中,之所以这么做是因为在run.c运行的时候,会调用atoi函数将超过整数最大值的数转为 2147483647,这样结果就不对,而以负数的补码形式保存下来的话,在计算机内存里的值不会发生变化,可以通过GetUIntStr builtin的库函数来将负数以uint无符号整形的形式还原显示出来。
最后本版本实现了一个最基本的类的定义,声明和使用,目前的类只有成员,没有方法即类的函数。所以本质上相当于c语言的结构体。
类定义格式是class 类名 类成员;类成员;... endcls这样的格式,在使用前要用类名 变量名;的格式来声明。目前类的声明要单独一条语句,且只能是类名 变量名加分号的格式,以后如有需要会将声明的格式进行扩展。
类的使用方法以点来访问类的成员如user.poker.point。也可以和数组连用,如:user.pokerlist[i].point的形式等。关于类的使用方法可以参考test_sdl_class.zl。
类的本质其实就是数组,例如:user.name,如果name是user类的第一个成员则相当于user[0],所以可以用user[0]来代替user.name 。
类的实现原理:在类的定义时将类的名称,类ID(编译器生成的类的唯一标识),类成员信息加入到hash表中,在类的声明时,在变量的全局或局部hash 表中为变量添加类id信息,当通过点运算符访问某成员时,就在类成员的hash表中搜索到该成员的索引值,最后以数组的汇编形式写入汇编代码中。
其他的详情请用git log -p 或 gitk等软件来查看。
作者:zenglong
时间:2012年5月29日
官方网站:www.zengl.com
本节的测试脚本test_sdl_class.zl里使用了新加入的SDL游戏引擎模块,创建了一个可以控制人物精灵四处移动的小游戏。windows下的游戏截图如下:
test_sdl_class.zl在Linux下运行时的截图如下:
test_sdl_class.zl脚本代码如下:
use builtin,sdl; //加载内建模块和SDL游戏引擎模块
def TRUE 1;
def FALSE 0;
//sdl event def SDL游戏引擎事件相关的宏定义
def NoEvent 0;
def EvKeyDown 2;
def EvKeyUp 3;
def KeyEsc 27;
def KeyUp 273;
def KeyDown 274;
def KeyLeft 276;
def KeyRight 275;
def EvQuit 12;
//sdl event def end
def WinWidth 640; //整个游戏窗口的宽
def WinHeight 480; //游戏窗口的高
def spWidth 32; //人物精灵位图的宽
def spHeight 64; //人物精灵位图的高
def Speed 7; //人物精灵的移动速度
class clsPosition //和绘制矩形有关的类定义
x; //左上角x坐标
y; //左上角y坐标
w; //矩形宽
h; //矩形高
endcls //类定义结束
class clsSpirit //和人物精灵相关的类定义
image; //人物精灵的位图指针成员
clsPosition src; //将人物精灵绘制到游戏表面时要用到的绘制源矩形
clsPosition dest; //绘制目标矩形
endcls
class clsFont //和字体相关的类定义
obj; //SDL打开的字体对象指针
clsPosition src; //绘制字体到游戏表面时要用到的绘制源矩形
clsPosition dest; //绘制目标矩形
endcls
class clsEvent //和用户事件相关的类定义,如按键事件,消息循环事件,关闭按钮事件,鼠标事件等。
type; //事件类型
keytype; //按键类型
endcls
clsSpirit spirit; //声明一个clsSpirit类型的全局变量spirit代表游戏中的人物精灵
clsFont font; //声明一个clsFont类型的全局变量font代表字体
clsEvent event; //声明事件全局变量event
sdlInit(); //SDL游戏初始化
screen = sdlCreateWin(WinWidth,WinHeight,32,0); //创建一个游戏窗口,返回游戏的屏幕指针或者叫表面指针
sdlSetCaption('ZENGL SDL 测试 按上下左右键移动人物 ,按ESC键退出 官网:www.zengl.com'); //设置游戏的窗口标题
bkimg = sdlLoadBmp('bkimg.bmp'); //加载bkimg.bmp背景位图
spirit = array(sdlLoadBmp('foo2.bmp',0,0xff,0xff),
array(0,0,spWidth,spHeight),array(0,0,spWidth,spHeight)); //加载foo2.bmp人物精灵位图
sdlFontInit(); //字体全局初始化
font = array(sdlOpenFont('lazy.ttf',28),
array(0,0,0,0),array(100,100,0,0)); //加载lazy.ttf字体文件
fontcolor = array(0xff,0,0); //设置字体的前景色为红色。
bgcolor = array(0,0,0); //设置字体的背景色为黑色。
event = array(); //将事件通过array函数初始化
gameover = FALSE; //判断游戏是否结束的全局变量
for(;!gameover;)
time = sdlGetTicks(); //获取游戏的滴答数,用于设置帧数的。
for(;sdlPollEvent(event)!=NoEvent;)
switch(event.type)
case EvQuit: //如果点了游戏窗口的关闭按钮时,设置gameover
gameover = TRUE;
break;
case EvKeyDown: //如果按下了ESC键时,也gameover
switch(event.keytype)
case KeyEsc:
gameover = TRUE;
break;
endswt
break;
endswt
endfor
sdlGetKeyState(event); //获取用户的按键数组
if(event[KeyUp] && spirit.dest.y > 0) //如果按下了上键,则根据精灵当前的位置的y坐标减去速度值,得到新的y坐标。当然y值不能小于0,小于0就到窗口外了。
spirit.dest.y -=Speed;
endif
if(event[KeyDown] && spirit.dest.y < WinHeight-spHeight) //按下键时则人物向下移动
spirit.dest.y +=Speed;
endif
if(event[KeyLeft] && spirit.dest.x >0) //按下左键则左移
spirit.dest.x -=Speed;
endif
if(event[KeyRight] && spirit.dest.x <WinWidth-spWidth) //按右键则右移
spirit.dest.x +=Speed;
endif
sdlDelay(10); //设置10毫秒延时。
sdlSetBkImg(bkimg); //设置游戏背景图片
sdlBlitImg(spirit.image,spirit.src,screen,spirit.dest); //将人物精灵绘制到游戏表面
time = sdlGetTicks() - time; //将当前滴答数减去上一次统计的滴答数,得到一帧经过的毫秒数
text = sdlDrawText(font.obj,'FPS:'+1000/time + 'f/s',fontcolor,bgcolor); //绘制帧数的字符串位图
sdlBlitImg(text,0,screen,font.dest); //将帧数的字符串位图绘制到游戏表面
sdlFreeImage(text); //释放掉字符串位图
sdlShowScreen(); //将绘制的游戏表面输出到显卡显示出来。
endfor
sdlCloseFont(font.obj); //关闭前面打开的字体
sdlFontQuit(); //释放字体系统资源
sdlQuit(); //释放SDL系统资源并退出
上面的注释在原文件中都有。在linux代码包的makefile文件里有个地方需要注意,下面是makefile文件的代码片段:
#makefile for zengl language
CC = gcc
CFLAGS = -g3 -ggdb -O0
SDL_LIB_CFLAGS_H = -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT
SDL_LIB_CFLAGS_LIB = -L/usr/lib -Wl,-rpath,/usr/lib -lSDL -lSDL_ttf -lpthread
....................................... //省略N行代码
从上面可以看出,makefile中增加了和SDL游戏引擎相关的头文件和链接库文件相关的配置(由红色标注的部分),这部分就是告诉编译器SDL游戏引擎的头文件和链接库文件所在位置。这些信息在linux下可以使用sdl-config命令得到。下面是我的slackware系统打印出来的SDL配置信息:
至于SDL在linux下的安装方法,可以参考lazyfoo.net ,这个网站有SDL开发的详细入门教程,他的第一课就是教大家如何在windows系统下和linux系统下安装sdl ,在linux下安装完SDL后,正常情况下就可以调用上图中的sdl-config命令打印配置信息,请根据自己的配置信息对应修改makefile文 件里的SDL_LIB_CFLAGS_H和SDL_LIB_CFLAGS_LIB
windows下的代码包里已经将SDL的VC开发包加入到压缩包里了:
上图中的SDL-1.2.15就是SDL 1.2.15版本的VC开发目录,里面包含了所需的头文件和链接库文件,并且在VS2008的zenglrun的项目属性里也使用相对路径的方式将附加包 含目录指向了SDL-1.2.15里的include目录,将附加库目录指向了SDL-1.2.15里的lib下的x86目录,还设置了链接附加依赖性为 SDL.lib SDLmain.lib SDL_ttf.lib SDL_image.lib ,所以只要有VS2008的开发环境,直接解压运行项目即可。
因为zengl_lang_v0.0.23项目里加入了SDL所需的DLL动态链接库如SDL.dll , SDL_image.dll , SDL_ttf.dll等,所以本节的XP的压缩包有2M多,比之前的版本要大。
最后还是老生常谈的话题:
windowsXP压缩包中的代码都是采用GBK的编码,Linux压缩包中的代码以及git里的信息都是UTF8的编码,还有本节的测试脚本test_sdl_class.zl是采用UTF8的编码的,XP和linux下都是一样,因为SDL游戏引擎只支持UTF8的汉字显示,所以如果哪些地方出现了乱码,请自行调整。
对于windows用户,请确保在项目属性的配置里,命令行参数配置的是test_sdl_class.zl(对于zengl_lang_v0.0.23的项目)或test_sdl_class.zlc(对于zenglrun的项目)。另外工作目录设置为../zengl_lang_v0.0.23 ,这样在zengl_lang_v0.0.23下修改了.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的目标程序中加入调 试代码。
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 test_sdl_class.zl 查看printASTnodes函数打印抽象语法树节点的结果,以及符号表输出的变量信息以及函数信息还有类定义和类成员信息等。(例如变量的内存地址,以及在源文件的行列号,函数的唯一标识ID等)。
接着运行./zenglrun test_sdl_class.zlc
如果要调试脚本则使用./zengldebug test_sdl_class.zlc(注意是.zlc结尾的文件名,因为zenglrun虚拟机和zengldebug调试器只能运行.zlc里的汇编代码)。
zengl语言涉及到的很多高级的编译原理都可以在《龙书》中找到。
最后的最后,如果转载请注明来源 http://www.zengl.com , OK , 先到这里,休息,休息一下 O(∩_∩)O~