词法分析器--C实现

实验目的:

编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类(可自主添加类别)。并依次输出各个单词的内部编码及单词符号自身值。

 程序及其子程序:

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;
		}
	}
	//不是关键字即为404
	return 404;
}

//判断是否为关键字
int isKeyword(char *str){
	for(int i=0;i<sizeof(keyWords)/sizeof(keyWords[0]);i++) {
		if(strcmp(str,keyWords[i])==0){//匹配
			return i;
		}
	}
	//不是关键字即为404
	return 404;
}

//判断是否为运算符
int isOperators(char str){
	for(int i=0; i<sizeof(operators)/sizeof(operators[0]); i++) {
		if(str==*operators[i]){
			return i;
		}
	}
	//不是关键字即为404
	return 404;
}

//判断是否为特殊字符
int isSpesymbols(char str){
	for(int i=0; i<sizeof(Spesymbols)/sizeof(Spesymbols[0]); i++) {
		if(str==*Spesymbols[i]){
			return i;
		}
	}
	//不是关键字即为404
	return 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;
    	//文件读入resourceProject
    	int 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的index
        int 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
        }//endIfEls
		else//特殊字符或控制命令
		{
//			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[]) 函数进行改写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七月是你的谎言..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值