1、编译原理实验报告软件131 陈万全 132852一、 需求分析通过对一个常用高级程序设计语言的简单语言子集编译系统中词法分析、语法分析、语义处理模块的设计、开发,掌握实际编译系统的核心结构、工作流程及其实现技术,获得分析、设计、实现编译程序等方面的实际操作能力,增强设计、编写和调试程序的能力。通过开源编译器分析、编译过程可视化等扩展实验,促进学生增强复杂系统分析、设计和实现能力,鼓励学生创新意识和能力。实验项目实 验 内 容1、词法分析程序设计与实现构造具有关键字、运算符、标识符、无符号常数等单词的词法分析程序。输入由符合及不符合规定单词类别结构的各类单词组成的源程序。输出单词串的二元式编码(
2、CLASS,VALUE)。2、语法分析程序设计与实现将词法分析程序输出的单词串作为输入,针对常见的表达式、赋值语句、条件语句、循环语句等语法成分,选择有代表性的语法分析方法,如递归下降法、算符优先分析、LL(1)、LR等方法之一,设计实现相应的语法分析程序。3、语义分析程序设计与实现对各语法单位增加语义子程序,将表达式或可执行语句翻译为四元式序列输出,并能进行错误检查与处理,将错误信息输出。1、 词法分析程序设计与实现假定一种高级程序设计语言中的单词主要包括五个关键字begin、end、if、then、else;标识符;无符号常数;六种关系运算符;一个赋值符和四个算术运算符,试构造能识别这些单
3、词的词法分析程序。输入:由符合和不符合所规定的单词类别结构的各类单词组成的源程序文件。输出:把所识别出的每一单词均按形如(CLASS,VALUE)的二元式形式输出,并将结果放到某个文件中。对于标识符和无符号常数,CLASS字段为相应的类别码的助记符;VALUE字段则是该标识符、常数的具体值;对于关键字和运算符,采用一词一类的编码形式,仅需在二元式的CLASS字段上放置相应单词的类别码的助记符,VALUE字段则为“空”。2、 语法分析程序设计与实现选择对各种常见高级程序设计语言都较为通用的语法结构算术表达式的一个简化子集作为分析对象,根据如下描述其语法结构的BNF定义G2,任选一种学过的语法分析
4、方法,针对运算对象为无符号常数和变量的四则运算,设计并实现一个语法分析程序。G2: | + | - | * | / | ()若将语法范畴、和分别用E、T、F和i代表,则G2可写成:G2E:E T | E+T | E-T T F | T*F | T/F F i | (E)输入:由实验一输出的单词串,例如:UCON,PL,UCON,MU,ID 输出:若输入源程序中的符号串是给定文法的句子,则输出“RIGHT”,并且给出每一步分析过程;若不是句子,即输入串有错误,则输出“ERROR”,并且显示分析至此所得的中间结果,如分析栈、符号栈中的信息等,以及必要的出错说明信息。3、语义分析程序设计与实现对文法
5、G2中的产生式添加语义处理子程序,完成运算对象是简单变量(标识符)和无符号数的四则运算的计值处理,将输入的四则运算转换为四元式形式的中间代码。输入:包含测试用例(由标识符、无符号数和+、*、/、(、)构成的算术表达式)的源程序文件。输出:将源程序转换为中间代码形式表示,并将中间代码序列输出到文件中。若源程序中有错误,应指出错误信息二、 设计思路1、 词法分析程序设计与实现1)单词分类为了编程的实现。我们假定要编译的语言中,全部关键字都是保留字,程序员不得将它们作为源程序中的标识符;作了这些限制以后,就可以把关键字和标识符的识别统一进行处理。即每当开始识别一个单词时,若扫视到的第一个字符为字母,
6、则把后续输入的字母或数字字符依次进行拼接,直至扫视到非字母、数字字符为止,以期获得一个尽可能长的字母数字字符串,然后以此字符串查所谓保留字表(此保留字表要事先造好),若查到此字符串,则取出相应的类别码;反之,则表明该字符串应为一标识符。表1 语言中的各类单词符号及其分类码表单词符号类别编码类别码的助记符单词值begin1BEGINend2ENDif3IFthen4THENelse5ELSE标识符6ID字母打头的字母数字串无符号常数7UCON机内二进制表示8LT=9LE=10EQ11NE12GT=13GE:=14IS+15PL-16MI*17MU/18DI2) 词法分析器的设计函数GETCHAR
7、:每调用一次,就把扫描指示器当前所指示的源程序字符送入字符变量ch,然后把扫描指示器前推一个字符位置。字符数组TOKEN:用来依次存放一个单词词文中的各个字符。函数CAT:每调用一次,就把当前ch中的字符拼接于TOKEN中所存字符串的右边。函数LOOKUP:每调用一次,就以TOKEN中的字符串查保留字表,若查到,就将相应关键字的类别码赋给整型变量c;否则将c置为零。函数RETRACT:每调用一次,就把扫描指示器回退一个字符位置(即退回多读的那个字符)。函数OUT:一般仅在进入终态时调用此函数,调用的形式为OUT(c,VAL)。图1 识别表I所列语言中的部分单词的DFA及相关的语义过程3)词法分
8、析程序的实现编写的扫描器:char TOKEN20,TOKEND20,TOKENDO20;int lookup (char*);void out (int, char*);void report_error (void);/extern void LEX(void);int siagn=0;/标志位FILE *fp1; char *KeyWordTableMAX_KEY_NUMBER=begin,end, if, then, else, KEY_WORD_END;/* 查保留字表,判断是否为关键字 */int lookup (char *token)int n=0;while (strcmp(
9、KeyWordTablen, KEY_WORD_END) /*strcmp比较两串是否相同,若相同返回0*/if (!strcmp(KeyWordTablen, token) /*比较token所指向的关键字和保留字表中哪个关键字相符*/return n+1; /*根据单词分类码表I,设置正确的关键字类别码,并返回此类别码的值*/break;n+;return 0;void scanner_example (FILE *fp)char ch; int i, c, isd,cpoint; double o;ch=fgetc (fp);/fgetc函数 在文件中读取一个字符if (isalpha
10、(ch) /*it must be a identifer! 它必须是一个标识符 判断字符ch是否为英文字母,若为小写字母,返回2,若为大写字母,返回1。若不是字母,返回0*/TOKEN0=ch; ch=fgetc(fp); i=1;while (isalnum (ch)|ch=.)/isalnum函数 判断ch是否为空 当ch为数字0-9或字母a-z及A-Z时,返回非零值,否则返回零if(ch=.)cpoint=-1;/标志字符串中有小数点TOKENi=ch; i+;ch=fgetc (fp);TOKENi= 0;if(ch=|ch=|ch=:) fseek (fp,-2,1);siagn=
11、1;else fseek (fp,-1,1);/fseek(fp,-1,1); /* retract fseek函数 每调用一次,就把扫描指示器回退一个字符位置(即退回多读的那个字符)*/i=0;if(TOKENi=o|TOKENi=O)i+;if(TOKENi=x|TOKENi=X)i+;while(TOKENi!=0)if(!isdigit(TOKENi)|TOKENi!=a|TOKENi!=b|TOKENi!=c|TOKENi!=d|TOKENi!=e|TOKENi!=f|TOKENi!=A|TOKENi!=B|TOKENi!=C|TOKENi!=D|TOKENi!=E|TOKENi!=
12、F|TOKENi!=.)isd=-1;isd=16;/标志字符串十六进制i+;else if(TOKENi=0|TOKENi=1|TOKENi=2|TOKENi=3|TOKENi=4|TOKENi=5|TOKENi=6|TOKENi=7|TOKENi=.)isd=8;/标志字符串八进制i+;while(TOKENi!=0)if(TOKENi!=0|TOKENi!=1|TOKENi!=2|TOKENi!=3|TOKENi!=4|TOKENi!=5|TOKENi!=6|TOKENi!=7|TOKENi!=.)isd=-1;isd=8;i+;if(isd=8)strncpy(TOKEND,TOKEN
13、+1,strlen(TOKEN)-1);/拷贝函数/printf(%o,atof(TOKEND);o=octal(TOKEND);/printf(%g,o);sprintf(TOKENDO, %g,octal(TOKEND);out(OCTAL,TOKENDO);else if(isd=16)strncpy(TOKEND,TOKEN+2,strlen(TOKEN)-2);/拷贝函数o=octal(TOKEND);/printf(%g,o);sprintf(TOKENDO, %g,hex(TOKEND);out(HEX,TOKENDO);elseif(cpoint=-1)report_error
14、();/当字符串中有小数点时,为错误elsec=lookup (TOKEN);/looup查询函数 查找保留字if (c=0) out (ID,TOKEN); else out (c, );/out函数 输出功能elseif(isdigit(ch)/isdigit函数 检查参数c是否为阿拉伯数字0到9。/*TOKEN0=ch; ch=fgetc(fp); i=1;while(isdigit(ch)|ch=-|ch=E|ch=e|ch=.)TOKENi=ch; i+;ch=fgetc(fp);TOKENi= 0;*/int Class;fseek (fp,-1,1);Class=LEX(fp);
15、if(Class=1)char pi30 = ;itoa(ICON,pi,10);out(INT,pi);else if(Class=2)char pd30 = ;sprintf(pd, %g,FCON);out(DOUBLE,pd);elsereport_error( );/fseek(fp,-1,1);if(chf=|chf=|chf=:|chf=+|chf=-|chf=*|chf=/) fseek (fp,-2,1);siagn=1;else fseek (fp,-1,1);siagn=1;elseswitch(ch)case ) ch=fgetc(fp);if(isalnum (ch)
16、 fseek (fp,-2,1);else fseek (fp,-1,1);siagn=1;out (NE, );elsech=fgetc(fp);if(isalnum (ch) fseek (fp,-2,1);else fseek (fp,-1,1);siagn=1;out (LT, );break;case =:ch=fgetc(fp);if(isalnum (ch) fseek (fp,-2,1);else fseek (fp,-1,1);siagn=1;out(EQ, );break;case : ch=fgetc(fp);if(ch=)ch=fgetc(fp);if(isalnum
17、(ch) fseek (fp,-2,1);else fseek (fp,-1,1); siagn=1;out(GE, );elsech=fgetc(fp);if(isalnum (ch) fseek (fp,-2,1);else fseek (fp,-1,1);siagn=1;out(GT, );break;case :ch=fgetc(fp);if(ch=)ch=fgetc(fp);if(isalnum (ch) fseek (fp,-2,1);else fseek (fp,-1,1);siagn=1;out(IS, );else report_error();break;case+:ch=
18、fgetc(fp);if(isalnum (ch) fseek (fp,-2,1); else fseek (fp,-1,1);siagn=1;out(PL, ); break;case-:ch=fgetc(fp);if(isalnum (ch) fseek (fp,-2,1);else fseek (fp,-1,1);siagn=1;out(MI, ); break;case*:ch=fgetc(fp);if(isalnum (ch) fseek (fp,-2,1);else fseek (fp,-1,1);siagn=1;out(MU, ); break;case/:ch=fgetc(fp
19、);if(isalnum (ch) fseek (fp,-2,1);else fseek (fp,-1,1);siagn=1;out(DI, ); break;case :break;/当遇到空格时继续向下走default: report_error( ); break;/report_error函数 单词错误时输出的内容if(ch= |siagn=1)fseek(fp,1,1); siagn=0;scanner_example(fp);if(ch=EOF)return;/ return;void report_error()printf(errorn);void out(int c, cha
20、r *vp)char* cl = NULL;switch (c)case 1:cl = BEGIN; break;case 2:cl = END; break;case 3:cl = IF; break;case 4:cl = THEN; break;case 5:cl = ELSE; break;case 6:cl = ID; break;case 7:cl = UCON; break;case 8:cl = LT; break;case 9:cl = LE; break;case 10: cl = EQ; break;case 11: cl = NE; break;case 12: cl
21、= GT; break;case 13: cl = GE; break;case 14: cl = IS; break;case 15: cl = PL; break;case 16: cl = MI; break;case 17: cl = MU; break;case 18: cl = DI; break;case 19: cl = UCON; break;case 20: cl = DOUBLE; break;case 21: cl = OCTAL; break;case 22: cl = HEX; break;printf(e(%s,%s)n,cl, vp);fprintf(fp1,e
22、(%s,%s)n,cl, vp);fseek(fp1,0,1); void main()char *fname=test1.txt;/读取为文件fp FILE *fp;if(fp1=fopen(asd.txt,w)=NULL) /*建立文件 写入文件的文件fp1*/ printf(nopen file error); getchar(); exit(1); if(fp=fopen(fname,r)=NULL)printf(nSorry, Cant open the file!n);getchar();exit(0); else scanner_example(fp); fclose(fp);f
23、close(fp1);本程序从文件test1.txt中读入要分析的单词并把扫描的结果输入asd.txt文件中;利用gechar()方法从文件中一个个读入字符,并采用递归的方法对字符多次读入直到读到文件的结尾停止。字符读入程序后采用图一的方法进行分析解决。4)无符号常数的识别针对扫描程序所得到的数字我们可以进行无符号数的处理。加入了语义过程说明的识别无符号数的状态矩阵,编写词法分析程序,部分实现代码如程序二所示。表2 包含语义处理过程的识别无符号数的状态矩阵程序2 单词分类码为UCON的无符号数的识别程序7 假定无符号常量的类数为7#define ClassOther 200#define En
24、dState -1int w,n,p,e,d;int Class=-1; /Used to indicate class of the word 用于表示单词的类型 1位int 2为double int ICON;double FCON;static int CurrentState; /Used to present current state, the initial value:0 用于当前状态,初始值:0int GetChar (FILE *p);int EXCUTE (int,int);int LEX (FILE *p);int HandleOtherWord (void)retur
25、n ClassOther;int HandleError (void)printf (Error!n); return 0;int GetChar (FILE *p) chf=fgetc(p); if(isdigit(chf) d=chf-0;return DIGIT; if (chf=.) return POINT; if (chf=E|chf=e) return POWER; if (chf=+) return PLUS; if (chf=-) return MINUS; return OTHER;int EXCUTE (int state, int symbol) switch (sta
26、te) case 0:switch (symbol) case DIGIT: n=0;p=0;e=1;w=d;CurrentState=1;break; case POINT: w=0;n=0;p=0;e=1;CurrentState=3;break; default: HandleOtherWord( );Class=ClassOther; CurrentState=EndState; break; case 1:switch (symbol) case DIGIT: w=w*10+d;break; /CurrentState=1 case POINT: CurrentState=2;bre
27、ak; case POWER: CurrentState=4;break; default: ICON=w;Class=1;CurrentState=EndState; break; case 2:switch (symbol) case DIGIT: n+;w=w*10+d;break; case POWER: CurrentState=4;break; default: FCON=w*pow(10,e*p-n);Class=2;CurrentState=EndState; break; case 3:switch (symbol) case DIGIT: n+;w=w*10+d;Curre
28、ntState=2;break; default: HandleError( );CurrentState=EndState; break; case 4:switch (symbol) case DIGIT: p=p*10+d;CurrentState=6;break; case MINUS: e=-1;CurrentState=5;break; case PLUS: CurrentState=5;break; default: HandleError( );CurrentState=EndState; break; case 5:switch (symbol) case DIGIT: p=
29、p*10+d;CurrentState=6;break; default: HandleError( );CurrentState=EndState; break; case 6:switch (symbol) case DIGIT:p=p*10+d;break; default: FCON=w*pow(10,e*p-n);Class=2;CurrentState=EndState; break; return CurrentState; int LEX (FILE *p) int ch; CurrentState=0; while (CurrentState!=EndState)ch=Get
30、Char(p);EXCUTE (CurrentState,ch); return Class; 5)八进制与十六进制数的处理在无符号数的处理过程中没有设计八进制与十六进制数转换成十进制的数,但为了方面以后的使用我们进入进制的转换。设计思路:判断八进制是以0开头,十六进制为0X开头,并且判断后面所跟数字是否超出八进制或十六进制的范围,若超出则错误,如不超出则用8或16转换为相应十进制数字再次读入写个字符循环,直到数字结束。程序3 单词分类码为八进制的无符号数的识别程序double octal(char *TP)int x,y=-1,ypoint=0;/计数 ypoint=-1记录有小数点 y记录
31、是第几位小数int m=0;double FINALL=0.0;/放结果while(*TP!=0)if(*TP=.)ypoint=-1;elsex=*TP-0;if(ypoint!=-1)FINALL=FINALL*8+x;if(ypoint=-1)FINALL=FINALL+x*pow(8,y);y-;TP+;return FINALL;程序4 单词分类码为十六进制的无符号数的识别程序double hex(char *TP)int x,y=-1,ypoint=0;/计数 ypoint=-1记录有小数点 y记录是第几位小数int m=0;double FINALL=0.0;/放结果while(
32、*TP!=0)if(*TP=.)ypoint=-1;elseif(ypoint!=-1)if(*TP=0&*TP=a&*TP=A&*TP=0&*TP=a&*TP=A&*TP=F)x=*TP-a+1;FINALL=FINALL+x*pow(16,y);y-;TP+;return FINALL;2、语法分析程序设计与实现语法分析,采用的是算符优先文法实现;重点与难点为根据文法求出FirstVt与LastVt集合,构造出算符表。语法分析程序的输入结构为词法分析的输出结果。数字的标志位都设为i 如(i,25.8);运算符的标志位为其本身如(+, )其他符号暂不计算,以方便语法分析程序的编写。算符优先总
33、流程图 图2开始初始化FIRSTVT集和LASTVT集求出文法终结符集是否为文法输出非终结符FIRSTVT集和LASTVT集生成并输出算法分析表输入需要验证的字符串,以#结束输出结果输出结果结束输入文法规则和数目NY1) 判断是否为算符文法程序5/judge1是判断是否是算符文法:若产生式中含有两个相继的非终结符则不是算符文法int judge1(int n)int j=3,flag=0;for(int i=0;i=n;i+)while(strij!=0)char a=strij;char b=strij+1;if(IsLetter(a)&IsLetter(b)flag=1;break;els
34、e j+; if(flag=1)return 0; elsereturn 1;结果:输入文件输入结果2) 判断是否为算符优先文法程序5judge2是判断文法G是否为算符优先文法:若不是算符文法或若文法中含空字或终结符的优先级不唯一则不是算符优先文法void judge2(int n)for(int i=0;i=n;i+)if(stri3=|judge1(n)=0|FLAG=1)/代表空字cout该文法不是算符优先文法!n)cout该文法是算符优先文法!endl; 3) 求FristVt( )和LastVt()集合程序6 /createF函数是用F数组存放每个终结符与非终结符和组合,并且值每队的
35、标志位为0;F数组是一个结构体void createF(int n)int k=0,i=1;char g;char t10;/t数组用来存放非终结符t0=str00;while(i=n) if(tk!=stri0) k+;tk=stri0;g=tk;i+; else i+;kk=0; char c;for(i=0;i=n;i+) int j=3; while(strij!=0) c=strij; if(IsLetter(c)=0) if(!search1(r,kk,c) rkk=c;kk+;/r数组用来存放终结符 j+; m=0;for(i=0;ik;i+) for(int j=0;jkk-1;j+) Fm.R=ti; Fm.r=rj; Fm.flag=0; m+; /search函数是将在