C语言词法分析器构造实验报告02计算机(2)2002374203 冯绍欣一、题目要求:完成一个C语言的词法分析器的构造。
此词法分析器能识别附值语句、循环语句、条件语句、并能处理注释。
二、设计方案:这个词法分析器分析的主要关键字有:main, int, float, char, if, else, for, while, do, switch, case, break; default。
选择要分析的c文件,首先对其去掉注释和与空格处理,再根据字符的不同类型分析。
1、全局数据结构:字符数组set[ ]:存放从文件中读到的所有字符;str[ ]:存放经过注释处理和预空格处理的字符;strtoken[ ]:存放当前分析的字符;结构体KEYTABLE:存放关键字及其标号;全局字符变量ch:当前读入字符;全局整型变量sr, to:数组str, strtoken 的指针。
2、以层次图形式描述模块的组成及调用关系3、主要函数的设计要求(功能、参数、返回值):openfile:打开文件;GetChar:将下一个输入字符读到ch中,搜索指示器前移一字符位置;GetBC:检查ch中的字符是否为空白。
若是,则调用GetChar直至ch中进入一个非空白字符;Concat:将ch中的字符连接到strtoken之后;IsLetter 和IsDigit:布尔函数过程,分别判断ch中的字符是否为字母和数字;Reserve:整型函数过程,对strtoken中的字符串查找关键字表,若是关键字则返回编码,否则返回-1;Retract:将搜索指示器回调一个字符位置,将ch置为空白字符;reflesh:刷新,把strtoken数组置为空;prearrange1:将注释部分置为空格;prearrange2:预处理空格,去掉多余空格;analysis:词法分析;main:主函数。
4、状态转换图:字符a包括:= , & , | , + , --字符b包括:-- , < , > , | , *字符c包括:, , : , ( , ) , { , } , [ , ] , ! ,# , % , ” , / , * , + , -- , > , <, .三、源代码如下:#include <stdio.h>#include <string.h>char set[1000],str[500],strtoken[20];char sign[50][10],constant[50][10];char ch;int sr,to,id=0,st=0;typedef struct keytable /*放置关键字*/{char name[20];int kind;}KEYTABLE;KEYTABLE keyword[]={ /*设置关键字*/{"main",0},{"int",1},{"float",2},{"char",3},{"if",4},{"else",5},{"for",6},{"while",7},{"do",8},{"switch",9},{"case",10},{"break",11},{"default",12},};openfile() /*打开文件*/{FILE *fp;char a,filename[10];int n=0;printf("Input the filename:");gets(filename);if((fp=fopen(filename,"r"))==NULL){printf("cannot open file.\n");exit(0);}elsewhile(!feof(fp)) /*文件不结束,则循环*/{a=getc(fp); /*getc函数带回一个字符,赋给a*/set[n]=a; /*文件的每一个字符都放入set[]数组中*/n++;}fclose(fp); /*关闭文件*/set[n-1]='\0';printf("\n\n-------------------Source Code--------------------------\n\n");puts(set);printf("\n--------------------------------------------------------\n");}reflesh() /*清空strtoken数组*/{to=0; /*全局变量to是strtoken的指示器*/strcpy(strtoken," ");}prearrange1() /*预处理程序1*/{int i,a,b,n=0;do{if(set[n]=='/' && set[n+1]=='*'){a=n; /*记录第一个注释符的位置*/while(!(set[n]=='*' && set[n+1]=='/'))n++;b=n+1; /*记录第二个注释符的位置*/for(i=a;i<=b;i++) /**/set[i]=' '; /*把注释的内容换成空格,等待第二步预处理*/ }n++;}while(set[n]!='\0');}prearrange2() /*预处理程序2*/{int j=0;sr=0; /*全局变量sr是str[]的指示器*/do{if(set[j]==' ' || set[j]=='\n'){while(set[j]==' ' || set[j]=='\n') /*扫描到有连续的空格或换行符*/j++;str[sr]=' '; /*用一个空格代替扫描到的连续空格和换行符放入str[]*/sr++;}else{str[sr]=set[j]; /*若当前字符不为空格或换行符就直接放入str[]*/sr++;j++;}}while(set[j]!='\0');str[sr]='\0';}char GetChar() /*把字符读入全局变量ch中,指示器sr前移*/{ch=str[sr];sr++;return(str[sr-1]);}void GetBC() /*开始读入符号,直至第一个不为空格*/{while(ch==' '){ch=GetChar();}}Concat() /*把ch中的字符放入strtoken[]*/{strtoken[to]=ch;to++; /*全局变量to是strtoken的指示器*/strtoken[to]='\0';}int IsLetter() /*判断是否为字母*/{if((ch>=65 && ch<=90)||(ch>=97 && ch<=122))return(1);else return(0);}int IsDigit() /*判断是否为数字*/{if(ch>=48 && ch<=57)return(1);else return(0);}int Reserve() /*对strtoken中的字符串查找保留字表,若是则返回它的编码,否则返回-1*/ {int i,k=0;for(i=0;i<=20;i++){if(strcmp(strtoken,keyword[i].name)==0){ k=1;return(keyword[i].kind);}}if(k!=1)return(-1);}void Retract() /*指示器sr回调一个字符位置,把ch置为空*/{sr--;}int InsertId(){int i,k;for(i=0;i<id;i++){k=strcmp(strtoken,sign[i]);if(k==0)return(i);}strcpy(sign[id],strtoken); /*插入标识符*/id++;return(id-1);}int InsertConst(){int i,k;for(i=0;i<st;i++){k=strcmp(strtoken,constant[i]);if(k==0)return(i);}strcpy(constant[st],strtoken); /*插入常数*/st++;return(st-1);}void analysis(){int value;reflesh(); /*清空strtoken数组*/prearrange1(); /*预处理,使注释内容换成单个空格,放回set[]中*/prearrange2(); /*预处理,使set[]中连续的空格置换成单个空格,并把set[]的内容放到str[]中*/GetChar();GetBC(); /*读取第一个字符*/while(ch!='\0') /*当不等于结束符,继续执行*/{if(IsLetter()){while(IsLetter() || IsDigit()) /*若第一个是字符,继续读取,直到出现空格*/{Concat();GetChar();}Retract(); /*指示器sr回调一个字符位置,把ch置为空*/value=Reserve(); /*对strtoken中的字符串查找保留字表,若是则返回它的编码,否则返回-1*/ if(value==-1) /*如果返回值是-1,那就是变量,把它输出*/{InsertId(); /*插入标识符*/printf("\n%s",strtoken);getch();}else /*否则就是关键字,也输出*/{printf("\n%s",strtoken);getch();}reflesh();}else if(IsDigit()){while(IsDigit()) /*否则,若第一个是数字,继续读取,知道出现空格*/{Concat();GetChar();}Retract();InsertConst(); /*插入常数*/printf("\n%s",strtoken);getch();reflesh();}elseswitch(ch) /*否则,若是下面的符号,就直接把它输出*/{case ',':case ';':case '(':case ')':case '{':case '}':case '[':case ']':case '!':case '#':case '%':case '"':case '/':case '*':Concat();printf("\n'%s'",strtoken);getch();reflesh();break;default:if(ch=='=' || ch=='&' || ch=='|' || ch=='+' || ch=='-') /*如果是这些符号,继续读取下一个*/ {Concat(); /*判断是否为==,&&,||,++,--的情况*/GetChar();if(ch==strtoken[0])Concat();elseRetract();printf("\n'%s'",strtoken);getch();reflesh();break;}else if(ch=='+' || ch=='-' || ch=='<' || ch=='>' || ch=='!' || ch=='*'){Concat(); /*判断是否为+=,-=,<=,>=,!=,*=的情况*/GetChar();if(ch=='=')Concat();elseRetract();printf("\n'%s'",strtoken);getch();reflesh();break;}else{printf("Error!");getch();break;}}GetChar();GetBC();}}main(){clrscr();openfile();analysis();printf(“analysis is over!”);}五、测试结果:1、分析文件test1.c中的程序:Input the filename:test.c*****************Original Code************************/* HELLO.C -- Hello, world */#include "stdio.h"#include "conio.h"main(){printf("Hello, world\n");getch();}*****************************************************'#'include'"'stdio'.'h'"''#'include'"'conio'.'h'"'main'('')''{'printf'(''"'Hello','worldError!n'"'')'';'getch'('')'';''}'Analysis is over!六、实验总结:这个程序主要参考书上关于词法分析器的设计。