华科编译原理实验报告(共26页).docx
精选优质文档-倾情为你奉上课 程 实 验 报 告 课程名称: 编 译 原 理 专业班级: CS1209 学 号: 姓 名: 指导教师: 周时阳 报告日期: 2015.6.28 计算机科学与技术学院专心-专注-专业目 录1 课程实验概述词法分析(Lexical Analysis) 是编译的第一阶段。词法分析器的主要任是读入源程序的输入字符、将他们组成词素,生成并输出一个词法单元序列,每个词法单元对应一个词素。这个词法单元序列被输出到语法分析器进行语法分析。词法分析器的功能输入源程序,按照构词规则分解成一系列单词符号。单词是语言中具有独立意义的最小单位,包括关键字、标识符、运算符、界符和常量等(1) 关键字 是由程序语言定义的具有固定意义的标识符。例如,Pascal 中的begin,end,if,while都是保留字。这些字通常不用作一般标识符。(2) 标识符 用来表示各种名字,如变量名,数组名,过程名等等。(3) 常数 常数的类型一般有整型、实型、布尔型、文字型等。(4) 运算符 如+、-、*、/等等。(5) 界符 如逗号、分号、括号、等等。词法分析器所输出单词符号常常表示成如下的二元式: (单词种别,单词符号的属性值)单词种别通常用整数编码。标识符一般统归为一种。常数则宜按类型(整、实、布尔等)分种。关键字可将其全体视为一种。运算符可采用一符一种的方法。界符一般用一符一种的方法。对于每个单词符号,除了给出了种别编码之外,还应给出有关单词符号的属性信息。单词符号的属性是指单词符号的特性或特征。 2 实验一 词法分析器的实现2.1 问题描述2.1.1 实验内容1.要完成一个完整的编译器,需要实现的功能如下:(1)组织源程序的输入(2)拼出单词并查找其类别编号,形成二元式输出,得到单词流文件(3)删除注释、空格和无用符号(4)发现并定位词法错误,需要输出错误的位置在源程序中的第几行。将错误信息输出到屏幕上。(5)对于普通标识符和常量,分别建立标识符表和常量表(使用线性表存储),当遇到一个标识符或常量时,查找标识符表或常量表,若存在,则返回位置,否则返回0并且填写符号表或常量表。标识符表结构:变量名,类型(整型、实型、字符型),分配的数据区地址注:词法分析阶段只填写变量名,其它部分在语法分析、语义分析、代码生成等阶段逐步填入。常量表结构:常量名,常量值2能对任何S语言源程序进行分析 在运行词法分析程序时,应该用问答形式输入要被分析的S源语言程序的文件名,然后对该程序完成词法分析任务。2.1.2实验原理 词法分析程序的算法思想:算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。2.2 系统设计2.2.1实验前准备(1)在扫描程序的过程中,注释被忽略。(2)各种单词符号对应的编号(如下): 表2-1:编号对照表单词符号编号单词符号编号auto1double2int3struct4break5else6long7switch8case9enum10register11typedef12char13extern14return15union16const17float18short19unsigned20continue21for22signed23void24default25goto26sizeof27volatile28do29while30static31if32a33b34f35n36t37v3839?4041”42043ddd44 xhh45数字46标识符47#48(49)505152535455*56:5758%5960+61?62=63|64&65!66<67>68>=69=70>>71!=72<<73&&74<=75|76+77?=78-79-80->81“82%A(A可为dsc)83;84_85/868788899091其他类别99(2)实验的状态转换图如图2-2示: 图2-2:实验状态转换图2.2.2实验流程图(3) 系统流程图如下图2-3所示: 开始输入需要扫描的文件名输入存放结果文件名P处跳转扫描其他符号文件是否为空 Y返回,退出ch是否为引号 N预读一位ch跳转扫描引号 NY行数加1ch是否为空格Y Nch是否为/跳转扫描注释Ych是否为字母或下划线N Ych是否为数字或 N跳转扫描数字Y跳转扫面文件单词及保留字 N 图2-3:系统流程图(4) 扫描注释模块流程图如下所示: 图2-4:注释分析模块(5) 扫描数字模块流程图如图2-5所示: 图2-5:数字分析模块流程图(6) 扫描引号模块流程图如图2-6所示: 图2-6:引号分析模块流程图(7) 扫描单词模块流程图如图2-7所示: 图2-7:单词分析模块流程图(8) 扫描其他字符模块流程图如图2-8所示: 图2-8:其他符号分析模块流程图2.3 系统实现(1) 确定关键字,一共有32个,把32个字符保存在定义的数组中,其下标就是每个关键字的编码,这样设计的原因是方便分析。其他字符根据自定义的编号,扫描到后直接返回它们的编号。(2) 空格有空白、制表符和换行符组成。空格一般用来分隔ID、SUM、运算符、界符和关键字,词法分析阶段通常被忽略。(3) 注释被忽略。(4) 确定设计的词法分析程序的功能: 输入:建立单独的txt文件保存所给文法的源程序字符串,我的是test.txt 文件。 输出:由行号、字符串和编号构成,分析的结果打印在显示在屏幕上,同时保存在另一个txt文件中,我命名的文件是a.txt。2.4 效率分析测试语句为:Program example ; var int j,m,n; Begin /*there is * a / comment*/ i:=2; j:=6; m:=3; /there is a comment n:=j+m; If n>=3 and n<5 then j:=j-1; end . 2.4.1程序的执行结果结果截图如图2-9所示: 图2-9:结果截图打开a.txt文件,如图2-10所示,结果与屏幕上的结果一致。 图2-10:a.txt文件内容2.4.2实验程序的优点和缺陷 优点:该程序可以出来多种情况,并且运行效率高。缺陷:程序中没有出错处理能力,并且在分析长代码时,不能全部分析完成,由于时间关系没有来得及修改这个bug。3 实验总结与评价 我在这次词法分析器的设计过程中学到了很多东西,其中最大的收获是对于编译原理中的词法分析这一过程理解的更加清楚明了。没做实验之前根本就不理解其中的原理,其次,是在使用C编程的能力有所提高,因为在程序分析时,要分析特别细,稍有疏忽,就可能出错。另外,在该词法分析器设计中也有一些不足的地方,比如没有出错处理功能。分析结果输出方式为:用一个存放结果的字符串数组,并用指针指向它,输出结果正确,总的来说,这次实验达到了其初衷,实现了规定的功能。源代码:#include<stdio.h>#include<stdlib.h>#include <ctype.h>#include<string.h>int guanjz(char ch1)/关键字和标识符判断 int i;char ch2329="auto","double","int","struct","break","else","long","switch","case","enum","register","typedef","char","extern","return","union","const","float","short","unsigned","continue","for","signed","void","default","goto","sizeof","volatile","do","while","static","if"/定义关键字集,共32个for(i=0;i<32;i+)/逐个比对如果为关键字则返回类别i+1if(!strcmp(ch1,ch2i) return i+1;return 47;/否则返回一般标识符类void main()FILE *fp,*fp1;int k=0;int i=0;int j=0;int hanjsq=1;/行计数器,保存行号int guanjz(char ch1);/关键字和标识符判断char ch,infile15,outfile15;/定义输入和输出文件名printf("Enter the infile name:");scanf("%s",infile);printf("Enter the outfile name:");scanf("%s",outfile);if(fp = fopen(infile,"r") = NULL)/打开需要扫描的文件printf("cannot open filen");exit(0);if(fp1 = fopen(outfile,"w") = NULL)/打开需要存入的文件printf("cannot open filen");exit(0);printf("n*开始进行词法分析*n");printf("n行号字符串编号nn");fprintf(fp1,"行号字符串编号n");fprintf(fp1,"*n");while(!feof(fp)ch=fgetc(fp);if(ch=10) hanjsq+;if(isalpha(ch) | ch='_')/如果第一个字符为字母或下划线则判断为标识符int i=0;char ch130;/假定每个标识符最长为ch1i+=ch;/将ch保存到ch10中并使i自加1while(!feof(fp)ch=fgetc(fp);if(ch=10) hanjsq+;/如果ch为换行符,则行计数器自加1if(isalpha(ch) | isdigit(ch) | ch='_')/如果ch为字母、数字或下划线就把ch放到ch1i中并使i自加1ch1i+=ch;if(ch='.')/如果ch为小数点则判断是否为头文件if(ch=fgetc(fp)='h')/如果小数点后一位为h则判定其为头文件if(ch=10) hanjsq+;ch1i+='.'ch1i+='h'ch1i='0'/把结束标志放到ch1i中作为单词结束标志printf("line %d:%s83n",hanjsq,ch1);/以字符串形式输出ch1fprintf(fp1,"line %d:%s83n",hanjsq,ch1);break;else/如果小数点后一位不是h则判定其为标识符fseek(fp,-1,1);/fp回退1ch1i='0'/把结束标志放到ch1i中作为单词结束标志printf("line %d:%s%dn",hanjsq,ch1,guanjz(ch1);/以字符串形式输出ch1fprintf(fp1,"line %d:%s%dn",hanjsq,ch1,guanjz(ch1);break;if(!isalpha(ch) && !isdigit(ch) && ch!='_' && ch!='.')/如果ch不为字母、数字、下划线和点时判断其为标识符ch1i='0'printf("line %d:%s%dn",hanjsq,ch1,guanjz(ch1);fprintf(fp1,"line %d:%s%dn",hanjsq,ch1,guanjz(ch1);break; if(isdigit(ch) | ch='-')/如果ch为数字或'-'if(isdigit(ch)/如果ch为数字printf("line %d:%c",hanjsq,ch);fprintf(fp1,"line %d:%c",hanjsq,ch);while(!feof(fp)ch=fgetc(fp);/预读一位如果ch为数字和点则循环输出if(isdigit(ch) | ch='.')printf("%c",ch);fprintf(fp1,"%c",ch);else/否则视为数字结束printf("46n");fprintf(fp1,"46n");fseek(fp,-1,1);/回退一位ch='0'/置ch为0,以免影响下面误判并顺利退出扫描数字break;if(ch='-')/如果ch为'-'ch=fgetc(fp);/预读一位if(ch='-')/如果ch还是为'-'则判断为自减符'-'printf("line %d:-80n",hanjsq);fprintf(fp1,"line %d:-80n",hanjsq);if(ch='>')/如果ch为'>',则判断为结构体运算符'->'printf("line %d:ch1i=->81n",hanjsq);fprintf(fp1,"line %d:->81n",hanjsq);if(isdigit(ch)/如果ch为数字则可能为减号或负号fseek(fp,-3,1);/回退3为判断ch=fgetc(fp);if(isdigit(ch)/如果ch为数字则判断'-'为减号 ch=fgetc(fp);printf("line %d:%c79n",hanjsq,ch);fprintf(fp1,"line %d:%c79n",hanjsq,ch);else /否则判断'-'为负号ch=fgetc(fp);printf("line %d:%c",hanjsq,ch);fprintf(fp1,"line %d:%c",hanjsq,ch);while(!feof(fp)ch=fgetc(fp);/预读一位如果ch为数字和点则循环输出if(isdigit(ch) | ch='.')printf("%c",ch);fprintf(fp1,"%c",ch);else/否则视为数字结束printf("46n");fprintf(fp1,"46n");fseek(fp,-1,1);/回退1break;if(ch='/')/如果ch为'/'则可能为注释ch=fgetc(fp);/读下一个字符if(ch=10)hanjsq+;if(ch='/')/如果该字符也为'/'则判断为注释一行while(fgetc(fp)!=10);if(ch=10)hanjsq+;/直到遇到换行符出现才认为注释结束if(ch='*')/如果该字符为'*'则判断为注释多行/直到出现'*/'才认为注释结束while(!feof(fp)ch=fgetc(fp);if(ch=10)hanjsq+;if(ch='*')/出现'*'/且接着出现'/'if(ch=fgetc(fp)='/')break;else/否则原样输出'/'printf("line %d:/83n",hanjsq);fprintf(fp1,"line %d:/83n",hanjsq);fseek(fp,-1,1);/回退1break;if(ch='"')/出现引号int i=0;printf("line %d:%c82n",hanjsq,ch);fprintf(fp1,"line %d:%c82n",hanjsq,ch);printf("line %d:",hanjsq);fprintf(fp1,"line %d:",hanjsq);while(!feof(fp)/先整体输出引号内所有字符并定为第99类ch=fgetc(fp);i+;/用于积累回退长度if(ch=10)hanjsq+;if(ch!='"')if(ch!=32)printf("%c",ch);fprintf(fp1,"%c",ch);else break;printf("99n");fprintf(fp1,"99n");fseek(fp,-i,1);/回退到引号开始for(;i>0;i-)ch=fgetc(fp);if(ch=92)/如果ch为''则可能为转义字符char ch513="abfntv?'"0"/转义字符集ch=fgetc(fp);/预读一位for(k=0;k<12;k+)/如果为转义字符则输出if(ch=ch5k)printf("line %d:%c%dn",hanjsq,ch,k+33);fprintf(fp1,"line %d:%c%dn",hanjsq,ch,k+33);if(ch='d' && isdigit(fgetc(fp) && isdigit(fgetc(fp)/任意字符转换为三位八进制fseek(fp,-2,1);printf("line %d:%c%c44n",hanjsq,fgetc(fp),fgetc(fp);fprintf(fp1,"line %d:%c%c44n",hanjsq,fgetc(fp),fgetc(fp);if(ch='x' && isdigit(fgetc(fp) && isdigit(fgetc(fp)/任意字符转换为二位十六进制fseek(fp,-2,1);printf("line %d:%c%c45n",hanjsq,fgetc(fp),fgetc(fp);fprintf(fp1,"line %d:%c%c45n",hanjsq,fgetc(fp),fgetc(fp);if(ch='%')/如果为'%'则可能为%s%c%dch=fgetc(fp);char bfh4="dcs"for(i=0;i<3;i+)if(bfhi=ch)printf("line %d:%c83n",hanjsq,ch);fprintf(fp1,"line %d:%c83n",hanjsq,ch);if(!isdigit(ch) && !isalpha(ch) && ch!='_' && ch!='"' && ch!='/')char ch214="#()'*:%"/定义部分单符号集char ch39="+?=|&!<>"/定义部分单符号或双符号(前半部分)集char ch49="+=|&="/定义部分双符号(后半部分)for(i=0;i<13;i+)/判断单个符号if(ch=ch2i)printf("line %d:%c%dn",hanjsq,ch,i+48);fprintf(fp1,"line %d:%c%dn",hanjsq,ch,i+48);for(j=0;j<8;j+)/判断双符号if(ch=ch3j)/如果ch与ch3中第j个字符匹配ch=fgetc(fp);/预读一位/if(ch=10)hanjsq+;if(ch=ch4j)/且ch与ch4第j个匹配,则表示ch3j与ch4j连起来为一个双符号printf("line %d:%c%c%dn",hanjsq,ch3j,ch4j,i+69);fprintf(fp1,"line %d:%c%c%dn",hanjsq,ch3j,ch4j,i+69);if(ch='<' && ch3j='<')/判断'<<'符printf("line %d:<<77n",hanjsq);fprintf(fp1,"line %d:<<77n",hanjsq);if(ch='>' && ch3j='>')/判断'>>'符printf("line %d:>>78n",hanjsq);fprintf(fp1,"line %d:>>78n",hanjsq);else/否则表示ch3j为单符号,不是双符号的一部分printf("line %d:%c%dn",hanjsq,ch3j,j+61);fprintf(fp1,"line %d:%c%dn",hanjsq,ch3j,j+61);fseek(fp,-1,1);