编译原理:C语言词法分析器

编译原理的实验:完成对C语言的词法分析

先说一下整体框架:

基类:Base  封装了一些基础的字符判断函数,如下:

int charkind(char c);//判断字符类型
	int spaces(char c); //当前空格是否可以消除
	int characters(char c);//是否是字母
	int keyword(char str[]);//是否是关键字
	int signwords(char str[]);//是否是标识符
	int numbers(char c);//是否是数字
	int integers(char str[]);//是否是整数
	int floats(char str[]);//是否是浮点型

派生类 LexAn 继承Base并且封装了对行和单词处理的函数,如下:

void scanwords(); //处理每一行
		void clearnotes();//清除注释和多余的空格
		void getwords(int state);//处理出单词
		void wordkind(char str[]);//判断单词类型并且输出

函数之间调用关系如下:

好了,整体框架说完了,我们来说具体的实现:

(一)清除注释和多余的空格

(1)C语言的注释有//和/* 两种形式,所以如果当前读进的是 / 只需分情况判断下一个:

如果是/ 那么本行 //之后的肯定都是注释,只需要保存注释,更新当前行即可;

如果是* ,那么接着寻找直至 */位置,保存注释,更新当前行,然后继续这个操作(有可能有本行有多个 /* */).

不足:不能处理跨行注释。

(2)处理多余的空格这里较为草率,只处理了形如if (    a    >=   b  ),即特殊符号和字母(数字)之间的空格;只要空格两端有特殊符号,那么去掉当前空格便不会造成错误。

void LexAn::clearnotes()
{
	int i, j, k;
	int noteCount = 0;
	int flag = 0;
	char note[100];

	/*注释*/
	for (i = 0; bufferin[buffernum][i] != '\0'; i++)
	{
		if (bufferin[buffernum][i] == '"')
		{
			flag = 1 - flag;
			continue;
		}
		if (bufferin[buffernum][i] == '/' && flag == 0)
		{
			if (bufferin[buffernum][i + 1] == '/')
			{
				for (j = i; bufferin[buffernum][j] != '\0'; j++)
				{
					note[noteCount++] = bufferin[buffernum][j];
				}
				note[noteCount] = '\0';
				noteCount = 0;
				fprintf(fout, "  [ %s ]  ----  [ 注释 ]\n", note);
				bufferin[buffernum][i] = '\0';
				break;
			}

			if (bufferin[buffernum][i + 1] == '*')
			{
				note[noteCount++] = '/';
				note[noteCount++] = '*';
				for (j = i + 2; bufferin[buffernum][j] != '\0'; j++)
				{
					note[noteCount++] = bufferin[buffernum][j];
					if (bufferin[buffernum][j] == '*' && bufferin[buffernum][j + 1] == '/')
					{
						j += 2;
						note[noteCount++] = bufferin[buffernum][j];
						note[noteCount] = '\0';
						noteCount = 0;
						fprintf(fout, "  [ %s ]  ----  [ 注释 ]\n", note);
						break;
					}
				}
				for (; bufferin[buffernum][j] != '\0'; j++, i++)
				{
					bufferin[buffernum][i] = bufferin[buffernum][j];
				}
				if (bufferin[buffernum][j] == '\0')
				{
					bufferin[buffernum][i] = '\0';
				}
			}
		}
	}

	//空格
	for (i = 0, flag = 0; bufferin[buffernum][i] != '\0'; i++)
	{
		if (bufferin[buffernum][i] == '"')
		{
			flag = 1 - flag;
			continue;
		}
		if (bufferin[buffernum][i] == ' ' && flag == 0)
		{
			for (j = i + 1; bufferin[buffernum][j] != '\0' && bufferin[buffernum][j] == ' '; j++)
			{
			}
			if (bufferin[buffernum][j] == '\0')
			{
				bufferin[buffernum][i] = '\0';
				break;
			}
			if (bufferin[buffernum][j] != '\0' && ((spaces(bufferin[buffernum][j]) == 1) || (i > 0 && spaces(bufferin[buffernum][i - 1]) == 1)))
			{
				for (k = i; bufferin[buffernum][j] != '\0'; j++, k++)
				{
					bufferin[buffernum][k] = bufferin[buffernum][j];
				}
				bufferin[buffernum][k] = '\0';
				i--;
			}
		}
	}

	//制表符
	for (i = 0, flag = 0; bufferin[buffernum][i] != '\0'; i++)
	{
		if (bufferin[buffernum][i] == '\t')
		{
			for (j = i; bufferin[buffernum][j] != '\0'; j++)
			{
				bufferin[buffernum][j] = bufferin[buffernum][j + 1];
			}
			i = -1;
		}
	}
}

(二)最重要的状态机的转化

画图不是很好话,我尽量用语言清除地描述,大家还需结合源码分析:

主要分为 <字母, 1> <数字,  2> <$ _ ,  3> <4 ,/ >(转义) < = ,5> <0,else >

state初始值设为0:

(1)如果首位字符是字母,那么只可能是标识符和关键字,之后遇到除 数字,字母,$,_,之外的字符结束,取出单词。

(2)如果首位字符是数字,那么只能是数字,即八进制,十六进制,. ,数字,$ ,之后遇到除上述之外的字符结束,取出单词。

(3)如果首位是$ _ ,那么只能是标识符,即字母,数字,$,之后遇到除上述之外的字符结束,取出单词。

(4)如果首位是特殊字符(" . () = 等),那么再分开处理,流程和上述的一致,遇到不可能的组合结束;这部分看代码吧。

//状态机
void LexAn::getwords(int state)
{
	char word[100];
	int charCount = 0;
	int finish = 0;
	int num;
	int i, j, k;
	for (i = 0; bufferscan[i] != '\0'; i++)
	{
		switch (state / 10)
		{
		case 0:
			switch (charkind(bufferscan[i]))
			{
			case 1:
				word[charCount++] = bufferscan[i];
				state = 10;
				break;
			case 2:
				word[charCount++] = bufferscan[i];
				state = 20;
				break;
			case 3:
				word[charCount++] = bufferscan[i];
				state = 30;
				break;
			case 0: case 5:
				word[charCount++] = bufferscan[i];
				switch (bufferscan[i])
				{
				case '"':
					state = 41;
					break;
				case '\'':
					state = 42;
					break;
				case '(': case ')': case '{': case '}': case '[': case ']': case ';': case ',': case '.':
					state = 50;
					word[charCount] = '\0';
					finish = 1;
					break;
				case '=':
					state = 43;
					break;
				default:
					state = 40;
					break;
				}
				break;
			default: word[charCount++] = bufferscan[i]; break;
			}
			break;
		case 1:
			switch (charkind(bufferscan[i]))
			{
			case 1:
				word[charCount++] = bufferscan[i];
				state = 10;
				break;
			case 2:
				word[charCount++] = bufferscan[i];
				state = 20;
				break;
			case 3:
				word[charCount++] = bufferscan[i];
				state = 30;
				break;
			case 0:case 5:
				word[charCount] = '\0';
				num = 0;
				while (word[num] != '\0')
					num++;

				<span style="color:#ff6600;">//长度的处理 !!
				if (num>7)
					word[7] = '\0';</span>

				i--;
				finish = 1;
				state = 50;
				break;
			default: word[charCount++] = bufferscan[i]; break;
			}
			break;
		case 2:
			switch (charkind(bufferscan[i]))
			{
			case 1:
				word[charCount++] = bufferscan[i];
				state = 20;
				break;
			case 2:
				word[charCount++] = bufferscan[i];
				state = 20;
				break;
			case 3:
				word[charCount++] = bufferscan[i];
				state = 30;
				break;
			case 0:
				if (bufferscan[i] == '.')
				{
					word[charCount++] = bufferscan[i];
					state = 20;
					break;
				}
				word[charCount] = '\0';
				i--;
				finish = 1;
				state = 50;
				break;
			default: word[charCount++] = bufferscan[i]; break;
			}
			break;
		case 3:
			switch (charkind(bufferscan[i]))
			{
			case 1:
				word[charCount++] = bufferscan[i];
				state = 30;
				break;
			case 2:
				word[charCount++] = bufferscan[i];
				state = 30;
				break;
			case 3:
				word[charCount++] = bufferscan[i];
				state = 30;
				break;
			case 0:
				word[charCount] = '\0';
				i--;
				finish = 1;
				state = 50;
				break;
			default: word[charCount++] = bufferscan[i]; break;
			}
			break;
		case 4:
			switch (state)
			{
			case 40:
				switch (charkind(bufferscan[i]))
				{
				case 1:
					word[charCount] = '\0';
					i--;
					finish = 1;
					state = 50;
					break;
				case 2:
					word[charCount] = '\0';
					i--;
					finish = 1;
					state = 50;
					break;
				case 3:
					word[charCount] = '\0';
					i--;
					finish = 1;
					state = 50;
					break;
				case 0:
					word[charCount++] = bufferscan[i];
					state = 40;
					break;
				default: word[charCount++] = bufferscan[i]; break;
				}
				break;
			case 41:
				word[charCount++] = bufferscan[i];
				if (bufferscan[i] == '"')
				{
					if (charkind(bufferscan[i - 1]) == 4)
					{
					}
					else
					{
						word[charCount] = '\0';
						finish = 1;
						state = 50;
					}
				}
				break;
			case 42:
				word[charCount++] = bufferscan[i];
				if (bufferscan[i] == '\'')
				{
					word[charCount] = '\0';
					finish = 1;
					state = 50;
				}
				break;
			case 43:
				if (bufferscan[i] == '=')
				{
					word[charCount++] = bufferscan[i];
					state = 43;
				}
				else
				{
					word[charCount] = '\0';
					finish = 1;
					i--;
					state = 50;
				}
				break;
			default: word[charCount++] = bufferscan[i]; break;
			}
			break;
		case 5:
			finish = 0;
			state = 0;
			charCount = 0;
			i--;

			wordkind(word);
			break;
		default:break;
		}
		if (bufferscan[i + 1] == '\0')
		{
			word[charCount] = '\0';
			wordkind(word);
		}
	}
}

另外注意:应实验要求,对长度超过7的标识符直接截断。如果需要正常处理的话删掉代码中红色标注的部分即可。

(三)效果截图:

本项目全部源码放在个人  Github上,欢迎大家star和fork学习哈。

时间: 2024-10-29 14:30:31

编译原理:C语言词法分析器的相关文章

大前端开发者需要了解的基础编译原理和语言知识

转自:https://yq.aliyun.com/articles/180879 在我刚刚进入大学,从零开始学习 C 语言的时候,我就不断的从学长的口中听到一个又一个语言,比如 C++.Java.Python.JavaScript 这些大众的,也有 Lisp.Perl.Ruby 这些相对小众的.一般来说,当程序员讨论一门语言的时候,默认的上下文经常是:“用 xxx 语言来完成 xxx 任务”.所以一直困扰着的我的一个问题就是,为什么完成某个任务,一定要选择特定的语言,比如安卓开发是 Java,前

编译原理简单词法分析器(first,follow,分析表)源码下载

编译原理(简单词法分析器下载) http://files.cnblogs.com/files/hujunzheng/%E7%AE%80%E5%8D%95%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90%E5%99%A8.zip

编译原理三大经典书籍

1.龙书(Dragon book)英文名:Compilers: Principles,Techniques,and Tools作者:Alfred V.Aho,Ravi Sethi,Jeffrey D.Ullman中文名:编译原理技术和工具 2.虎书(Tiger book)英文名:Modern Compiler Implementation in C作者:Andrew W.Appel,with Jens Palsberg中文名:现代编译原理-C语言描述 3.鲸书(Whale book)英文名:Ad

Atitit.编译原理与概论

编译原理 词法分析 Ast构建,语法分析 语意分析 6 数据结构  1. ? 记号 2. ? 语法树 3. ? 符号表 4. ? 常数表 5. ? 中间代码 1. ? 临时文件 7 其他问题  2. ? 分析和综合 3. ? 前端和后端 4. ? 遍 5. ? 语言定义和编译器 1.3 程序设计语言的发展历程1.3.1 走向高级程序设计语言1.3.2 对编译器的影响1.3.3 1.3节的练习1.4 构建一个编译器的相关科学1.4.1 编译器设计和实现中的建模1.4.2 代码优化的科学1.5 编译

编译原理三大经典书籍(龙书 虎书 鲸书)

1.龙书(Dragon book)  英文名:Compilers: Principles,Techniques,and Tools  作者:Alfred V.Aho,Ravi Sethi,Jeffrey D.Ullman  中文名:编译原理技术和工具   第一版龙书   第二版龙书 龙书”.龙书是Alfred V. Aho等人于1986年出版的,由于出版年代较早,其中包含部分过时的技术并且没有反映一些新的编译技术.新编的<编译原理>抛弃诸如算符优先分析等过时技术,增加面向对象编译.类型检查等新

0909 关于编译原理的理解

1.编译原理学什么? 答:编译原理是学习如何制作编译器,从而能够将自己所写的代码能够转换成机器能明白的语言,各种文法.各种词法语法分析算法,语义分析要处理很多很多细节,特别对于比较复杂的语言:最后的指令生成,可能需要读各种手册,也比较枯燥. 2.为什么学编译原理? 答:理解机器是如何理解高级语言的,能够理解自己所写的代码是如何转换成为机器的低级语言并且运行. 3.怎么学编译原理? 答:先去了解有关编译原理的基本知识,再结合程序代码进行学习,并且要学习编译原理的语言,去网上看有关如何学习编译原理的

&lt;编译原理 - 函数绘图语言解释器(1)词法分析器 - python&gt;

<编译原理 - 函数绘图语言解释器(1)词法分析器 - python> 背景 编译原理上机实现一个对函数绘图语言的解释器 - 用除C外的不同种语言实现 解释器分为三个实现块: 词法分析器:用于识别一条语句中的关键词是否符合预先定义的规则. 语法分析器:用来确定一条语句是否满足语法规则. 解释器:用来确定满足语法规则的句子,在意思上是否符合要求. 设计思路: 设计记号:词法分析器读取一个序列并根据构词规则把序列转化为记号流 定义一个字典:把所有符合一个模式的保留字.常量名.参数名.函数名等放进字

编译原理 - 1 手撸状态机词法分析器

感谢vczh轮子叔的坑了的教程,我的编译原理第一次入了个门,词法分析写完了,今后可以看看书继续往下学了. http://www.cppblog.com/vczh/archive/2014/03/02/206014.html 词法分析,就是对于一段代码,把他们分割成一个个的token,同时记录他们的行列号,丢掉不必要的信息,这个词法分析器很简单,简单的状态机就能胜任,用正则就没有自己造轮子的快感了,所以要自己手撸状态机拆token出来. 模仿vczh的语言,我的语言包括了以下要素 标识符:大小写字

编译原理实战——使用Lex/Flex进行编写一个有一定词汇量的词法分析器

编译原理实战--使用Lex/Flex进行编写一个有一定词汇量的词法分析器 by steve yu 2019.9.30 参考文档:1.https://blog.csdn.net/mist14/article/details/486413492.https://wenku.baidu.com/view/1c6398903868011ca300a6c30c2259010202f3a4.html 1.Flex工具的概述 Flex工具是生成C语言的工具,我们在日常生活中,如果直接使用C语言进行编写词法分析