实验目的:
编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类(可自主添加类别)。并依次输出各个单词的内部编码及单词符号自身值。
程序及其子程序:
1、文件输入(待分析文本拖入控制台或屏幕输入)
2、划分字符集成7个大类
3、预处理(去除空格、回车换行和非法字符)
4、词法分析
5、打印输出识别的终结符及其类别
//程序无法识别出double,因为它的前缀子串与do重复(如有需要自主修改)
/*
编译原理实验一:词法分析器
要求:编制一个读单词过程,从输入的C语言源程序中,识别出各个具有独立意义的单词,
即基本保留字、标识符、常数、运算符、分隔符、特殊字符、控制命令七大类。
并依次输出各个单词的内部编码及单词符号自身值的二元组。
*//*
一、复习c语言字符串数组相关处理,文件相关处理//用字符数组存放一个字符串
char str[ ]="";
//用字符指针指向一个字符串
char * s="I love China";
//二维数组存放字符串数组
const int N = 5;
char frd[N][10]={"Hello","World","My","Dear","Friends"};
//字符指针的数组存放字符串数组
char *p[]={"This","is","a","char*","array"};
//文件读写函数原型:FILE *fopen(const char *filename, const char *mode);`头文件:#include <stdio.h>`二、词法分析器
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <sys/types.h>
#define MAX_LINE 1024*10//编码,保留字:1、标识符:2、常数:3、运算符:4、分隔符: 5//控制命令表
static char *contCommands[]={"#include","#define","#undef","#asm","#endasm","#ifdef","#ifndef","#else","#endif"};//关键字表
static char *keyWords[]={"main","int","double","struct","if","else","char","return","const","float",
"short","void","while","for","break","then","long","switch","case","do","static","typedef","continue",
"default","sizeof","do","extern","static","auto","register","sizeof"};//运算符表
static char *operators[]={"+","-","*","\/","=","%",">","<","^","&","|","!"};//分隔符表
static char *delimiters[]={",","(",")","{","}","[","]",";","\""};//特殊符号表
static char *Spesymbols[]={".","$","?","~","^","%","\\","#","&",":","`","@"};//科学计数法
// 数字
//const char* number_rule="^([+-]?\\d+\\.\\d+)|([+-]?\\d+)|([+-]?\\.\\d+)$";
//const std::regex pattern_number(number_rule, regex::icase);
//科学计数
//const char* scientific_rule="^[+-]?((\\d+\\.?\\d*)|(\\.\\d+))[Ee][+-]?\\d+$";
//const regex pattern_scientific(scientific_rule, regex::icase);
//十六进制
//const char* hex_rule="^[+-]?0[xX]([A-Fa-f0-9])+$";
//const regex pattern_hex(hex_rule, regex::icase);
//八进制
//const char* oct_rule="^0([0-7])+$";
//const regex pattern_oct(oct_rule, regex::icase);//输入的源程序存放处,最大可以存放MAX_LINE个字符
static char *resourceProject = (char*)malloc(MAX_LINE * sizeof(char));
//p = (int*)realloc(p, sizeof(int)* 20);扩容//存放注释
static char *commentStr = (char*)malloc(MAX_LINE * sizeof(char));
static char *commentStr2 = (char*)malloc(MAX_LINE * sizeof(char));//从str中删除目标字符
void delete_char(char str[],char target){int i,j;for(i=j=0;str[i]!='\0';i++){if(str[i]!=target){str[j++]=str[i];}}str[j]='\0';//i-j即为串中存在的目标字符个数
}//处理"//,/* */"注释
void proComment(char str[]){int j=0;int p=0;//遍历resourceProject,记录注释for(int i=0; i<strlen(str); i++) {if(str[i]=='/'&&str[i+1]=='/') {int k=i-1;while(str[++k]!='\n') {commentStr[p++]=str[k];}i=k;} }printf("\n单行注释为:%s\n",commentStr);//遍历resourceProject,原地删除单行注释j=0;p=0;int i;for(i=0; i<strlen(str); i++){if(str[i]=='/'&&str[i+1]=='/') {p=i;while(str[++p]!='\n');i=p;}else{str[j++]=str[i];}
}while(j<=i){str[j++]='\0';}printf("\n单行注释处理后的源程序为:\n%s",str);//处理多行注释“/* 。。。*/”则去除该内容int count = 0;p=0;int k=0;for (int i=0;i<strlen(str);i++){if(str[i]=='/'&&str[i+1]=='*'){k=i;while(str[k]!='*'||str[k+1]!='/'){commentStr2[p++]=str[k++];}commentStr2[p++]='*';commentStr2[p++]='/';i=k+2;}
// printf("\n多行注释处理后的源程序为:%s\n",str);}printf("\n多行注释为:%s\n",commentStr2);//遍历resourceProject,原地删除多行注释j=0;p=0;for(i=0;str[i]!='\0';i++){if(str[i]=='/'&&str[i+1]=='*'){i=i+2;p=i;while(str[p++]!='*'&&str[p+1]!='/');i=p+2;}else{str[j++]=str[i];}
}
printf("end\n");while(j<=i){str[j++]='\0';}printf("\n多行注释处理后的源程序为:\n%s",str);}//endproComment//预处理函数(可以写个target数组传进来)
void preProcessing(){//先处理注释,再处理宏定义等预处理,最后处理空格、换行、制表符proComment(resourceProject);char target1=' ';char target2='\t';char target3='\n';delete_char(resourceProject,target1);delete_char(resourceProject,target2);delete_char(resourceProject,target3);
}//判断是否为字母
bool isChar(char ch){if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))return true;return false;
}//判断是否为数字
bool isDigit(char ch){if(ch>='0'&&ch<='9')return true;return false;
}//判断是否为定界符等
bool isDelimiters(char ch){for(int i=0;i<sizeof(delimiters)/sizeof(delimiters[0]);i++)if(ch==*delimiters[i])return true;return false;
}//判断是否为控制命令
int isContCommands(char *str){for(int i=0;i<sizeof(contCommands)/sizeof(contCommands[0]);i++) {if(strcmp(str,contCommands[i])==0){//匹配return i;}}//不是关键字即为404return 404;
}//判断是否为关键字
int isKeyword(char *str){for(int i=0;i<sizeof(keyWords)/sizeof(keyWords[0]);i++) {if(strcmp(str,keyWords[i])==0){//匹配return i;}}//不是关键字即为404return 404;
}//判断是否为运算符
int isOperators(char str){for(int i=0; i<sizeof(operators)/sizeof(operators[0]); i++) {if(str==*operators[i]){return i;}}//不是关键字即为404return 404;
}//判断是否为特殊字符
int isSpesymbols(char str){for(int i=0; i<sizeof(Spesymbols)/sizeof(Spesymbols[0]); i++) {if(str==*Spesymbols[i]){return i;}}//不是关键字即为404return 404;
}FILE * readTxt(){FILE *fp;char filename[100]; //文件名char tempstr[1024]; //读文件的缓冲bool flag = true; //文件读取成功标志printf("请输入或拖入想要打开的文本文件名及其路径,如c:\\temp.txt\n");while(flag){gets(filename); //这句要用户输入文件名char target='"';delete_char(filename,target);//getchar();if ((fp=fopen(filename,"r"))==NULL){//打开文件,并判断是否有打开错误printf("打开文件%s出现错误\n",filename);memset(filename,0, sizeof filename); //清空数组fclose(fp); //关闭文件printf("请在检查文件路径后再次输入: \n");}else{flag = false;//文件读入resourceProjectint cnt=0;
// while(!feof(fp)) //读文件,直到文件末尾
// {
// resourceProject[cnt++] = fgetc(fp); //将文件内容读入resourceProject
// }printf("文件%s已读取,请检查以下txt文件内容:\n",filename);}}if (fp == NULL )return 0;//以下显示文件内容memset(resourceProject,0,sizeof resourceProject);while(fgets(tempstr,1024,fp)!=NULL) //读文件一行内容,最多1024字符到缓冲区,并判断是否文件已经结束{printf("%s",tempstr); //显示缓冲区内容strcat(resourceProject,tempstr);//统一读入resourceProject}// fclose(fp); //关闭文件return fp;
}//endRead//读入下一个字符
char getChar(int p,char str[])
{return str[p];
}//分析函数
char textAnalyze(char str[]){char* elements[strlen(str)]; //文本元素拆分int tag[strlen(str)];//1:关键字 2:标识符 3:常量 4:运算符 5:分隔符 6:特殊字符 7:控制命令memset(tag,'\0',sizeof(tag)/sizeof(tag[0]));int indTag=0;//tag的indexint Tag=0;memset(elements,'\0',sizeof elements);memset(elements,'\0',(sizeof tag)/sizeof tag[0]);char ch;//读入的字符int p=0;//str的指针char token[128]={};//记录变量名int m=0;
while((ch=getChar(p++,str))!='\0'){Tag=0;//printf("\ngetFirstChar:%c\n",ch);memset(token,'\0',sizeof(token)/sizeof(token[0]));if(isChar(ch)||ch=='_') //可能是标示符或者关键字{m=0;token[m++]=ch;token[m]='\0';bool isKey=false;int tmp=p;bool While=false;while(isChar((ch=getChar(tmp++,str)))||isDigit((ch=getChar(--tmp,str)))){While=true;//printf("while:%c\n",ch);if(isDigit(ch))tmp++;token[m++]=ch;token[m]='\0';p++;if(isKeyword(token)!=404){tag[indTag++]=1;//关键字Tag=1;isKey=true;// p--;break;}}// token[m++]='\0';if(!isKey){//p++;tag[indTag++]=2;//标识符Tag=2;}//else//p++;//printf("(%d,\"%s\")\n",tag[indTag-1],token);printf("(%d,\"%s\")\n",Tag,token);}else if((ch=='-')||isDigit(ch)||(ch=='.'))//数字{bool sym=false;int m=0;char digit[128]={};memset(digit,'\0',sizeof(digit)/sizeof(digit[0]));digit[m++]=ch;digit[m]='\0';int tmp=p;if((ch=='-')&&isDigit((ch=getChar(tmp+1,str)))){sym=true;}long long constNum=0;while(isDigit(ch=getChar(tmp,str))||(ch=getChar(tmp,str))=='.'||(ch=getChar(tmp,str))=='e'||(ch=getChar(tmp,str))=='E'){digit[m++]=ch;digit[m]='\0';constNum=constNum*10+ch-'0';tmp++;p++;}digit[m++] = '\0';//结束符if(!sym){Tag=3;tag[indTag++]=3;//正数常量}else{tag[indTag++]=13;//负数常量Tag=13;}if(constNum>9223372036854775807){tag[indTag++]=23;//大数常量Tag=23;}printf("(%d,\"%s\")\n",tag[indTag-1],digit);}else if(isDelimiters(ch)){// printf("%c\n",ch);token[0]=ch;token[1]='\0';tag[indTag++]=5;printf("(%d,\"%s\")\n",tag[indTag-1],token);}else if(isOperators(ch)!=404)//操作符{switch(ch) //其他字符{case'<':m=0;token[m++]=ch;ch=getChar(p++,str);if(ch=='='){Tag=4;tag[indTag++]=4;token[m++]=ch;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}else{Tag=4;tag[indTag++]=4;token[m++]='\0';p--;printf("(%d,\"%s\")\n",tag[indTag-1],token);}break;case'>':m=0;token[m++]=ch;ch=getChar(p++,str);//取字符if(ch=='='){Tag=4;tag[indTag++]=4;token[m++]=ch;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}else{p--;Tag=4;tag[indTag++]=4;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}break;case'|':m=0;token[m++]=ch;ch=getChar(p++,str);//取字符if(ch=='|'){Tag=4;tag[indTag++]=4;token[m++]=ch;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}else{p--;Tag=4;tag[indTag++]=4;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}break;case'&':m=0;token[m++]=ch;ch=getChar(p++,str);//取字符if(ch=='&'){Tag=4;tag[indTag++]=4;token[m++]=ch;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}else{p--;Tag=4;tag[indTag++]=4;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}break;case'!':m=0;token[m++]=ch;ch=getChar(p++,str);//取字符if(ch=='='){tag[indTag++]=4;token[m++]=ch;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}else{p--;tag[indTag++]=4;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}break;default:{m=0;token[m++]=ch;token[m++]='\0';tag[indTag++]=4;printf("(%d,\"%s\")\n",tag[indTag-1],token);break;}}//endSwitch}//endIfElselse//特殊字符或控制命令{
// printf("%c\n",ch);if(isSpesymbols(ch)!=404){//printf("%c\n",ch);bool isControl=false;if(ch=='#'){m=0;token[m++]=ch;token[m]='\0';int t=p;while(isChar((ch=getChar(t++,str)))){p++;token[m++]=ch;token[m]='\0';if(isContCommands(token)!=404){Tag=7;tag[indTag++]=7;//控制命令isControl=true;break;}}// p++;if(isControl){token[m++]='\0';
// printf("(%d,\"%s\")\n",tag[indTag-1],token);printf("(%d,\"%s\")\n",Tag,token);}}else{m=0;token[m++]=ch;token[m++]='\0';tag[indTag++]=6;//特殊符号符printf("(%d,\"%s\")\n",tag[indTag-1],token);}}}//endElse}//endWhile
}//endfunc//主函数
int main(){char buf[MAX_LINE]; /*缓冲区*/char ch,sh;FILE* fp; /*文件指针*///下面是写数据,将数字0~9写入到data.txt文件中printf("***请选择读取待编译程序的方式***\n");printf("***输入 1:屏幕输入;输入 2:已放入记事本***\n");int tmp;scanf("%d",&tmp);getchar();//吞掉空格if (tmp==1){FILE *fpWrite=fopen("data.txt","a");if(fpWrite==NULL){perror("fail to read");exit(1);return 0;}printf("您输入的是 1,请继续输入待编译程序,并以@加回车结束!\n");printf("输入内容将同步显示..\n");ch = getchar();while (ch != '@') {fputc(ch,fpWrite); //写入文件putchar(ch); //输出到屏幕ch = getchar();}fclose(fpWrite);//关闭文件printf("程序已读入文件data.txt..");//已经获取文件处理程序//fpWrite=fopen("data.txt","a");//重新打开文件,重置文件指针//fclose(fpWrite);//关闭文件getchar();fp=readTxt();//获取文件指针printf("文件读取完毕..\n");printf("resourceProject中的程序为:\n");printf("%s",resourceProject);fclose(fp);//关闭文件}else if(tmp==2){printf("您输入的是 2,接下来将进入文件读取程序..\n");fp=readTxt();//获取文件指针printf("文件读取完毕..\n");printf("resourceProject中的程序为:\n");printf("%s",resourceProject);fclose(fp);//关闭文件}//开始处理读入的程序preProcessing();//预处理printf("\n预处处理完成后的程序为:\n");printf("%s",resourceProject);printf("\n下面开始词法分析:\n");textAnalyze(resourceProject);//下面是读数据,将读到的数据存到数组a[10]中,并且打印到控制台上
// int a[10]={0};
// FILE *fpRead=fopen("data.txt","r");
// if(fpRead==NULL)
// {
// return 0;
// }
// for(int i=0;i<10;i++)
// {
// fscanf(fpRead,"%d ",&a[i]);
// printf("%d ",a[i]);
// }
// getchar();//等待
//
// fclose(fpRead);free(commentStr);
free(resourceProject);return 0;
}
运行结果:


对编译原理中的词法分析实验进行了记录,但需要说明的是,本次实验面向测试集编程,处理方式分为两遍,并非一遍扫描处理。
编写代码时需十分小心指针的位置,符号的提前读入、回溯等。
程序逻辑划分较为明显,有需要的可主要对进行词法分析的 char textAnalyze(char str[]) 函数进行改写。










