1.目的要求
设计、编制并调试一个词法分析程序,分析一段程序的词法,包括:过滤无效字符、数值转换、宏展开、预包含处理等,加深对词法分析原理的理解。
2.单词分类表
单词分类表如表1所示
类别 | 类别码 | 类别 | 类别码 |
int | 1 | for | 11 |
float | 2 | do | 12 |
double | 3 | continue | 13 |
char | 4 | break | 14 |
long | 5 | switch | 15 |
short | 6 | case | 16 |
void | 7 | default | 17 |
if | 8 | return | 18 |
else | 9 | static | 19 |
while | 10 |
|
|
标识符 | 41 | 数字 | 42 |
空格,换行,tab | -1 | + | 61 |
- | 62 | ? | 74 |
* | 63 | “ | 75 |
/ | 64 | ‘ | 76 |
> | 65 | { | 77 |
< | 66 | } | 78 |
= | 67 | ( | 79 |
| | 68 | ) | 80 |
^ | 69 | # | 81 |
. | 70 | ! | 82 |
: | 71 | % | 83 |
; | 72 | & | 84 |
, | 73 | \ | 85 |
[ | 86 | ] | 87 |
++ | 91 | -- | 92 |
** | 93 | == | 97 |
|| | 98 | // | 94 |
预编译 | 120 | 宏定义 | 121 |
表 1
3.单词结构描述(正规式或正规文法)
I=A|a|B|b|C|c|D|d|E|e|F|f|G|g|H|h|I|i|J|j|K|k|L|l|M|m|N|n|O|o|P|p|Q|q|R|r|S|s|T|t|
U|u|V|v|W|w|X|x|Y|y|Z|z
D=1|2|3|4|5|6|7|8|9|0
标识符:I(I|D)*
数字:D(D*)
4.算法描述
(1)将标识符,数字,预编译,宏定义一种一类,关键字一词一类,符号一词一类,标号
(2)书写函数以下函数:IsLetter(检查字母),IsDigit(检查数字),IsSpace(检查空格),InImp(检查关键字),InMark(检查是否在标识符表中,如果不在那么在标识符标中加入这个字符串),IsNumber(判断数字是否在已经在数字表中,如果不在那么加入到数字表中),IsSign(各种符号)
(3)读取文件,将文件的字符调入到一个数组text中,
(4)读取数组text中的第一个字符,判断是字母,数字还是特殊字符,若是字母则将字母写入到临时数组strToken中去,读入下一个字符,判断是数字还是字母如果是其中一个那么继续写入到数组strToken中,直到读入非字母非数字的字符,判断strToken是关键字,如果是,那么输出关键的编号,如果不是那么去标识符表中查找,若表中有,则输出标识符的标号,以及输出标识符在表中的位置,若没有,将标识符添加到表中,输出标号和位置。
(5)若第一个字符是数字,将数字读入数组strToken中,再读入下一个字符判断是数字还是“.” ,如果是其中一个那么继续写入到数组,继续读入,如果都不是,那么去判断strToken中“.”的个数,如果个数不合法那么输出提示错误,如果合法,那么判断数字表中是否存在,如果不存在那么将字符串转为数字,存入表中,最后输出数字的标号,以及在数字表中的位置。
(6)读取字符是特殊符号那么输出特殊符号的标号,若特殊符号是#,那么判断下一个字符是不是“d”或者“i”若是“d”那么读入到数组strToken中,直到遇到回车,输出是一个预编译,如果是“d”,那么读入到数组strToken中,输出是宏定义,并且输出其定义内容。
6.程序结构
程序结构如图2所示,图中包括了整个程序的算法结构,包括每个分支,每个模块需要实现的功能内容。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191221224441199.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDgzMTUzNg==,size_16,color_FFFFFF,t_70)图 1
7.设计技巧及体会
在设计中,刚开感觉会有点麻烦,无从下手,在找了一些资料以及看了书之后,才渐渐知道自己要做的是一个怎样的东西,才有了自己的想法。从分类到编号,再到输出标号,按照标号去找属于哪一个类别,分别构建每一个函数,以及函数之间的关系,和最后那个进行分析的函数要调那些那些函数,怎么调用,都花了一些时间来构思,在调试阶段,由于是写完一个部分就进行测试,所以这个阶段比较轻松一点,只是注意指向数组的变量的变化。这次实验,我收获了很多,掌握词法分析器的一般原理,对程序书写规范有了更深的了解和认识,希望以后能够再接再厉。
8.代码
#include<stdio.h>
#include<conio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
char mark[50][20]; //标识符表
double number[50];//数字表
int mar=0;//标识符数组下标
int num=0;//数字表数组下标
//关键字
char imp[50][20]={"int","float","double","char","long","short","void","if","else","while","for","do","continue","break","switch","case","default","return","static"};
//预编译
//检查字母
bool IsLetter (char ch) {
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')){
return true;
}
else return false;
}
//检查数字
bool IsDigit(char ch) {
if(ch>='0'&&ch<='9'){
return true;
}
else{
return false;
}
}
//检查空格
bool IsSpace(char ch){
if(ch==' '||ch=='\n'||ch=='\t'){
return true;
}
else return false;
}
//检查关键字
int InImp(char strToken[20]){
int n;
for (n = 0; n < 50; n++)
{
if (strcmp(strToken, imp[n]) == 0)
{
return n+1;
}
}
if(n==50){
return 0;
}
}
//检查是否在标识符表中,如果不在那么在标识符标中加入这个字符串
int InMark(char strToken[20]){
if (mar!= 0)
{
int q = 0;
while (q<mar)
{
if (strcmp(strToken, mark[q++]) == 0)
{
return q;
}
}
}
//将该标识符保存到标识符表中
strcpy(mark[mar], strToken);
mar++;
return mar;
}
//为宏定义而备
//判断数字是否在已经在数字表中,如果不在那么加入到数字表中
int IsNumber(char strToken[20]){
int count=0;
for(int s=0;s<19;s++){
if(strToken[s]=='.'){
count++;
}
}
//printf("%d-----------------",count);
if(count==0){
int nu = atoi(strToken);
//判断该常数是否存在于常数表中
if (num != 0)
{
for(int y=0;y<num;y++)
{
if(number[y]==nu)
{
return y+1;
}
}
}
//将该常数保存到标识符表中
number[num]=nu;
num++;
return num;
}else if(count==1){
double nu = atof(strToken);
//判断该常数是否存在于常数表中
if (num != 0)
{
for(int y=0;y<num;y++)
{
if(number[y]==nu)
{
return y+1;
}
}
}
//将该常数保存到标识符表中
number[num]=nu;
num++;
return num;
}else{
return 0;
}
}
//各种符号
int IsSign(char ch){
switch(ch){
case ' ':
case '\n':
case '\t':
return -1;
case '+': return 61;
case '-': return 62;
case '*': return 63;
case '/': return 64;
case '>': return 65;
case '<': return 66;
case '=': return 67;
case '|': return 68;
case '^': return 69;
case '.': return 70;
case ':': return 71;
case ';': return 72;
case ',': return 73;
case '?': return 74;
case '"': return 75;
case '\'': return 76;
case '{': return 77;
case '}': return 78;
case '(': return 79;
case ')': return 80;
case '#': return 81;
case '!': return 82;
case '%': return 83;
case '&': return 84;
case '\\': return 85;
case '[': return 86;
case ']': return 87;
}
}
void wordAnaly(char text[1000],int length){
char ch; //最新的字符
char strToken[20];//找到的字符串
int i=0;
while(i<length){
for(int k=0;k<20;k++){
strToken[k]='\0';
}
ch = text[i];
//过滤空格
while(IsSpace(ch)){
i++;
ch = text[i];
}
//关键字和标识符
if(IsLetter(ch)){
int n = 0;
strToken[n++] = text[i++];
while (IsLetter(text[i])||IsDigit(text[i]))
{
strToken[n++] = text[i++];
}
i--;
int flag = InImp(strToken);
if(flag==0){
flag=InMark(strToken);
printf("%s----(%d,%d) 标识符\n",strToken,41,flag);
}else{
printf("%s----(%d,-) 关键字\n",strToken,flag);
}
}
//数字
else if(IsDigit(ch)){
int n = 0;
strToken[n++] = text[i++];
while (IsDigit(text[i])||text[i]=='.')
{
strToken[n++] = text[i++];
}
i--;
int flag=IsNumber(strToken);
if(flag==0){
printf("数字输入有误!\n");
}else {
printf("%s----(%d,%d) 数字\n",strToken,42,flag);
}
}
//预编译
else if(ch=='#'){
char word[50];
for(int w=0;w<50;w++){
word[w]='\0';
}
int a = 0;
word[a]='#';
a++;
if(text[i+1]=='i'){
i++;
while(text[i]!='\n'){
word[a++]=text[i++];
}
printf("%s----(%d,-) 预包含文件处理\n",word,120);
}else if(text[i+1]=='d'){
i++;
while(text[i]!='\n'){
word[a++]=text[i++];
}
//得到宏定义内容
char cha[20];
char chr[20];
for(int w=0;w<50;w++){
cha[w]='\0';
chr[w]='\0';
}
int fl=0;
int index=0;
for(int f=7;f<=a;f++){
if(IsLetter(word[f])){
cha[index++]=word[f];
}
}
index=0;
for(int f=fl+1;f<=a;f++){
if(IsDigit(word[f])||word[f]=='.'){
chr[index++]=word[f];
}
}
InMark(cha);
IsNumber(chr);
printf("%s----(%d,-) 宏定义 ",word,121);
printf("将%s赋值为%s\n",cha,chr);
}
}
//特殊符号
else if(IsSign(ch)){
int flag = IsSign(ch);
if(IsSign(text[i+1])==flag){
i++;
printf("%c%c----(%d,-) 特殊符号\n",ch,text[i],flag+30);
}else{
printf("%c----(%d,-) 特殊符号\n",ch,flag);
}
}
i++;
}
}
int main(){
int c = 0;
char text[1000];
FILE *fp;
fp=fopen("H:\\test.txt","r");
if (fp == NULL)
{
printf("can't open file!\n");
exit(0);
}
while (!feof(fp))
{
text[c++] = fgetc(fp);
}
// for(int j=0;j<=c;j++){
// printf("%d,%c",j,text[j]);
// printf("\n");
// }
wordAnaly(text,c-1);
//将数字表number和标识符表mark写入文件a.txt
FILE *fp1;
int j;
fp=fopen("H:\\a.txt","w");//打开文件以便写入数据
for (j = 0; j < num; j++) { //将a数组中的整数写入fp指向的c:\a.txt文件
fprintf(fp,"%f\n",number[j]);
}
for (j = 0; j < mar; j++) { //将a数组中的整数写入fp指向的c:\a.txt文件
fprintf(fp,"%s\n",mark[j]);
}
fclose(fp); //写入完毕,关闭文件
}