非常小的词法分析程序

最近看了人家的C词法分析程序,就抽了一个C非常小的子集来做练习

/**
 简单语言词法分析程序
 关键字:if else while for           KEY 1
 标识符:(字母|_)(字母|数字|_)*      IDT 2
 运算符:= == + - * / != > <         OPT 3
 常量数字:数字(数字)*               DGT 4
 分界符:;( ) { } ,                 LMT 5
 空白符:\n \t 空格  (文件结束符EOF作分析完毕的参照点)

 2012.8.13 cc
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#define KEY 1
#define IDT 2
#define OPT 3
#define DGT 4
#define LMT 5

char *keywords[5]={"if","else","while","for","break"};
char *opts[7]={"=","==","!=","+","-","*","/"};
char delimiter[7]={';','(',')','{','}',',',' '};
char whitepace[3]={'\n','\t',' '};

int number;		//当前常数
char curr[20];		//当前字符串
char file[1000];	//当前文件的内存映射
int curpos;		//处理的文件当前字符串位置

char filename[]="d:\\test.c";
void prehandle(char *src);
int isLimiter(char ch);
int isLetter(char ch);
int isDigit(char ch);
int isOperator(char *ch);
int isWhitepace(char *src);//包括: \n \t 连续两个空白符

//处理字母开始的串
int handleLetter(char *src){
	assert(isLetter(*src));
	int pos=0;
	char curch;
	//如果是字母可能是两种情况  标识符/关键字
	curch=*(src);
	while((isLetter(curch))||(curch=='_')||(isDigit(curch))){
		curr[pos]=curch;
		curch=*(src+pos+1);
		pos++;
	}
	curr[pos]='\0';
	curpos+=strlen(curr);
	//得到了当前字符串curr,判断curr的属性,可能是关键字或者标识符
	for(int i=0;i<5;i++){
		if(strcmp(keywords[i],curr)==0){
			return KEY;
		}
	}
	return IDT;
}

//处理下划线开始的串
int handleUnderline(char *src){
	assert(*src=='_');
	int pos=0;
	char curch=*(src+curpos);
	while((isLetter(curch))||(curch=='_')||(isDigit(curch))){
		curr[pos++]=curch;
		curpos++;
		curch=*(src+curpos);
	}
	return IDT;
}

//处理数字开始的串
int handleDigit(char *src){
	assert(isDigit((*src)));
	int pos=0;
	char curch=*(src);
	while(isDigit(curch)){
		curr[pos]=curch;
		curch=*(src+pos+1);
		pos++;
	}
	curr[pos]='\0';
	curpos+=strlen(curr);
	return DGT;
}
//处理分界符
int handleLimitter(char *src){
	assert(isLimiter(*src));
	curr[0]=*src;
	curr[1]='\0';

	curpos++;

	return LMT;
}
//处理运算符
int handleOperator(char *src){
	assert(isOperator(src));
	char curch=*src;
	if((curch=='+')||(curch=='-')||(curch=='*')||(curch=='/')||(curch=='>')||(curch=='<')){
		curr[0]=curch;
		curr[1]='\0';
		curpos++;
		return OPT;
	} 
	// !=
	else if((curch=='!')&&((*(src+1))=='=')){
		curr[0]='!';
		curr[1]='=';
		curr[2]='\0';

		curpos+=2;

		return OPT;
	}
	// == / =
	else if((curch=='=')){
		if(((*(src+1))=='=')){
			curr[0]='=';
			curr[1]='=';
			curr[2]='\0';

			curpos+=2;

			return OPT;
		}
		else{
			curr[0]='=';
			curr[1]='\0';

			curpos++;

			return OPT;
		}
	}
	return OPT;
}
int main(){
	FILE *fp;
	curpos=0;

	if((fp=fopen(filename,"r"))==NULL){
		printf("Open file <%s> error .\n",filename);
		return 0;
	}
	//fgets(file,200,fp);
	int pos=0;
	while(!feof(fp)){
		file[pos++]=fgetc(fp);
	}
	file[pos]=EOF;

	//strcpy(file,"while(asdd==2);");
	puts(file);
	prehandle(file);
	puts(file);
	char curch;
	while((*(file+curpos))!=EOF){
		curch=*(file+curpos);
		int curType;
		while(curch==' '){
			curpos++;
			curch=*(file+curpos);
		}
		//字母
		if(isLetter(curch)){
			curType=1;
		}
		//下划线
		else if(curch=='_'){
			curType=2;
		}
		//数字
		else if(isDigit(curch)){
			curType=3;
		}
		//分界符
		else if(isLimiter(curch)){
			curType=4;
		}
		//运算符
		else if(isOperator((file+curpos))){
			curType=5;
		}
		else if(curch==EOF){
			printf("Lex analysis is done.\n");
			break;
		}
		else{
			curType=0;
		}

		switch(curType){
			case 1:
				printf("<%d,%s>\n",handleLetter(file+curpos),curr);
				break;
			case 2:
				printf("<%d,%s>\n",handleUnderline(file+curpos),curr);
				break;
			case 3:
				printf("<%d,%s>\n",handleDigit(file+curpos),curr);
				break;
			case 4:
				printf("<%d,%s>\n",handleLimitter(file+curpos),curr);
				break;
			case 5:
				printf("<%d,%s>\n",handleOperator(file+curpos),curr);
				break;

			default:
				printf("error.\n");
				return 0;
		}
	}
	fclose(fp);
	return 0;
}

//预处理掉空白符
void prehandle(char *src){
	int pos=0;
	char cur;
	while((cur=(*(src+pos)))!=EOF){
		if(cur=='\n'||cur=='\t'){
			*(src+pos)=' ';
		}
		++pos;
	}
}

//判断是否是空白符
int isWhitepace(char *src){
	char cur;
	cur=*src;
	//换行和tab是空白符
	if(cur=='\n'&&cur=='\t'){
		return 1;
	}
	//连续两个空白符算,第一个算,连续n个空格符,前面n-1个算,保留最后一个
	if(cur==' '){
		if(*(src+1)==' '){
			return 1;
		}
	}
	return 0;
}
//判断是否是分界符
int isLimiter(char ch){
	for(int i=0;i<sizeof(delimiter)/sizeof(char);i++){
		if(delimiter[i]==ch){
			return 1;
		}
	}
	return 0;
}

//判断是否是数字
int isDigit(char ch){
	if(ch>='0'&&ch<='9'){
		return 1;
	}
	else{
		return 0;
	}
}

//是否是字母
int isLetter(char ch){
	if(((ch>='a'&&ch<='z'))||((ch>='A')&&(ch<='Z'))){
		return 1;
	}
	else{
		return 0;
	}
}

//判断是否是运算符
int isOperator(char *ch){
	char curch=*ch;
	if(
		(curch=='+')||
		(curch=='-')||
		(curch=='*')||
		(curch=='/')||
		(curch=='<')||
		(curch=='>')
		){
		return 1;
	}
	//!=
	else if((curch=='!')&&((*(ch+1))=='=')){
		return 1;
	}
	//==
	else if((curch=='=')){
		return 1;
	}
	else
		return 0;
}


1.代码扩展性。比如当要增加关键字的时候,要修改很大部分代码

可以用文件来处理,把关键字保存在一个配置文件中,提供一个增加关键字接口,一方面用户可以用这个接口来灵活添加关键字,另一方面我们也可以通过修改文件来设置哪些算关键字。

2.效率问题。当关键字很多时,一个源文件会涉及很多关键字,难道每一个查找都用完全遍历方法,这无疑会是编译效率大大降低

可以用hash来解决,有很多字符串哈希算法,包括BKDB,ELF等等,来提高编译效率


当作练习好了


你可能感兴趣的:(算法,File,null,扩展,语言,FP)