史天永221300220词法分析实验.docx
《史天永221300220词法分析实验.docx》由会员分享,可在线阅读,更多相关《史天永221300220词法分析实验.docx(24页珍藏版)》请在冰豆网上搜索。
史天永221300220词法分析实验
《编译系统设计实践》
实验项目一:
词法分析实验
学号:
221300220
姓名:
史天永
年级:
2014级
学院:
软件学院
专业:
软件工程
实验时间:
2016-2017学年第二学期
任课教师:
陈晖
1、程序源代码
#include
#include//isdigit/isalpha/isspace
#include//exit
#include//strcmp
#include
#definelength120
#definelength26
#definelength317
#definelength46
boolcompare(char*str1,char*str2);
voidmain()
{
FILE*fp1=NULL;
FILE*fp2=NULL;
fp1=fopen("input.txt","r");//打开文件
fp2=fopen("output.txt","w");
if(fp1==NULL)
{
printf("找不到文件:
input.txt");
system("pause");
exit
(1);//process.h中的函数
}
//单词类型的编号:
1保留字2标识符3常数4运算符5分隔符
char*reservedWords[length1]={"if","else","goto","for","switch","case","while","do","break","continue","return","int","long","short","const","struct","void","float","double","char"};//保留字表
char*identifiers[length2]={"include","define","main","printf","scanf"};//标识符表[还包括自定义的变量]
char*operators[length3]={"+","-","*","%",">","<","=","!
","+=","-=","*=","/=","%=",">=","<=","==","!
="};//运算符表
char*splitors[length4]={",",";","{","}","(",")"};//分隔符表
charch;//用于存放读取到的字符
charstr[30];//用于存放读取到的单词
for(inti=0;i<30;i++)//清空str[30]
str[i]='\0';
ch=getc(fp1);//getc函数读取文件中的字符
while(ch!
=EOF)
{
//开始判断是否为空白字符,如果是空白字符,则掠过,什么都不做。
isspace用于判断是否为空白字符
if(isspace(ch));
//开始判断是否为注解,如果是注解,则掠过,什么都不做。
elseif(ch=='/')
{
ch=getc(fp1);//再往前读一位才能继续判断
if(ch=='/')//说明该行中发现了一个注解”//“,那么该行之后的字符串就可以省略了
{
char*temp=(char*)malloc(1024);
fgets(temp,1023,fp1);//把该行读取掉,每读一次,读取指针跳到下一行的开头,如果是最后一行,则读取指针指向EOF
free(temp);
}
elseif(ch=='=')//说明读到了运算符/=
{
printf("[4,/=]\n");
}
elseif(ch=='*')//说明读到了/*型的注释开始利用栈来寻找/**/对,情形可能有/*/**//*///**dds*/
{
char*temp=(char*)malloc(1024);//创建一个数组当作栈
intindex=0;
temp[0]='/';//将第一个读到的/*放入栈中
temp[1]='*';
index=2;
while((ch=getc(fp1))!
=EOF)
{
if(ch=='*')//每读到一个*,则判断它上一个字符是否是/,如果是,则读到了一个/*,否则判断它下一个字符是否是/,如果是,则读到了一个*/
{
fseek(fp1,-2L,SEEK_CUR);
ch=getc(fp1);
if(ch=='/')//本次字符*的上一个字符是/,说明读到了一个/*
{
temp[index]='/';index++;//将/*入栈
temp[index]='*';index++;
fseek(fp1,1L,SEEK_CUR);//将读取指针还原回来
}
else
{
fseek(fp1,1L,SEEK_CUR);//将读取指针还原回来
ch=getc(fp1);
if(ch=='/')//本次字符*的下一个字符是/,说明读到了一个*/
{
//这里不用判断栈是否为空,能运行到这里,栈里面必定至少有一个/*
//对于一开始就读到*/这种情况,会被翻译为一个乘号,一个除号,至于是否语法错误,这要在语法分析里再考虑
index-=2;//将栈中栈顶的/*出栈
if(index==0)//每当有/*出栈都要检查栈是否变空,如果为空,说明此次注解扫描告一段落,可以退出while循环了。
break;
}
}
}
}
free(temp);
}
else//如果字符/后面的字符不是/或=或*,说明/是除号
{
printf("[4,/]\n");
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
}
}
//开始判断是否为保留字或标识符
elseif(isalpha(ch))//以字母开头,要么是保留字,要么是标识符。
C语言变量组成:
数字、字母、下划线。
做为变量名的开头不能为数字
{
intindex=0;//str字符数组的下标
str[index]=ch;
index++;
ch=getc(fp1);
while(isalpha(ch)||isdigit(ch)||ch=='_')//如果是字母,下划线,则一直往下读,直到读到的字符不是数字、字母或下划线
{
str[index]=ch;
index++;
ch=getc(fp1);
if(index>29)//如果读的变量长度大于30,报错
{
printf("Error!
\n");
exit(0);
}
}
//如果能从while中跳出来,说明单词读完了
str[index]='\0';
if(ch!
=EOF)//如果此时ch为EOF,则当前指针仍然指向EOF,即EOF是终极文件尾,读取指针的最后终点,如果此时再用fseek回退的话,就会出现无限重复的情况
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
//开始判断以字母开头字符串是否是保留字
intinfo=0;
for(inti=0;i{
if(compare(reservedWords[i],str))//strcmp比较的两字符数组的长度要一致,不然会出现内存溢出的错误
{
//fputs("[1,str",fp2);
fprintf(fp2,"[1,%s]\n",str);//判断出是保留字,保存到文件
for(inti=0;i<30;i++)//清空str[30]
str[i]='\0';
info=1;
break;
}
}
if(info==0)//如果不是保留字,那么它肯定就是标识符了,要么是预定义标识符,要么是自定义标识符,反正都是标识符
{
fprintf(fp2,"[2,%s]\n",str);//判断出是预定义标识符
for(inti=0;i<30;i++)str[i]=0;//清空str[30]
}
}
//开始判断是否为常量,根据龙书84页的无符号数字的状态转换图,无符号数字有以下6种情况
//digit
//digitE±digit
//digitEdigit
//digit.digit
//digit.digitE±digit
//digit.digitEdigit
elseif(isdigit(ch))//以数字开头,肯定是常量
{
intindex=0;//str字符数组的下标
str[index]=ch;
index++;
ch=getc(fp1);
while(isdigit(ch))//如果是数字,则一直往下读,直到遇到一个非数字的字符
{
str[index]=ch;
index++;
ch=getc(fp1);
}
if(ch=='.')//说明是第4、5、6种情况
{
str[index]='.';
index++;
ch=getc(fp1);
if(isdigit(ch))//判断字符.后面是不是数字
{
while(isdigit(ch))//如果是数字,则一直往下读,直到遇到一个非数字的字符
{
str[index]=ch;
index++;
ch=getc(fp1);
}
if(ch=='E')//说明是第5、6种情况
{
str[index]=ch;
index++;
ch=getc(fp1);
if(ch=='+'||ch=='-')//说明是第5种情况
{
str[index]=ch;
index++;
ch=getc(fp1);
while(isdigit(ch))//如果是数字,则一直往下读,直到遇到一个非数字的字符
{
str[index]=ch;
index++;
ch=getc(fp1);
}
//能从while中跳出来,说明第5种情况的字符串已经读完
str[index]='\0';
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
fprintf(fp2,"[3,%s]\n",str);
}
elseif(isdigit(ch))//说明是第6种情况
{
str[index]=ch;
index++;
ch=getc(fp1);
while(isdigit(ch))//如果是数字,则一直往下读,直到遇到一个非数字的字符
{
str[index]=ch;
index++;
ch=getc(fp1);
}
//能从while中跳出来,说明第5种情况的字符串已经读完
str[index]='\0';
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
fprintf(fp2,"[3,%s]\n",str);
}
else//即E后面既不是±,也不是数字,则报错
{
str[index]='\0';
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
fprintf(fp2,"字符串%s有错误!
\n",str);
}
}
else//说明是第4种情况
{
str[index]='\0';
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
fprintf(fp2,"[3,%s]\n",str);
}
}
else//字符.后面不是数字,所以错误
{
str[index]='\0';
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
fprintf(fp2,"字符串%s有错误\n",str);
}
}
elseif(ch=='E')//说明是第2、3种情况
{
str[index]=ch;
index++;
ch=getc(fp1);
if(ch=='+'||ch=='-')//说明是第2种情况
{
str[index]=ch;
index++;
ch=getc(fp1);
while(isdigit(ch))//如果是数字,则一直往下读,直到遇到一个非数字的字符
{
str[index]=ch;
index++;
ch=getc(fp1);
}
//能从while中跳出来,说明第5种情况的字符串已经读完
str[index]='\0';
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
fprintf(fp2,"[3,%s]\n",str);
}
elseif(isdigit(ch))//说明是第3种情况
{
str[index]=ch;
index++;
ch=getc(fp1);
while(isdigit(ch))//如果是数字,则一直往下读,直到遇到一个非数字的字符
{
str[index]=ch;
index++;
ch=getc(fp1);
}
//能从while中跳出来,说明第5种情况的字符串已经读完
str[index]='\0';
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
fprintf(fp2,"[3,%s]\n",str);
}
else//即E后面既不是±,也不是数字,则报错
{
str[index]='\0';
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
fprintf(fp2,"字符串%s有错误!
\n",str);
}
}
else//说明是第1种情况
{
str[index]='\0';
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
fprintf(fp2,"[3,%s]\n",str);
}
}
//开始判断是否为运算符[除号/的判断放在注解判断那一块了]
elseif(ch=='+'||ch=='-'||ch=='*'||ch=='%'||ch=='>'||ch=='<'||ch=='='||ch=='!
')
{
str[0]=ch;
ch=getc(fp1);
if(ch=='=')//如果在+-*%><=后面的是=号,则说明运算符已经读取完毕,而且是双字符的运算符
{
str[1]=ch;
fprintf(fp2,"[4,%s]\n",str);
}
else//如果在+-*%><=!
后面的不是=号,则说明运算符已经读取完毕,而且是单字符的运算符
{
if(ch!
=EOF)
fseek(fp1,-1L,SEEK_CUR);//把读取指针从当前位置退回1个字节
fprintf(fp2,"[4,%s]\n",str);
}
}
//开始判断是否为分隔符
elseif(ch==','||ch==';'||ch=='{'||ch=='}'||ch=='('||ch==')')
fprintf(fp2,"[5,%c]\n",ch);
else
{
fprintf(fp2,"当前字符%c错误!
\n",ch);
}
ch=getc(fp1);
}
fclose(fp1);
fclose(fp2);
}
boolcompare(char*str1,char*str2)//垃圾strcmp只能比较长度相同的两个字符串,我只好自己写字符串比较函数
{
intlen1=strlen(str1);//strlen()用来计算指定的字符串s的长度,不包括最后一个非空字符后的空白字符""和结束字符"\0"
intlen2=strlen(str2);//例如:
charstr[30]="hello5555";strlen(str)值为9
if(len1!
=len2)
returnfalse;
else
{
for(inti=0;i{
if(str1[i]!
=str2[i])
returnfalse;
}
returntrue;
}
}
2、已经通过的测试数据以及截图
测试数据1:
main(){
inta;//thisisatest
a=10;/*thisisatest*/
}
测试数据2:
voidmain(){
inta=10;//thisisatest
for(inti=0;ia+=i;
/*annotation*/
doubleb=1.5;
doublec=45.5E+10;
floatd=0.9999;
}
测试数据3:
voidmain(){
inta=10;//thisisatest
for(inti=0;ia+=i;
/*annotation*/
doubleb=1.;
doublec=45.5E;
floatd=0.9999;
}
3、功能描述
本程序使用C语言编写,用于处理C语言源代码的词法分析,程序读取input.txt中的C语言源代码,然后以[token编号,token]的格式输出各个词法单元到output.txt文件中。
4、程序结构描述
本程序里只有main和compare两个函数,main函数是c语言的主函数,里面有一个大的while循环,循环里众多的if和elseif语句用于判断读取到的字符的类型。
compare函数是我自己编写的用于比较两个字符串是否相等的函数,boolcompare(char*str1,char*str2);该函数接收两个char*类型的字符数组地址变量,如果两字符串相同,则返回true,否则返回false
5、符号表的设计和结构
符号表包括:
1保留字2标识符3常数4运算符5分隔符
char*reservedWords[length1]={"if","else","for","while","do","break","continue","int","return"};//保留字表
char*identifiers[length2]={"include","stdio","define","main","printf","scanf"};//标识符表[还包括自定义的变量]
char*operators[length3]={"+","-","*","%",">","<","=","!
","+=","-=","*=","/=","%=",">=","<=","==","!
="};//运算符表
char*splitors[length4]={",",";","{","}","(",")"};//分隔符表
取单词流程图:
是
否
是
否
是
否
是
否
是
否
是
否
6、实验总结
在开始时