实验2词法分析程序的设计一、实验目的掌握计算机语言的词法分析程序的开发方法。二、实验内容编制一个能够分析三种整数、标识符、主要运算符和主要关键字的词法分析程序。三、实验要求1、根据以下的正规式,编制正规文法,画出状态图;标识符字母(字母|数字字符)*十进制整数0|((1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*)八进制整数0(1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)*十六进制整数0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)*运算符和界符+-*/=();关键字ifthenelsewhiledo2、根据状态图,设计词法分析函数intscan(),完成以下功能:1)从文本文件中读入测试源代码,根据状态转换图,分析出一个单词,2)以二元式形式输出单词单词种类,单词属性其中单词种类用整数表示:0:标识符1:十进制整数2:八进制整数3:十六进制整数运算符和界符,关键字采用一字一符,不编码其中单词属性表示如下:标识符,整数由于采用一类一符,属性用单词表示运算符和界符,关键字采用一字一符,属性为空3、编写测试程序,反复调用函数scan(),输出单词种别和属性。四、实验环境PC微机DOS操作系统或Windows操作系统TurboC程序集成环境或VisualC++程序集成环境五、实验步骤1、根据正规式,画出状态转换图;2、根据状态图,设计词法分析算法;观察状态图,其中状态2、4、7、10(右上角打了星号)需要回调一个字符。声明一些变量和函数:ch:字符变量,存放最新读进的源程序字符。strToken:字符串变量,存放构成单词符号的字符串。GetChar():子函数,将下一输入字符读到ch中,搜索指示器前移一字符位置。GetBC():子函数,检查ch中的字符是否为空白。若是,则调用GetChar()直至ch中进入一个非空白字符。Concat():子函数,将ch中的字符连接到strToken之后。IsLetter():布尔函数,判断ch中的字符是否为字母。IsDigit():布尔函数,判断ch中的字符是否为数字。Reserve():整型函数,对strToken中的字符串查找保留字表,若它是一个保留字则返回它的编码,否则返回0。SearchOp():整型函数,对ch查找运算符和界符,若它是一个运算符或界符,则返回它的编码,否则返回0。Retract():子函数,将搜索指示器回调一个字符位置,将ch置为空白字符。ProError():错误处理函数。关键字保存在字符数组中,定义编码为相对数组首地址的位置+1。保留子表顺序如下:{if,then,else,while,do},则相应编码为:1,2,3,4,5。运算符和界符保存在字符数组中,编码定义与关键字相同,顺序如下:{+,-,*,/,,,=,(,),;},编码为:1~10。01字母空白字母或数字非字母与数字31~940~92非数字5061~70~7非0~778x90~9或a~f非0~9与a~f1011+或—或*或/或或或=或(或)或;12ifthenelsewhiledo****非1~7与x0~9或a~f二元表单词单词种类属性标识符0单词自身十进制整数1单词自身八进制整数2单词自身十六进制整数3单词自身运算符和界符单词自身-关键字单词自身-算法如下:ch=’‘;strToken=””;GetBC();if(IsLetter()){while(IsLetter()||IsDigit()){Concat();GetChar();}Retract();If(Reserve())printf(%s,-,strToken);elseprintf(,0,%s,strToken);}elseif(‘1’=ch&&ch=’9’){while(IsDigit()){Concat();GetChar();}Retract();printf(,1,%s,strToken);}elseif(ch==’0’){GetChar();if(ch=‘1’&&ch=‘7’){while(ch=‘0’&&ch=‘7’){Concat();GetChar();}Retract();printf(,2,%s,strToken);}elseif(ch==’x’){GetChar();while(IsDigit()||ch=‘a’&&ch=’f’){Concat();GetChar();}Retract();printf(,3,%s,strToken);}else{Retract();printf(“1,0“);}}elseif(SearchOp())printf(%c,-,ch);elseProError();3、采用C或C++语言,设计函数scan(),实现该算法;charGetChar(FILE*fp){//读取文件中的一个字符charch;ch=fgetc(fp);returnch;}charGetBC(FILE*fp){//读取文件的字符直至ch不是空白charch;do{ch=GetChar(fp);}while(ch==''||ch=='\t'||ch=='\n');returnch;}voidConcat(charch,charstrToken[]){//将ch中的字符连接到strToken之后charstr[2];str[0]=ch;str[1]='\0';strcat(strToken,str);}intIsLetter(charch){//布尔函数,判断ch中的字符是否为字母,是返回1,否则返回0intflag=0;if(ch='a'&&ch='z')flag=1;returnflag;}intIsDigit(charch){//布尔函数,判断ch中的字符是否为数字,是返回1,否则返回0intflag=0;if(ch='0'&&ch='9')flag=1;returnflag;}intReserve(charstrToken[]){//整型函数,对strToken中的字符串查找保留字表,若它是一个保留字则返回它的编码,否则返回0intcode=0,i;charkeyWord[6][6]={if,then,else,while,do};for(i=0;i5;i++){if(strcmp(strToken,keyWord[i])==0){code=i+1;break;}}returncode;}intSearchOP(charch){//整型函数,对strToken中的字符串查找运算符和界符,若它是一个运算符或界符,则返回它的编码,否则返回0intcode=0,i;charOP[11]={'+','-','*','/','','','=','(',')',';'};for(i=0;i10;i++){if(ch==OP[i]){code=i+1;break;}}returncode;}charRetract(FILE*fp,charch){//子函数,将搜索指示器回调一个字符位置,将ch置为空白字符ch='';fseek(fp,-1L,1);returnch;}voidProError(){//错误处理函数printf(输入错误!\n);return;}FILE*scan(FILE*fp){//输出单个二元式charch;charstrToken[10];strToken[0]='\0';//置strToken为空串ch=GetBC(fp);//先读取一个非空白的字符if(feof(fp))returnfp;//判断文件尾,是则返回调用程序if(IsLetter(ch)){//判断标识符while(IsLetter(ch)||IsDigit(ch)){Concat(ch,strToken);ch=GetChar(fp);}ch=Retract(fp,ch);if(Reserve(strToken)){//判断关键字printf(%s,-\n,strToken);}elseprintf(0,%s\n,strToken);}elseif(ch='1'&&ch='9'){//判断十进制整数while(IsDigit(ch)){Concat(ch,strToken);ch=GetChar(fp);}ch=Retract(fp,ch);printf(1,%s\n,strToken);}elseif(ch=='0'){ch=GetChar(fp);if(ch='1'&&ch='7'){//判断八进制整数while(ch='0'&&ch='7'){Concat(ch,strToken);ch=GetChar(fp);}ch=Retract(fp,ch);printf(2,%s\n,strToken);}elseif(ch=='x'){//判断十六进制整数ch=GetChar(fp);while(IsDigit(ch)||ch='a'&&ch='f'){Concat(ch,strToken);ch=GetChar(fp);}ch=Retract(fp,ch);printf(3,%s\n,strToken);}else{//判断十进制的0ch=Retract(fp,ch);printf(1,0\n);}}elseif(SearchOP(ch)){//判断运算符和界符printf(%c,-\n,ch);}else{//出错ProError();}returnfp;}4、编制测试程序(主函数main);#includeiostreamusingnamespacestd;#defineNULL0intmain(){FILE*fp;if((fp=fopen(C:\\Users\\Administrator\\Desktop\\Code.txt,r))==NULL){//以只读方式打开文件,失败则退出程序printf(filecannotopen!);exit(0);}printf(词法分析结果如下:\n);while(!feof(fp)){//若不是文件尾则执行循环fp=scan(fp);//输出单词种类、属性的二元式}fclose(fp);//关闭文件fp=NULL;//避免指向非法内存}5、调试程序:读入文本文件,检查输出结果。六、测试数据输入数据:编辑一个文本文件program.txt,在文件中输入如下内容:正确结果:ifdata+920x3fthendata=data+01;elsedata=data-01;if,-0,data+,-1,92,-3,3fthen,-0,data=,0,data+,-2,1;,-else,-0,data=,-0,data-,-2,-;,-七、实验报告要求实验报告应包括以下几个部分:1、词法的正规式描述;2、变换后的状态图;3、词法分析程序的数据结构与算法。八、思考题1、词法分析能否采用空格来区分单词?答:不能,因为程序的语法里有包括:’;’,‘{’,‘}‘,‘(‘,‘)‘等界符或连接符号存在,这些符号符与单词的连接无空格,用空格区分单词将无法保证程序语法的正确。2、程序设计中哪些环节影响词法分析的效率?如何提高效率?答:本程序在判断关键字时,是在完成对标志符的识别后,判断该标识符是否是保留字,若是则判断为关键字,这种情况下,导致每次识别完一个标识符,都要查询保留字表,会影响效率,可在识别标识符的程序段中添加对关键字的识别,如首字母的特别判断或遇到数字跳过关键字的判断等。另外,程序的实现是通过在主函数中循环调用scan()函数来输出二元式,一次调用就输出一个二元式,可以考虑使用堆栈,先将读来的数据压栈,再进行识别,这样比重复调用函数效率更高