编译原理词法分析器的设计

步骤:
1.选择测试代码,即词法分析器的输入,这里选择冒泡排序的代码(仅去掉预处理命令)存入a.txt文件中,如图1所示。
图1测试代码
2.统计针对该测试程序包含的单词符号,设计单词符号对应的种别码,如图2
图2单词符号对应的种别码
3.设计词法分析器
代码如下:(要使该代码能够运行,需先编写a.txt文件,内容为图1的代码,并将该文件与.cpp文件放入同一目录下)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define _KEY_WORD_END "waiting for your expanding"

//定义二元组的结构体
typedef struct
{
	int typenum;
	char *word;
}WORD;

char *str;		  //测试代码字符串
char token[255];  //单词缓冲区
int p_input;      //测试代码字符串的指针
int p_token;     //单词缓冲区指针
char ch;		  //当前读入字符
char *rwtab[] = {"void","int","if","for","break",_KEY_WORD_END};//关键字的指针数组,_KEY_WORD_END不加分号

//自定义函数
char m_getch();     //从代码字符串中读取一个字符到char中
void getbc();       //去掉回车、换行、制表符
void concat();		//为token拼接单词
int letter();		//判断当前字符char是否为字母
int digit();		//判断当前字符char是否为数字
int reserve();		//判断字母开头的是标识符还是关键字,返回其种别码
void retract();		//回退一个字符
char *dtb();		//十进制字符串转换成对应二进制字符串
WORD *scaner();     //词法扫描函数,获得返回一个单词指针

int main ()
{
	FILE *p;					//测试代码文件指针
	FILE *q;					//词法分析器输出的文件指针
	int filesize;				//代码字符串长度
	char *fname_in = "a.txt";   //源程序所在的文件
	char *fname_out = "b.txt";  //词法分析得到的单词符号所在的文件
	p_input = 0;
	int over = 1;				//种别码
	char typenum_str[10];		//种别码对应的字符串,用于fputs写入文件
	WORD *oneword = new WORD;	//单词,即二元组结构体的指针
	
	//打开测试程序所在的文件
    p = fopen(fname_in,"rb");		//p = fopen("D:\\a.txt","rb");
	if(p == NULL)
	{
		printf("打开文件%s错误\n",fname_in);
		exit(1);//exit(-1);
	}

	fseek(p,0,SEEK_END);
	filesize = ftell(p);
	str = (char*)malloc(filesize+1);//动态内存分配
	rewind(p);
	fread(str,sizeof(char),filesize,p);
	str[filesize] = '\0';
	fclose(p);
	//输出文件中的代码
	printf("a.txt文件中的代码为:\n");
	puts(str);
	fclose(p);


	//创建并打开词法分析所得目标程序所在的文件
	q = fopen(fname_out,"wb");
	if(q == NULL)
	{
		printf("打开文件%s错误\n",fname_out);
		exit(1);
	}

	while(over<1000 && over!=-1)  //1000为'\0'的种别码(不会输出,仅作为for循环退出的标志),-1位错误单词的种别码
	{
		oneword = scaner();
		if(oneword->typenum<1000)
		{
			itoa(oneword->typenum,typenum_str,10);//种别码整数转换为对应字符串
			fputs(typenum_str,q);				  //将种别码写入b.txt
			fputs(", ",q);						  //将,写入b.txt
			fputs(oneword->word,q);				  //将单词的值写入b.txt
			fputc('\n',q);						  //写入一个单词后换行
		}
		over = oneword->typenum;
	}
	printf("词法分析所得的单词符号已存入b.txt文件\n");	
	fclose(q);
	return 0;
}




//******************************!!以下为自定义函数!!****************************************
//******************************************************************************************
//从测试代码字符串str中读取一个字符到char中
char m_getch()
{
	ch = str[p_input];
	p_input = p_input+1;
	return (ch);
}

//去掉回车、换行、制表符
void getbc()
{
	while(' '==ch || '\r'==ch || '\n'==ch || '\t'==ch)  //文件回车='\r\n',windows
	{
		ch = str[p_input];
		p_input++;
	}
}

//为token拼接单词
void concat()
{
	token[p_token] = ch;
	p_token++;
	token[p_token] = '\0';  //巧
}

//判断当前字符char是否为字母
int letter()
{
	if(ch>='a'&&ch<='z' || ch>='A'&&ch<='Z')
		return 1;
	else
		return 0;
}

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

//判断字母开头的是标识符还是关键字,返回其种别码
int reserve()
{
	int i = 0;
	while(strcmp(rwtab[i],_KEY_WORD_END))
	{
		if(!strcmp(rwtab[i],token))
		{
			return (i+1);   //关键字的种别码
		}
		i++;
	}
	return 20;              //标识符的种别码
}

//回退一个字符
void retract()
{
	p_input--;
}

//十进制字符串转换成对应二进制字符串
char *dtb()
{
	int b = 0;
	int i = 0;
	int a = atoi(token);//字符串转换成整数
	while(a != 0)
	{
		b = b+a%2*(int)pow(10,i++);
		a = a/2;
	}
	itoa(b,token,10);//整数转换为字符串
	return token;
}

//词法扫描函数,获得返回一个单词指针
WORD *scaner()
{
	WORD *myword = new WORD;
	myword->typenum = 20;	//初始值赋值为标识符的种别码
	myword->word = "";
	p_token = 0;
	m_getch();
	getbc();
	if(letter())			//字母开头,即标识符或者关键字
	{
		while(letter() || digit())
		{
			concat();
			m_getch();
		}
		retract();
		myword->typenum = reserve();  //reserve函数判断是关键字还是标识符
		myword->word = token;
		return (myword);
	}
	else if(digit())		//数字开头,即常数
	{	
		while(digit())
		{
			concat();
			m_getch();
		}
		if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z')  //非常数,例如56x为错误输入
		{
			myword->typenum = -1;
			myword->word = "ERROR";
			return (myword);
		}
		else
		{
			retract();
			myword->typenum = 21; //常数的种别码
			myword->word = dtb(); //dtb返回十进制常数对应二进制的字符串
			return (myword);
		}
	}
	else switch(ch)			//非数字或字母开头,即界符或运算符
	{
		case '[':
			myword->typenum = 22;
			myword->word = "[";
			return (myword);
			break;
		case ']':
			myword->typenum = 23;
			myword->word = "]";
			return (myword);
			break;
		case '(':
			myword->typenum = 24;
			myword->word = "(";
			return (myword);
			break;
		case ')':
			myword->typenum = 25;
			myword->word = ")";
			return (myword);
			break;
		case '{':
			myword->typenum = 26;
			myword->word = "{";
			return (myword);
			break;
		case '}':
			myword->typenum = 27;
			myword->word = "}";
			return (myword);
			break;
		case ',':
			myword->typenum = 28;
			myword->word = ",";
			return (myword);
			break;
		case ';':
			myword->typenum = 29;
			myword->word = ";";
			return (myword);
			break;
		case '<':
			myword->typenum = 40;
			myword->word = "<";
			return (myword);
			break;
		case '>':
			myword->typenum = 41;
			myword->word = ">";
			return (myword);
			break;
		case '=':			//判断以=开头的运算符是=还是==
			m_getch();
			if('=' == ch)
			{
				myword->typenum = 45;
				myword->word = "==";
				return (myword);
			}
			retract();
			myword->typenum = 44;
			myword->word = "=";
			return (myword);
			break;
		case '+':			//判断以+开头的运算符是+还是++
			m_getch();
			if('+' == ch)
			{
				myword->typenum = 44;
				myword->word = "++";
				return (myword);
			}
			retract();
			myword->typenum = 43;
			myword->word = "+";
			return (myword);
			break;
		case '\0':					//代码字符串结束
			myword->typenum = 1000;
			myword->word = "OVER";
			return (myword);
			break;
		default:					//其他字符均为定义,种别码为-1,错误
			myword->typenum = -1;
			myword->word = "ERROR";
			return (myword);
	}
}

4.查看词法分析器的输出,即b.txt文件,部分截图如下
图3

发布了11 篇原创文章 · 获赞 7 · 访问量 1680

猜你喜欢

转载自blog.csdn.net/qq_43015237/article/details/102508322