编译原理——词法分析器的编写(改良版)

词法分析(英语:lexical analysis):

是计算机科学中将字符序列转换为标记(token)序列的过程。进行词法分析的程序或者函数叫作词法分析器(lexical analyzer,简称lexer),也叫扫描器(scanner)。词法分析器一般以函数的形式存在,供语法分析器调用。
(上文摘自维基百科)

分析:

我们知道,语法分析器是分析每个词的,既然这样的话,我们就要对每个词进行分析,然后识别保存,那么如何进行识别呢?

我们通常采用的是种别码,那什么是种别码呢?初学者可能会对这个概念比较模糊。其实种别码就是对每个词的定位,比如我们可以把数字这个词的种别码定义为10,把标识符的种别码定义为11等等。

代码实际讲解:
种别码

/保留字的种别码。从1开始到25
//出现过的标识符的种别码设为26
//未出现过的标识符种别码为52,将该标识符保存到标识符表中
//数字种别码设为27
// 等号种别码为28
// <= 种别码为29.
// <> 种别码为30。
// < 种别码为31
// >= 种别码为32
// > 种别码为33.
// + 种别码为34
// - 种别码为35
// * 种别码为36
// / 种别码 53
// ;种别码为38
// = 种别码为28
//1到51为正常的。空格为100, 换行符为101, #号为102,错误为404
//case ‘(’: syn = 39;
//case ‘)’: syn = 40;

上面这部分是我一部分字符的种别码,没有列出所有,其他种别码读者可以在代码中查询。

其中有一个需要注意一下的是,标识符的种别码分为两种,一种是之前出现过的,为26,一种是新出现的,为52。

我们设定以#为结束符。

关于代码可以看一下我上一篇关于词法分析器的文章,主要不一样是关于种别码的,也是跟语法分析器配合
源代码:
#include<stdio.h>
#include<conio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>

int  wordanalysis();


using namespace std;


//BEGIN
//
//
//a = 10;
//b = a + 10;
//e = a + b
//end#


//保留字的种别码。从1开始到25
//出现过的标识符的种别码设为26
//未出现过的标识符种别码为52,将该标识符保存到标识符表中
//数字种别码设为27
// 等号种别码为28
// <= 种别码为29.
// <> 种别码为30。
// < 种别码为31
// >= 种别码为32
// > 种别码为33.
// + 种别码为34
// - 种别码为35
// * 种别码为36
// / 种别码 53
// ;种别码为38
// = 种别码为28
//1到51为正常的。空格为100, 换行符为101, #号为102,错误为404
//case '(': syn = 39; 
//case ')': syn = 40; 

int i, row = 0, line = 0; //line用来保存标识符的个数。row来保存数字个数。i保存读到哪个字符了
char test[1000];  //这是读取文件的内容的。 
int number[100];	//常数表,有个疑问,数字需要常数来保存吗?
char mark[100][5];   //用来存放标识符的。
int count = 1; //记录出错的行号 
int syn;
int wu;
int final_flag = 0;
//关键字:
char pro[100][100] = { "PROGRAM", "BEGIN", "END", "VAR", "INTEGER", "WHILE",
"IF", "THEN", "ELSE", "DO", "PROCEDURE" ,"char",
"int","if","else","var" ,"return","break",
"do","while","for","double","float","short", "main" };
//关键字:从1开始到25.

//过滤,但是一般时候这样做的效率是非常低的。 
void guolv() {
	char temp[1000];
	int flag = 0;
	int j = 0;
	while (test[flag] != '#') {
		//单行注释// 
		if (test[flag] == '/' && test[flag + 1] == '/') {
			while (test[flag] != '\n') {
				flag++;
			}
		}
		else if (test[flag] == '/' && test[flag + 1] == '*') {
			//多行注释/**/ 
			flag = flag + 2;
			while (test[flag] != '*' && test[flag + 1] != '/') {
				flag++;
			}
			flag = flag + 2;
		}
		temp[j] = test[flag];
		flag++;
		j++;
	}
	temp[j] = '#';
	strcpy(test, temp);
}

//词法分析
int wordanalysis()
{
	//标识符和保留字
	//当第一个字符是字母的时候
	if ((test[i] >= 'A'&&test[i] <= 'Z') || (test[i] >= 'a'&&test[i] <= 'z'))
	{
		char word[10];
		//保留字的种别码。从1开始到25

		int n = 0;
		word[n++] = test[i++];
		while ((test[i] >= 'A'&&test[i] <= 'Z') || (test[i] >= '0' && test[i] <= '9') || (test[i] >= 'a'&&test[i] <= 'z'))
		{
			word[n++] = test[i++];
		}
		word[n] = '\0';
		i--;

		//判断该标识符是否为保留字
		for (n = 0; n < 100; n++)
		{
			if (strcmp(word, pro[n]) == 0)
			{
				syn = n + 1;
				printf(">> %s\t(%d,-) 保留字\n", pro[n], syn);
				return syn;
			}
		}

		//不是关键字的话,判断该标识符是否在标识符表中
		int m = 0;
		if (line != 0)
		{
			int q = 0;
			while (q<line)
			{
				if (strcmp(word, mark[q++]) == 0)
				{
					syn = 26; //出现过的标识符的种别码设为26
					printf(">> %s\t(%d,%d) 标识符\n", word, syn, q);
					return syn;
				}
			}

		}
		//未出现过的标识符种别码为52,将该标识符保存到标识符表中
		strcpy(mark[line], word);
		syn = 52;
		printf(">> %s\t(%d, %d) 标识符\n", word, syn, line + 1);
		line++;
		return syn;

	}
	//数字 
	else if (test[i] >= '0' && test[i] <= '9')
	{
		char x[100];
		int n = 0;
		x[n++] = test[i++];

		while (test[i] >= '0' && test[i] <= '9')
		{
			x[n++] = test[i++];
		}
		x[n] = '\0';
		i--;
		int num = atoi(x); //将字符串转换成int型

						   //判断该常数是否存在于常数表中
		if (row != 0)
		{

			for (int y = 0; y<row; y++)
			{
				if (number[y] == num)
				{
					syn = 27; //数字种别码设为27
					printf(">> %d\t(%d,%d)\n", num, syn, y + 1);
					return syn;
				}
			}
		}

		//将该常数保存到标识符表中
		number[row] = num;
		int line = row;
		syn = 27;
		printf(">> %d\t(%d,%d)\n", num, syn, line + 1);
		row++;
		return syn;
	}

	//各种符号
	else {
		switch (test[i])
		{
		case '=': syn = 28; // 等号种别码为28
			printf(">> =\t(%d,-)\n", syn); return syn;
		case '<':
			i++;
			if (test[i] == '=')
			{
				syn = 29; // <= 种别码为29.
				printf(">> <= \t(%d,-)\n", syn);
				return syn;
			}
			else if (test[i] == '>')
			{
				syn = 30; // <> 种别码为30。
				printf(">> <>\t(%d,-)\n", syn);
				return syn;
			}
			else
			{
				i--;
				syn = 31; // < 种别码为31
				printf(">> <\t(%d,-)\n", syn);
				return syn;
			}
		case '>':
			i++;
			if (test[i] == '=')
			{
				syn = 32; // >= 种别码为32
				printf(">> >=\t(%d,-)\n", syn);
				return syn;
			}
			else
			{
				i--;
				syn = 33; // > 种别码为33.
				printf(">> >\t(%d,-)\n", syn);
				return syn;
			}
		case '+': syn = 34; printf(">> +\t(%d,-)\n", syn); return syn; // + 种别码为34
		case '-': syn = 35;  printf(">> -\t(%d,-)\n", syn); return syn; // - 种别码为35
		case '*': syn = 36;  printf(">> *\t(%d,-)\n", syn); return syn; // * 种别码为36
		case '/': syn = 53; printf(">> *\t(%d, -)\n", syn); return syn; // /的种别码为53 
		case ':': syn = 37; printf(">> :\t(%d,-)\n", syn); return syn;
		case ';': syn = 38; printf(">> ;\t(%d,-)\n", syn); return syn;
		case '(': syn = 39; printf(">> (\t(%d,-)\n", syn); return syn;
		case ')': syn = 40; printf(">> )\t(%d,-)\n", syn); return syn;
		case '{': syn = 41; printf(">> {\t(%d,-)\n", syn); return syn;
		case '}': syn = 42; printf(">> }\t(%d,-)\n", syn); return syn;
		case '[': syn = 43; printf(">> [\t(%d,-)\n", syn); return syn;
		case ']': syn = 44; printf(">> ]\t(%d,-)\n", syn); return syn;
		case '|': syn = 45; printf(">> |\t(%d,-)\n", syn); return syn;
		case '"': syn = 46; printf(">> \"\t(%d,-)\n", syn); return syn;
		case ',': syn = 47; printf(">> ,\t(%d,-)\n", syn); return syn;
		case '\'': syn = 48; printf(">> '\t(%d,-)\n", syn); return syn;//单引号
		case '&':
			i++;
			if (test[i] != '&') {
				i--;
				// & 种别码为 49;
				syn = 49;
				printf(">> &\t(%d,-)\n", syn); return syn;
			}
			//&&种别码为50
			syn = 50;
			printf(">> &&\t(%d,-)\n", syn); return syn;
			// \ 种别码为51
		case '\\': syn = 51; printf(">> \\\t(%d,-)\n", syn); return syn;

		case ' ':
			syn = 100; // 空格种别码为28
			return syn;
		case '\n':count++;
			syn = 101; // 换行符种别码为29
			return syn;
		case '#': syn = 102; //结束符#种别码为30. 
			return syn;
		default:
			syn = 404; //错误的种别码为404
			printf(">> %c error in %d row\n", test[i], count);
			return syn;
		}
	}

}

int main()
{

	int c = 0;
	int m;
	i = 0;
	//读取文件
	FILE *fp;
	fp = fopen("D:\\yes.txt", "r");
	if (fp == NULL)
	{
		printf("can't open file!\n");
		exit(0);
	}
	//把文件内容读取到test数组中
	while (!feof(fp))
	{
		test[c++] = fgetc(fp);
	}
	test[c] = '#';
	while (test[i] != '#') {
		printf("%c", test[i]);
		i++;
	}
	printf("\n");
	printf("------------过滤后的-------------\n");
	guolv();
	i = 0;
	while (test[i] != '#') {
		printf("%c", test[i]);
		i++;
	}
	printf("\n");
	printf("输出文法分析后的:\n");
	//1到51为正常的。空格为100, 换行符为101, #号为102,错误为404
	i = 0;
	while(1){
		int is = wordanalysis();
		if(is == 102){
			break;
		}
		else{
			i++;
		}
	}
	return 0;
}
运行结果:
BEGIN
a = 10;
//fad a = b + c;
b = a + 10;
e = a + b;
d = (a + b) + e;
d = (a + b)
END
------------过滤后的-------------
BEGIN
a = 10;

b = a + 10;
e = a + b;
d = (a + b) + e;
d = (a + b)
END
输出文法分析后的:
>> BEGIN        (2,-) 保留字
>> a    (52, 1) 标识符
>> =    (28,-)
>> 10   (27,1)
>> ;    (38,-)
>> b    (52, 2) 标识符
>> =    (28,-)
>> a    (26,1) 标识符
>> +    (34,-)
>> 10   (27,1)
>> ;    (38,-)
>> e    (52, 3) 标识符
>> =    (28,-)
>> a    (26,1) 标识符
>> +    (34,-)
>> b    (26,2) 标识符
>> ;    (38,-)
>> d    (52, 4) 标识符
>> =    (28,-)
>> (    (39,-)
>> a    (26,1) 标识符
>> +    (34,-)
>> b    (26,2) 标识符
>> )    (40,-)
>> +    (34,-)
>> e    (26,3) 标识符
>> ;    (38,-)
>> d    (26,4) 标识符
>> =    (28,-)
>> (    (39,-)
>> a    (26,1) 标识符
>> +    (34,-)
>> b    (26,2) 标识符
>> )    (40,-)
>> END  (3,-) 保留字
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值