上一节完成了AST抽象语法树的构建,有了抽象语法树就可以将语法树转为真实的汇编中间码,再创建一个虚拟机来解释执行这些汇编中间代码就可以完成一个动态脚本该做的事了。 本节v0.0.5版本的代码...
上一节完成了AST抽象语法树的构建,有了抽象语法树就可以将语法树转为真实的汇编中间码,再创建一个虚拟机来解释执行这些汇编中间代码就可以完成一个动态脚本该做的事了。
本节v0.0.5版本的代码下载地址为:http://pan.baidu.com/share/link?shareid=73956& uk=940392313 (此为百度云盘的共享链接地址)访问该地址可以看到三个文件,zengl_language_v0.0.5_forLinux.tar.gz (linux下的代码文件) , zengl_lang_v0.0.5_forXP.rar (XP下的代码文件) , v0.0.5-v0.0.4-diffs.txt (v0.0.5和v0.0.4的代码变化情况)。
我在SourceForge.net上开了个project,地址为:https://sourceforge.net/projects/zengl/files/ 从里面可以看到各个版本的代码压缩包,例如本节的zengl_language_v0.0.5_forLinux.tar.gz,zengl_lang_v0.0.5_forXP.rar,v0.0.5-v0.0.4-diffs.txt
先来看下本版本的描述 (在linux代码包里的usage.txt里有这段描述):
v0.0.5的版本,该版本完成符号表,并生成汇编代码,最后创建解释器来执行汇编代码。
symbol.c为通过parser.c生成的语法树,生成标示符变量对应的符号表,符号表里存有变量的内存地址和变量在源文件中的行列号等。
有了语法树和符号表就可以生成解释器需要的代码。assemble.c文件通过语法树和符号表生成代码并写入.zlc为后缀的代码文件中。
func.c为run.c所需的函数库辅助文件,里面有些zl_malloc之类的内存分配函数,从main.c中提取出来的。
run.c为运行代码的解释器程序,解释器里有类似真机的寄存器数组,内存数组,指令数组(都为C实现的动态数组)。
将代码文件里的代码读入指令数组中,由PC指针寄存器来指示当前要执行的指令,执行指令类似真实的汇编代码,如MOV之类的指令,暂时只实现MOV,PRINT指令,以END指令为程序结束。
指令的执行过程其实就是对寄存器数组和内存数组的相互操作,其中用到了大量的类似语法分析中的DFA自动状态机的方法。
这样就已经从编译到执行完成了最简单的类似JAVA和python的动态脚本的实现。
以上就是该版本的一个简单的描述。从该版本开始,C源代码里包含了很多注释信息,方便调试和分析源代码。
先举个简单的例子来说明下本版本的运行机制: 比如代码 : a = 3; print a; 这两条脚本语句,会先通过parser.c生成语法树:
生成语法树后,再通过symbol.c生成符号表: 0 a 1:1 1:14 (第一个0表示全局变量a对应的内存地址为0,随后为变量名a,接着出现的1:1 1:14表示变量a在原脚本文件的1行1列和1行14列出现过)。
有了语法树和符号表,就可以由assemble.c生成如下的汇编代码(注释是自己加的,不在生成的汇编码里):
0 MOV AX 3; //将3移入虚拟机的虚拟寄存器AX里(之所以叫虚拟,因为都是数组来模拟的,并非PC机真实的寄存器)
1 MOV (0) AX; //将虚拟寄存器AX的值移入虚拟内存地址0的空间(即变量a的虚拟内存空间)。
2 print (0); //将内存0的值读取并打印出来。
3 END; //程序结束,退出虚拟机
有了汇编代码,就可以利用run.c对应的虚拟机来解释执行。执行的过程在上面注释里已经说明了。所以run.c构建的虚拟机的任务就是读取汇编代码文件,创建虚拟寄存器组以及虚拟内存,然后一条条的执行汇编代码里的指令,执行过程很像PC里的CPU。
目前版本的虚拟机里实现了两个虚拟寄存器:PC寄存器(里面存放的值表示虚拟机当前要执行那条指令,在每条指令前面都有个数字如0 MOV AX 3,这个数字就是给PC寄存器看的),AX寄存器(用来传递数据的中间寄存器,名副其实起个“寄存”的作用)。
至于代码的分析,因为已经在C源代码里加了很多注释,请结合注释,加上VS2008之类的调试开发环境进行调试分析。从本版本开始,每个源代码包括.h的头文件的开头都加入了GNU的自由版权声明。
linux系统下的用户请结合usage.txt的说明,先运行make clean 将原来生成的zengl zenglrun 和 main.o parser.o assemble.o func.o run.o symbol.o文件删除。
再运行make all (此版本开始,单纯的make只能生成zengl,所以需要make all来生成所有的目标)
生成zengl zenglrun 和 main.o parser.o assemble.o func.o run.o symbol.o。(在生成过程中如果出现一些警告,暂不管他)
最后运行 ./zengl test.zl 查看printASTnodes函数打印抽象语法树节点的结果,以及符号表输出的变量信息(例如变量的内存地址,以及在源文件的行列号等)。
接着运行./zenglrun test.zlc (注意是.zlc结尾的文件名,因为zenglrun虚拟机只能运行.zlc里的汇编代码)。
本例的抽象语法树等很多高级的原理都可以在《龙书》中找到。
最后的最后,如果转载请注明来源 http://www.zengl.com , OK , 先到这里,休息,休息一下 O(∩_∩)O~