一、目的
深入了解编译技术中的词法分析,能够用c语言编写一个简单的c语言词法分析器。
二、题目
使用自己熟悉的语言,实现简化版的C语言代码的单词识别。
创新功能:显示标识符和常数数组中已添加的内容。
三、要求
在设计的状态转换图中,首先对输入串做预处理,即剔除多余的空白符(在实际的词法分析中,预处理还包括剔除注释和制表换行符等编辑性字符的工作),使词法分析工作既简单又清晰。其次,将保留字作为一类特殊的标识符来处理,也即对保留字不专设对应的状态转换图,当转换图识别出一个标识符时就去查对表的前五项,确定它是否为一个保留字。当然,也可以专设一个保留字表来进行处理。
四、实验环境
Windows系统、visual C++ 6.0
五、系统实现
1.思路分析:
1)主函数中写入一个c语言单词,判断其范围,通过函数指针确定需要做哪一种识别。
2)按照要求中不同种类将识别分为五种:保留字、标识符、常数、符号、其他。
3)主函数中对输入的单词识别其首位,从而进入不同种类识别。
2.实现过程
1)保留字、标识符、常数、符号、其他五类的定义我将其作为全局变量定义在程序开头。包括函数指针的定义。
2)五个识别函数我都放在主函数之前,可以偷懒省去,声明的步骤。
3)保留字识别,判断单词与保留字数组中指向的内容是否相同,相同即输出,若都不相同,转标识符识别。
4)标识符识别和常数识别类似,一开始数组内为空,先判断单词是否存在,不存在即录入,若已录满,报错。
5)符号识别,与保留字识别类似。
6)主函数,通过j=1;while(j)进行循环,直到选择退出(j=0),通过面板选择,实现不同功能。
2.状态转换图
六、程序运行结果
1.基本单词识别 保留字
标识符
常数
符号
其他
输入相同的常数或标识符
显示数组中已添加的内容
/* 针对题目及完成的任务要求来设计测试用例 */
七、总结
拿到题目时感觉很容易,以为只要针对输入的单词逐位识别,将其分类即可。而实现过程中却遇到不少问题:
1. 一开始想要输入一条语句,识别出每个单词的种类和种类编码,但怎样将语句分解为单词并保存让我很头疼,查阅资料也没能找到解决办法;
2.数组指针保存数据和重复时报错两者不兼容也没能解决。思考了很久,主函数中入口单词只有一个,所以其地址也唯一,若想保存到数组指针中,必须另外定义变量,赋值后才能保存,而检测重复性时,因为输入单词必与某一变量相同,所以会一直报错,我也没能想到解决方法,只能在调试时,手动更改数组属性,从而完成实验。
3.总结来说,问题主要出现在对字符串的输入、分解、保存不够熟练,对数组知识理解不够深入上,还有就是数据结构学的不好,建立线性表老是不成功,其实数组保存的问题用线性表都能够解决的。
源代码:
#include<stdio.h>//保留字while if else switch case
#include<stdlib.h>
#include<string.h>
#define in 1
#define out 0
char *baoliuzi[5]={"while","if","else","switch","case"};
char *biaoshifu[10]; //标识符指针数组,初始状态为空
char *changshu[50]; //常数指针数组,初始状态为空
char *fuhao[8]={"+","-","*","<=","<","==","=",";"};
char *qita[]={"error"};
void (*p)(char a[]); //函数指针,对输入单词判断类型,从而进行不同类型识别
int m=0,n=0;
void shibie1(char a[]) //保留字
{ void shibie2(char); //申明标识符函数
int i;
for(i=0;i<=4;i++)
{
if(strcmp(a,baoliuzi[i])==0)
{
printf("%s:\t< %s,_ >\n\n",baoliuzi[i],baoliuzi[i]);
break;
}
}
if(i==5)
{
shibie2(a); //若不为保留字,转识别2
}
}
void shibie2(char a[]) //标识符
{
int i;
for(i=0;i<=9;i++) //避免重复
{
if(biaoshifu[i]==a)
{
printf("标识符数组中已存在该标识符。内码值为:%d\n\n",&biaoshifu[i]);
goto lo;
}
}
for(i=0;i<=9;i++) //数组未满时,录入数据
{
if(biaoshifu[i]==0)
{
biaoshifu[i]=a;
printf("为标识符。");
printf("录入的标识符内码值为:%d,标识符为:%s\n\n",&biaoshifu[i],a); //地址作为内码值输出
break;
}
}
lo: if(biaoshifu[9]!=0) //数组已满,禁止输入
{
printf("标识符数组已满,禁止输入!\n\n ");
}
}
void shibie3(char a[]) //常数
{
int i,j;
for(i=0;i<=49;i++) //避免重复
{
if(changshu[i]==a)
{
printf("常数数组中已存在该标识符。内码值为:%d\n\n",&changshu[i]);
goto lb;
}
}
for(i=0;i<=49;i++) //常数数组未满,输入数据
{
if(changshu[i]==0)
{
changshu[i]=a;
printf("为常数,已录入。\n%s:\t< %s,%d >\n\n",a,a,&changshu[i]);
break;
}
}
lb: if(changshu[9]!=0) //数组已满,禁止输入
{
printf("常数数组已满,禁止输入!\n\n ");
}
}
oid shibie4(char a[]) //符号
{
int i;
for(i=0;i<=7;i++)
{
if(strcmp(a,fuhao[i])==0)
{
if(i<=2)
{
printf("%s:\t< %s,_ >\n\n",a,a);
break;
}
if(i==3)
{
printf("%s:\t< relop,LE >\n\n",a);
break;
}
if(i==4)
{
printf("%s:\t< relop,LT >\n\n",a);
break;
}
if(i==5)
{
printf("%s:\t< relop,EQ >\n\n",a);
break;
}
if(i>=6)
{
printf("%s:\t< %s,_ >\n\n",a,a);
break;
}
}
}
}
void shibie5(char a[]) //其他
{
printf("非法字符,错!\n\n");
}
void main()
{
int i,j,t;
char danci[10];
while(j)
{
printf("请输入您想进行的操作(输入其他字符默认为1,进行查询):\n1.查询(输入标识符和常数时自动录入)\n2.显示标识符数组内容(初始为空)\n3.显示常数数组内容(初始为空)\n0.退出\n");
scanf("%d",&j);
if(j==1)
{
printf("请输入一个单词:\n");
scanf("%s",&danci);
if(danci[0]>='a'&& danci[0]<='z') //首位为字母,转标识符(保留字)识别
{
p=shibie1;
}
else if( danci[0]>='A'&&danci[0]<='Z') //大写字母,转标识符识别
{
p=shibie1;
}
else if(danci[0]>='0'&&danci[0]<='9') //首位为数字,转常数识别
{
p=shibie3;
}
else if(strcmp(danci,fuhao[0])==0)
{
p=shibie4;
}
else if(strcmp(danci,fuhao[1])==0)
{
p=shibie4;
}
else if(strcmp(danci,fuhao[2])==0)
{
p=shibie4;
}
else if(strcmp(danci,fuhao[3])==0)
{
p=shibie4;
}
else if(strcmp(danci,fuhao[4])==0)
{
p=shibie4;
}
else if(strcmp(danci,fuhao[5])==0)
{
p=shibie4;
}
else if(strcmp(danci,fuhao[6])==0)
{
p=shibie4;
}
else if(strcmp(danci,fuhao[7])==0) //笨比方法,但为什么写成if(danci=='+')一直不运行?
{
p=shibie4;
}
else
p=shibie5;
p(danci);
}
if(j==2)
{
for(i=0;i<=9;i++)
{
printf("%s",biaoshifu[i]);
}
}
if(j==3)
{
for(i=0;i<=49;i++)
{
printf("%s",changshu[i]);
}
}
}
}