测试当前符号(即sym变量中的值)是否在s1集合中,如果不在,就通过调用出错报告过程输出出错代码n,并放弃当前符号,通过词法分析过程获取一下单词,直到这个单词出现在s1或s2集合中为止。
这个过程在实际使用中很灵活,主要有两个用法:
在进入某个语法单位时,调用本过程,检查当前符号是否属于该语法单位的开始符号集合。若不属于,则滤去开始符号和后继符号集合外的所有符号。
在语法单位分析结束时,调用本过程,检查当前符号是否属于调用该语法单位时应有的后继符号集合。若不属于,则滤去后继符号和开始符号集合外的所有符号。 通过这样的机制,可以在源程序出现错误时,及时跳过出错的部分,保证语法分析可以继续下去。
语法分析过程中调用的其它子过程相对比较简单,请参考源程序的注释。
类PCODE代码解释执行过程分析:
这个过程模拟了一台可以运行类PCODE指令的栈式计算机。它拥有一个栈式数据段用于存放运行期数据、拥有一个代码段用于存放类PCODE程序代码。同时还拥用数据段分配指针、指令指针、指令寄存器、局部段基址指针等寄存器。 解释执行类PCODE代码时,数据段存储分配方式如下: 对于源程序的每一个过程(包括主程序),在被调用时,首先在数据段中开辟三个空间,存放静态链SL、动态链DL和返回地址RA。静态链记录了定义该过程的直接外过程(或主程序)运行时最新数据段的基地址。动态链记录调用该过程前正在运行的过程的数据段基址。返回地址记录了调用该过程时程序运行的断点位置。对于主程序来说,SL、DL和RA的值均置为0。静态链的功能是在一个子过程要引用它的直接或间接父过程(这里的父过程是按定义过程时的嵌套情况来定的,而不是按执行时的调用顺序定的)的变量时,可以通过静态链,跳过个数为层差的数据段,找到包含要引用的变量所在的数据段基址,然后通过偏移地址访问它。
在过程返回时,解释程序通过返回地址恢复指令指针的值到调用前的地址,通过当前段基址恢复数据段分配指针,通过动态链恢复局部段基址指针。实现子过程的返回。对于主程序来说,解释程序会遇到返回地址为0的情况,这时就认为程序运行结束。
解释程序过程中的base函数的功能,就是用于沿着静态链,向前查找相差指定层数的局部数据段基址。这在使用sto、lod、stoArr、lodArr等访问局部变量的指令中会经常用到。
类PCODE代码解释执行的部分通过循环和简单的case判断不同的指令,做出相应的动作。当遇到主程序中的返回指令时,指令指针会指到0位置,把这样一个条件作为终至循环的条件,保证程序运行可以正常的结束。
七.上机调试情况 调试环境:
操作系统:Windows XP下c语言PL/0编译程序 编译系统:Visual C++ 6.0(sp6) 运行步骤:
把pl0.c和pl0.h两个文件放在同一目录下,直接用Visual C++ 6.0(sp6)打开便可运行
然后运行后输入PL/0源程序文件名,回答是否输出虚拟机代码,回答是否输出名字表,输入完成以后可以看到结果,若豁达是要输出虚拟机代码和名字表且程序无错误,则输出相应的机器代码和目标代码。
以下是几个程序测试结果:
六.实验感想及体会
其实刚开始对PL/0编译程序不是很清楚,只知道理论的知识,对这个实验无从下手,经过和同学的讨论加上自己的琢磨,终于经过一周的PL/0编译程序的C语言扩充,使我加深了对编译理论知识的理解和掌握,并能达到基本上的运用。同时也增强了动手和实践的能力。虽然在遇到了不少困难,但是正因为这些困难,我也学到了更多的东西。首先,要勇于面对困难,只要敢于去做,并将所学灵活运用,定能取得成功;其次,我也更好的了解到合作的重要性,在和同学探讨和认真分析后,最终找到解决方案。程序的最终顺利运行,是离不开同学的互相帮助的。