中间代码生成实验报告
- 格式:doc
- 大小:75.00 KB
- 文档页数:17
一 、 实验目的 通过在实验二的基础上,增加中间代码生成部分,使程序能够 对实验二中的识别出的赋值语句,if语句和while语句进行语义分析,生成四元式中间代码。
二 、 实验方法 实验程序由c语言完成,在Turboc 2.0环境中调试通过。 语义分析程序的基本做法是对文法中的每个产生式分别编写一个语义分析子程序,当程序语法部分进行推倒或规约时,就分别调用各自的语义分析程序。当语法分析结束时,语义分析也就结束了。 在本实验程序中,当语法分析部分识别出语法正确的句子时,就进入content函数(当语法分析识别出不正确的句子时,不进入content函数,也就是不进行语义分析),然后根据句子的类型进行分类,进入不同的语义处理部分。 对于赋值语句,关键是产生正确的处理算术表达式E的四元式。程序中的ec函数的功能就是产生算术表达式的四元式,在ec函数中使用了两个栈idshed,opshed,分别是算术表达式的数据栈和符号栈。每次提取一个数字和一个算符,然后将算符与与栈顶算符进行优先级比较,优先级高则将单前数字和算符进栈,低或者相等的话则将当前栈顶元素进行合并,产生四元式。直至整个算术表达式结束。其中还有一些细节问题,具体的做法可以参看程序。 对于实验给定的if语句的文法格式,条件判断式C只中可能是>或者<=两种关系,不可能是布尔表达式,这样程序就简单的多了。 通过ec函数可以产生条件判断式C中的E的四元式,然后只要加上转向四元式就可以了。本实验程序中只给出真出口的转向四元式,没有给出假出口的转向四元式,这在实际中是不可以的,但在本实验中,实际上是对每条独立的语句进行语法分析,给出假出口转向四元式实际上意义不大,而且假出口转向语句的转移目标必须要到整个语句分析结束以后才可以知道,这样就要建立栈,然后回填,这样会使程序复杂很多,所以没有加上假出口转向四元式。 对于while语句,具体的做法和if语句差不多,所不同的是当while语句结束时,要多出一条无条件转向四元式,重新转到条件判断式C的第一条四元式。当要产生无条件转向四元式时,它的转向目标C的第一条四元式已经产生了,所以具体的做起来是不太困难的。只要记下当前while中的C的第一条四元式的位置,填上就可以了。 整个程序的结束是当读入“ . ”时,程序就中止。 程序中还有很多细节问题,具体的可以后面的附录:程序的完整代码。
三 、 测试程序 ff:=6+6*6-; if sl>89+56*67 then f:=7*7+4; ff:=6+6*6-6%4+8; if sl+78*76>89*56+67 then while a-7>98+45*45 do f:=7*7+4;
. 四 、 运行结果
首先对测试程序进行语法分析,识别出正确的句子,当识别出正确的句子时,就对当前句子进行语义分析,而语法不正确的句子不进行语义分析。
ff:=6+6*6- Error(4):Except ID or NUM ; Error(2):Syntax error if sl>89+56*67 then f:=7*7+4; success (1) [ *, 56, 67, T1 ] (2) [ +, 89, T1, T2 ] (3) [j>, sl, T2, (4) ] (4) [ *, 7, 7, T3 ] (5) [ +, T3, 4, T4 ] (6) [ :=, T4, -, f ] ff:=6+6*6-6%4+8; success (7) [ *, 6, 6, T5 ] (8) [ +, 6, T5, T6 ] (9) [ %, 6, 4, T7 ] (10) [ -, T6, T7, T8 ] (11) [ +, T8, 8, T9 ] (12) [ :=, T9, -, ff ] if sl+78*76>89*56+67 then while a-7>98+45*45 do f:=7*7+4; success (13) [ *, 78, 76, T10 ] (14) [ +, sl,T10, T11 ] (15) [ *, 89, 56, T12 ] (16) [ +,T12, 67, T13 ] (17) [j>, T11, T13, (18) ] (18) [ -, a, 7, T14 ] (19) [ *, 45, 45, T15 ] (20) [ +, 98,T15, T16 ] (21) [j>, T14, T16, (22) ] (22) [ *, 7, 7, T17 ] (23) [ +,T17, 4, T18 ] (24) [ :=, T18, -, f ] (25) [j, _, _, (18)] . Error(2):Syntax error 五 、 实验小结
终于完成了编译原理的三次实验,这几次实验使我们更彻底地巩固了编译原理。从詷法分析到语法分析直到这次的中间代码都是看似简单的基础知识,却花了不少时间对课本多次反复研究、请教学习,进行深层次地探讨才找出编程时出现的种种问题。还有很多程序方面很细节的问题,很容易被突略,却往往又是关键。 总地来说,虽然实验做得很辛苦,但真的可以从实验当中学习到很多,认识到很多。并对编译理解也透彻了许多,比较清晰地掌握了编译程序的原理和方法。
附录: #include #include #include #define reser 20
char *charstring="abcdefghijklmnopqrstuvwxyz"; char *numstring="0123456789"; char *strstring="abcdefghijklmnopqrstuvwxyz0123456789"; char reserve[reser][10]; char idshed[40][10],opshed[40][10]; char token[15],id[15],sym[15]; char line[80],tempp[240]; char ch,ch1,temp,tem; char tx[10]; char tn[4],signt1[20],signt2[20],ju[20]; int cc,ii,k,num,kind,t,e4=0,e3=0,judge=1,row1=0; int startc,idsh=0,opsh=0,tt=1,nn=1,signwh,over=0,adds=0,whs=0,pp=0; int li=0; char filename[15]; FILE *fp;
void getch(){ if(li==0) { if (cc==ii){ cc=1; ii=0;
if (row1==1) fseek(fp,-1,1); /*读行首字符将指针退回一格,若是整个文本的开头,则不需要*/ line[0]=fgetc(fp); row1=1;
while(((temp=fgetc(fp))!='\n') && (temp!=EOF)) { line[cc]=temp; cc++; tempp[pp]=temp; pp++; } line[cc]=' '; /*将缓冲带后加上一个空字符,以便行和行之间号区分 */ cc++;
tempp[pp]=' '; pp++;
while(((temp=fgetc(fp))=='\n') && (temp!=EOF)); /* 跳过空行*/ line[cc]='\0'; } tem=line[ii]; ii++; ch=tem; } else{ ch=tempp[pp]; pp++; } }
void getnbc(){ getch(); while (ch==' ') getch(); if (ch=='{') { do {getch();} while( ch=='}'); getnbc(); } }
void retract(){ ii--; }
void ret(){ pp--; }
int jchar(char ch){ /* 判断ch是不是字母 */ if(strchr(charstring,ch)==0) return 0; else return 1; }
int jnum(char ch){ /* 判断ch是不是数字 */ if(strchr(numstring,ch)==0) return 0; else return 1; }
int jstr(char ch){ /* 判断ch是不是字母或数字 */ if(strchr(strstring,ch)==0) return 0; else return 1; }
void advance() { getnbc(); kind=0;
if (jchar(ch)==1) { k=0; do { if(k<15) { token[k]=ch; k++;} getch(); } while(jstr(ch)==1);
if (li==0) retract(); else ret();
token[k]='\0'; /*截去token中的无用字符*/ strcpy(id,token); for(t =0;t<=20;t++) {if(strcmp(reserve[t],id)==0) break;} if ( t<=20 ) kind=t ; else kind=21;