编译原理 · 2022年1月2日 0

编译原理——词法分析

站长注:本文为大二学习编译原理时候的课程设计,完整代码在文末,可能存在少许bug,仅供参考。欢迎大佬指正!(站长原创)

我们知道,编程语言中,源程序需要经过编译器的编译才能被计算机识别。而词法分析,就是编译器通过扫描外部文件或者用户输入字符,再经过程序分析,分析出对应的标识符、关键字、数字、运算符、界符。然后存储在token中,为下一步语法分析做准备。

主函数

主函数中,建立一个字符指针,并逐个字符读取外部文件D:/sample.txt。若指针非空(即没有到sample.txt文件最后一个字符),则进入到查找函数中。

int main()
{    
    fp = fopen("D:\\sample.txt", "r");			//读取文件	
    Fp = fopen("D:\\token.txt", "w");		//写入文件
	
    if (fp == NULL)			//若文件为空,则报错
    {
        perror("Open file error!");
        return 1;
    }
    printf("Check file from D:\\token.txt ...\n\n");
    chr = fgetc(fp);	//定义全局变量chr
    if (chr == EOF)
    {
        perror("fgetc error");
        return 1;
    }
	
	Find();
return 1; 
}

查找函数

Find函数中,每个字符指针识别的字符将会到每一个if else循环中。

void Find()
{ 
	while (chr != EOF)
	{
		char ch = chr;
		if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_')
		{
			identifier(ch);		//识别关键字
		}
		else if (ch >= '0' && ch <= '9')
		{
				digit(ch);	//识别数字		
		}
		else if (ch=='"'||ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == '!' || ch == '*' || ch == '/' || ch == '%' || ch == '+' || ch == '-' || ch == '<' || ch == '<=' || ch == '>' || ch == '>=' || ch == '==' || ch == '!=' || ch == '&&' || ch == '||' || ch == '=' || ch == '.'||ch=='#')
		{
			obelus(ch);		//识别运算符
		}
		else if (ch =='{'||ch=='}'||ch==';'||ch==','||ch=='\n')
		{
			delimiter(ch);		//识别界符
		}
		else if (ch == ' ')
		{
			chr = fgetc(fp);	//若为空格,则直接识别下一个字符
		}
	}
	//printf("\n\nSuceesful!\n");        测试是否识别成功
	fclose(fp);
}

识别字母或者下划线

字母和下划线开头可以作为C语言的标识符(即用户自定义的函数或者变量),需要判断的是,前后几个字符进行连接,是否为C语言的关键字。如果为C语言关键字,我们还需要进行下一步判断。

//识别字母或者下划线
void identifier(char ch)	
{
	char state = '0';	//初始状态
	while (state != '2')
	{
		switch (state)
		{
		case '0':
			if (isletter(ch)|| isline(ch))state = '1';	//是字母或者下划线,转向状态1
			break;
		case '1':
			input[_count++] = ch; 
			ch = GetNextChar();//读取下一个输入字符
			if (isletter(ch) || isline(ch) )state = '1';//是字母或者下划线状态不变
			else state = '2';//其他字符,转向状态2
			break;
		}
	}
	chr = ch;
	return(connect_identifier());//返回识别的单词的token值 
}	 
bool  isletter(char ch)
{
	bool bch=1; 
	if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
		bch = 1;
	else 
	{
		bch = 0;	
	}
	return bch;
}
bool isline(char ch)
{
	bool isch;
	if (ch == '_')
	{
		isch = 1;
	}
	else
	{
		isch = 0;
	}
	return isch;
}

字符串的判断

字符串判断是否为关键字,建立一个数组,把识别的字符保存在数组中,并判断是否为关键字。可以用switch case或者多层嵌套的if else 。判断完成后,需要对数组置空。

//字符串连接判断和清零
void connect_identifier()
{
	std::string str = "";
	for (int i = 0; i < _count; i++)
	{
		str += input[i];
	}
	if (str == "char")
	{
		fprintf(Fp, "%s	101\n", str.c_str());
	}
	else if (str == "int")
	{
		fprintf(Fp, "%s	102\n", str.c_str());
	}
	else if (str == "float") 
	{
		fprintf(Fp, "%s	103\n", str.c_str());
	}
	else if (str == "break")
	{
		fprintf(Fp, "%s	104\n", str.c_str());
	}
	else if (str == "const")
	{
		fprintf(Fp, "%s	105\n", str.c_str());
	}
	else if (str == "return")
	{
		fprintf(Fp, "%s	106\n", str.c_str());
	}
	else if (str == "void")
	{
		fprintf(Fp, "%s	107\n", str.c_str());
	}
	else if (str == "continue")
	{
		fprintf(Fp, "%s	108\n", str.c_str());
	}
	else if (str == "do")
	{
		fprintf(Fp, "%s	109\n", str.c_str());
	}
	else if (str == "while")
	{
		fprintf(Fp, "%s	110\n", str.c_str());
	}
	else if (str == "if")
	{
		fprintf(Fp, "%s	111\n", str.c_str());
	}
	else if (str == "else")
	{
		fprintf(Fp, "%s	112\n", str.c_str());
	}
	else if (str == "for")
	{
		fprintf(Fp, "%s	113\n", str.c_str());
	}
	else if (str == "_")
	{
		fprintf(Fp, "%s	700\n", str.c_str());
	}
	else
	{
		fprintf(Fp, "%s	700\n", str.c_str());
	}	
	clean();
}
void clean() 
{
	_count = 0;
};
void _GetToken() {
	return;
}

运算符识别和连接

运算符号中,部分符号需要连接,例如”<=”和”<“”=”,需要连接判断,原理同上。

//运算符号的连接
void connect_obelus()
{
	std::string str = "";
	for (int i = 0; i < _count; i++)
	{
		str += input[i];
	}
	if (str == "(")
	{
		fprintf(Fp, "%s	201\n", str.c_str());
	}
	else if (str == ")")
	{
		fprintf(Fp, "%s	202\n", str.c_str());
	}
	else if (str == "[")
	{
		fprintf(Fp, "%s	203\n", str.c_str());
	}
	else if (str == "]")
	{
		fprintf(Fp, "%s	204\n", str.c_str());
	}
	else if (str == "!")
	{
		fprintf(Fp, "%s	205\n", str.c_str());
	}
	else if (str == "*")
	{
		fprintf(Fp, "%s	206\n", str.c_str());
	}
	else if (str == "/")
	{
		fprintf(Fp, "%s	207\n", str.c_str());
	}
	else if (str == "%")
	{
		fprintf(Fp, "%s	208\n", str.c_str());
	}
	else if (str == "+")
	{
		fprintf(Fp, "%s	209\n", str.c_str());
	}
	else if (str == "-")
	{
		fprintf(Fp, "%s	210\n", str.c_str());
	}
	else if (str == "<")
	{
		fprintf(Fp, "%s	211\n", str.c_str());
	}
	else if (str == "<=")
	{
		fprintf(Fp, "%s	212\n", str.c_str());
	}
	else if (str == ">")
	{
		fprintf(Fp, "%s	213\n", str.c_str());
	}
	else if (str == ">=")
	{
		fprintf(Fp, "%s	214\n", str.c_str());
	}
	else if (str == "==")
	{
		fprintf(Fp, "%s	215\n", str.c_str());
	}
	else if (str == "!=")
	{
		fprintf(Fp, "%s	216\n", str.c_str());
	}
	else if (str == "&&")
	{
		fprintf(Fp, "%s	217\n", str.c_str());
	}
	else if (str == "||")
	{
		fprintf(Fp, "%s	218\n", str.c_str());
	}
	else if (str == "=")
	{
		fprintf(Fp, "%s	219\n", str.c_str());
	}
	else if (str == "<=")
	{
		fprintf(Fp, "%s	220\n", str.c_str());
	}
	else if (str == "#")
	{
		fprintf(Fp, "%s	221\n", str.c_str());
	}
	else if (str == "++")
	{
		fprintf(Fp, "%s	222\n", str.c_str());
	}
	clean();
}

识别数字和界符

数字同关键字一样,需要进行连接,界符均为单个字符。暂时无需链接。

//识别数字
void digit(char ch)
{
	char state = '0';	//初始状态
	while (state != '2')
	{
		switch (state)
		{
		case '0':
		if (isdigit(ch) && ch != 0)state = '1';	//是数字1~9,转向状态1
			break;
		case '1':
			ch = GetNextChar();//读取下一个输入字符
			if (isdigit(ch))state = '1';//是数字状态不变
			else state = '2';//其他字符,转向状态2
			break;
		}
	}
	chr = ch;
	return(_GetToken());//返回识别的数字的token值 
}
bool  isdigit(char ch)
{
	bool bch;
	if (ch >='0' && ch <= '9')
	{
		bch = 1;
		fprintf(Fp,"%c	400\n",ch);
	}
	else
	{
		bch = 0;
	}	
	return bch;
}
//识别界符
void delimiter(char ch)
{
	isdelimiter(ch);
	ch = GetNextChar();
	chr = ch;
	return(_GetToken());//返回识别的单词的token值 
}
bool  isdelimiter(char ch)
{
	bool bch=1;
	if (ch == '{' || ch == '}' || ch == ';' || ch == ','||ch=='\n')
	{ 
		switch (ch)
		{
			case '{':fprintf(Fp,"%c	301\n", ch);	break;
			case '}':fprintf(Fp, "%c	302\n", ch);	break;
			case ';':fprintf(Fp, "%c	303\n", ch);	break;
			case ',':fprintf(Fp, "%c	304\n", ch);	break;
			case '\n':printf(" "); break;
		}
	}
	else
	{
		bch = 0;
	}
	return bch;
}

完整源代码下载

站长原创,转载时请附上本站地址:https://www.xxsirs.top