燕山大学《编译原理课程设计》题目:《PL/0编译程序改进及完善》姓名:简越班级:06级计算机应用3班学号:060104010084日期:2009年7月15日设计题目:PL/0编译程序改进及完善。
设计目的:阅读研究,改进设计和调试一个简单的编译程序。
加深对编译理论和过程的了解。
设计要求:1.有选择的对PL/0编译源程序补充,完善.2.设计编译典型的运行实例,以便反应出自己作出改进后的编具有的功能。
设计思想:PL/0语言可以看成PASCAL语言的子集,它的编译程序是一个编译解释执行系统。
PL/0的目标程序为假想栈式计算机的汇编语言,与具体计算机无关。
PL/0的编译程序和目标程序的解释执行程序都是用PASCAL语言书写的,因此PL/0语言可在配备PASCAL语言的任何机器上实现。
其编译过程采用一趟扫描方式,以语法分析程序为核心,词法分析和代码生成程序都作为一个独立的过程,当语法分析需要读单词时就调用词法分析程序,而当语法分析正确需要生成相应的目标代码时,则调用代码生成程序。
用表格管理程序建立变量、常量和过程表示符的说明与引用之间的信息联系。
当源程序编译正确时,PL/0编译程序自动调用解释执行程序,对目标代码进行解释执行,并按用户程序的要求输入数据和输出运行结果。
主要变量说明:/*变量说明*/FILE* fas; /*输出名字表*/FILE* fa; /*输出虚拟机代码*/FILE* fa1; /*输出源文件及其各行对应的首地址*/FILE* fa2; /*输出结果*/bool listswitch; /*显示虚拟机代码与否*/bool tableswitch; /*显示名字表与否*/char ch; /*获取字符的缓冲区,getch使用*/enum symbol sym; /*当前的符号*/char id[al+1]; /*当前ident,多出的一个字节用于存放0*/ int num; /*当前number*/int cc,ll; /*getch使用的计数器,cc表示当前字符(ch)的位置*/int cx; /*虚拟机代码指针,取值范围[0,cxmax-1]*/char line[81]; /*读取行缓冲区*/char a[al+1]; /*临时符号,多出的一个字节用于存放0*/ struct instruction code[cxmax]; /*存放虚拟机代码的数组*/char word[norw][al]; /*保留字*/enum symbol wsym[norw]; /*保留字对应的符号值*/enum symbol ssym[256]; /*单字符的符号值*/char mnemonic[fctnum][5]; /*虚拟机代码指令名称*/bool declbegsys[symnum]; /*表示声明开始的符号集合*/bool statbegsys[symnum]; /*表示语句开始的符号集合*/bool facbegsys[symnum]; /*表示因子开始的符号集合*//* 目标指令*/1、LIT:将常量值取到运行栈顶.2、LOD:将变量放到运行栈顶.3、STO:将栈顶的内容送入某变量单元中.4、CAL:调用过程的指令.5、INT:为被调用的过程(或主程序)在运行栈中开辟数据区.6、JMP:无条件转移指令.7、JPC:条件转移指令,当栈顶的布尔值为真时, 顺序执行,否则转向域的地址.8、OPR:系运算符和算术运算指令.将栈顶和次栈顶的内容进行运算,结果存放栈顶./*函数说明*/void error(int n,int line)说明:出错处理函数,打印出错信息,错误总数加1。
int getch()说明:读取字符函数,返回字符。
int getsym()说明:读取下一单词符号int position(char* idt,int tx);说明:字符在符号表中位置查询函数返回值:返回标识符在符号表中的索引int gen(enum fct x,int y,int z);说明:生成P代码指令int test(bool* s1,bool* s2,int n);说明:测试当前符号是否合法,若不合法,打印出错信息并进行跳读void enter(enum object k, int* ptx,int lev,int* pdx);说明:在符号表中登录分程序说明部分出现的名字int constdeclaration(int* ptx,int lev,int* pdx);说明:处理常量说明,并将常量名及相应信息填入符号表int vardeclaration(int* ptx,int lev,int* pdx);说明:处理变量说明,并将变量名及相应信息填入符号表int statement(bool* fsys,int* ptx,int lev);说明:分析处理各种语句int condition(bool* fsys,int* ptx,int lev);说明:分析处理条件式返回值:由参数x返回求值结果的类型void listcode(int cx0);说明:打印P代码int block(int lev,int tx,bool* fsys);说明:PL0编译器对外的接口,由main()调用void interpret();说明:解释执行P代码int base(int l,int* s,int b);说明:基地址处理函数返回值:返回变量的基地址值int expression(bool* fsys,int* ptx,int lev)说明:表达式处理,由参数返回结果类型int term(bool* fsys,int* ptx,int lev);说明:项处理,由参数返回结果类型int factor(bool* fsys,int* ptx,int lev);说明:因子处理,由参数返回结果类型int inset(int e,bool* s);int addset(bool* sr,bool* s1,bool*s2,int n);int subset(bool* sr,bool* s1,bool*s2,int n);int mulset(bool* sr,bool* s1,bool*s2,int n);说明:使用数组实现集合的集合运算算法描述:在原算法基础上我进行了一下修改1.增加单词:保留字ELSE2.运算符+=,-=,++,――3.增加条件语句的ELSE子句为此添加了ELSESYM,为了实现如上要求的扩充,必须再正确地添加JIAJIA,JIANJIAN,JIADENG,JIANDENG等几个个SYMBOL,具体必须包括以下三个方面的修改或添加:在枚举变量SYMBOL的定义内添加JIAJIA,JIANJIAN,JIADENG,JIANDENG和ELSESYM;然后在在源程序文件中加上保留字ELSE,为了识别例如A:=++A之类的功能,还需在因子开始符号中加入JIAJIA,JIANJIAN。
这样,我们就正确地加入了五个将用来作为扩充语言功能的SYM。
为了实现功能首先需要在词法分析中加入识别单词功能增加JIAJIA(++)、JIADENG(+=)、JIANJIAN(——)、JIANDENG(—=)的词法分析,代码修改如下:if(ch=='+'){getchdo;if(ch=='='){sym=jiadeng;getchdo;}else{if(ch=='+'){sym=jiajia;getchdo;}else{sym=plus;}}}else{if(ch=='-'){getchdo;if(ch=='='){sym=jiandeng;getchdo;}else{if(ch=='-'){sym=jianjian;getchdo;}else{sym=minus;}}}这样,我们就完成了对词法分析器的修改。
然后JIAJIA.,JIANJIAN操作的语法图有如下两个:语句因子根据以上语法图,我们只要对语句处理程序和因子处理程序进行修改添加,即可实现增加INC和DEC操作,首先对语句处理程序进行如下修改:语句中形如++A,——A修改:if(sym==jiajia||sym==jianjian)//++a,--a{if(sym==jiajia){getsymdo;if(sym==ident){i=position(id,*ptx);if(i==0){error(11);}else{if(table[i].kind!=variable){error(12);i=0;}else{gendo(lod,lev-table[i].level,table[i].adr);//生成指令,将变量放到栈顶gendo(lit,0,1);//生成指令,把常量取到运行栈顶gendo(opr,0,2);//生成指令,栈顶加次栈顶gendo(sto,lev-table[i].level,table[i].adr);//生成指令,结果写回变量地址单元getsymdo;}}}}else//--a{getsymdo;if(sym==ident){i=position(id,*ptx);if(i==0){error(11);}else{if(table[i].kind!=variable){error(12);i=0;}else{gendo(lod,lev-table[i].level,table[i].adr);//生成指令,取变量放大栈顶gendo(lit,0,1);//生成指令,取常数到栈顶gendo(opr,0,3);//生成指令,栈顶减次栈顶gendo(sto,lev-table[i].level,table[i].adr);//生成指令,结果写回变量地址单元getsymdo;}}}}随后修改如A++,A——if(sym==jiajia||sym==jianjian)//a++,a--{addop=sym;gendo(lod,lev-table[i].level,table[i].adr); //把变量的值压入栈gendo(lit,0,1);if(addop==jiajia){gendo(opr,0,2);gendo(sto,lev-table[i].level,table[i].adr);}else{gendo(opr,0,3);gendo(sto,lev-table[i].level,table[i].adr);}getsymdo;}这样,对JIAJIA和JIANJIAN操作就扩充完成。
扩充+=和-=操作这两个操作都是一种对变量进行赋值的形式,其合法的语句形式的语法图如下所示:根据图3,在已经处理部分的基础上,添加对+=运算和-=运算的扩充,相关代码添加如下:if(sym==jiadeng||sym==jiandeng){addop=sym;getsymdo;gendo(lod,lev-table[i].level,table[i].adr); /*把变量的值压入栈*/factordo(nxtlev,ptx,lev);if(addop==jiadeng){gendo(opr,0,2); /*生成加法指令*/}else{gendo(opr,0,3); /*生成减法指令*/}}这样就完成了对+=运算和-=运算的添加。