编译原理程序设计实践(三) 错误处理和词法分析代码

/* 出错处理过程error */
/* 参数:n:出错代码 */
void error(int n)
{
	cout<<"****"<<setw(cc-1)<<'!'<<setw(2)<<n<<endl; /* 在屏幕cc-1位置显示!与出错代码提示,由于cc
                                           是行缓冲区指针,所以!所指位置即为出错位置 */
	fa1<<"****"<<setw(cc-1)<<'!'<<setw(2)<<n<<endl; /* 在文件cc-1位置输出!与出错代码提示 */
	++err; /* 出错总次数加一 */
}/* error */
//判断是否到了行尾
bool eoln(istream & is)
{
	return is.peek()=='\n';
}
//得到字符对应的数字
int ord(char ch)
{
	return ch -'0';
}
/* 读取原程序中下一个字符过程getch( );*/
void getch(void)
{
	if  (cc == ll)/* 如果行缓冲区指针指向行缓冲区最后一个字符就从文件读一行到行缓冲区 */
	{
		if  (fin.peek()==EOF) /* 如果到达文件末尾 */
		{
			cout<<"Program incomplete"; /* 出错,退出程序 */
			throw(99);
		}
		ll = -1 ; /* 行缓冲区长度置于行首 */
		cc = -1 ; /* 行缓冲区指针置行首 */
		
		cout<<setw(4)<<cx<<' '; /* 输出cx值,宽度为4 */
		fa1<<setw(4)<<cx<<' '; /* 输出cx值,宽度为4到文件 */
		while (!eoln(fin))  /* 当未到行末时 */
		{
			++ll ; /* 行缓冲区长度加一 */
			fin.get(ch); /* 从文件读入一个字符到 ch */
			cout<<ch; /* 在屏幕输出ch */
			fa1<< ch; /* 把ch输出到文件 */
			line[ll] = ch ; /* 把读到的字符存入行缓冲区相应的位置 */
		}
		/* 可见,PL/0源程序要求每行的长度都小于81个字符 */
		cout << endl; 
		++ll ; 	/* 行缓冲区长度加一,用于容纳即将读入的回车符CR */
		fin.get(line[ll]);/* 把#13(CR)读入行缓冲区尾部 */
		fa1<<endl; 
	}
	++cc ; /* 行缓冲区指针加一,指向即将读到的字符 */
	ch = line[cc]; /* 读出字符,放入全局变量ch */
} /* getch( )*/
/* 词法分析过程getsym */
void getsym(void)
{  
	int i, j, k;

	/* getsym */
	while(isspace(ch) ) /* 读一个有效的字符 (跳过读出的字符中多余的空格),但实际上还要跳
									  过多余的回车 */
		getch(); 
	if  (isalpha(ch)) /* 如果读出的字符是一个字母,说明是保留字或标识符 */
	{
		k = -1 ; /* 标识符缓冲区指针置0 */
		do { /* 这个循环用于依次读出源文件中的字符构成标识符 */
			if  (k < al-1) /* 如果标识符长度没有超过最大标识符长度(如果超过,就取前面一部分,把多余的抛弃) */
			{
				++k ; 
				a[k] = ch ;
			}
			getch( );/* 读下一个字符 */
		}while (isalpha(ch)||isdigit(ch)); /* 直到读出的不是字母或数字,由此可知PL/0的标识符构成规则是:
							  以字母开头,后面跟若干个字母或数字 */

		if  (k>= kk) /* 如果当前获得的标识符长度大于等于kk */
			kk = k; /* 令kk为当前标识符长度 */
		else
			do { /* 这个循环用于把标识符缓冲后部没有填入相应字母或空格的空间用空格补足 */
				a[kk] = ' ' ;
				kk = kk - 1; 
			}while (kk != k);
		/* 在第一次运行这个过程时,kk的值为al,即最大标识符长度,如果读到的标识符长度小于kk,
		就把a数组的后部没有字母的空间用空格补足。
		这时,kk的值就成为a数组前部非空格字符的个数。以后再运行getsym时,如果读到的标识符长度大于等于kk,
		就把kk的值变成当前标识符的长度。
		这时就不必在后面填空格了,因为它的后面肯定全是空格。反之如果最近读到的标识符长度小于kk,那就需要从kk位置向前,
		把超过当前标识长度的空间填满空格。
		以上的这样一个逻辑,完全是出于程序性能的上考虑。其实完全可以简单的把a数组中a[k]元素以后的空间不管三七二十一全填空格。 
		*/

		/* 下面开始二分法查找看读出的标识符是不是保留字之一 */
		memcpy(id, a, al);  /* 最后读出标识符等于a */
		i = 0 ; /* i指向第一个保留字 */
		j = norw - 1 ; /* j指向最后一个保留字 */
		do {
			k = (i + j) / 2 ; /* k指向中间一个保留字 */
			if  (memcmp(id ,word[k],al)<= 0) /* 如果当前的标识符小于k所指的保留字 */
				j = k - 1 ; /* 移动j指针 */
			if  (memcmp(id ,word[k],al)>= 0) /* 如果当前的标识符大于k所指的保留字 */
				i = k + 1; /* 移动i指针 */
		}while (i <= j); /* 循环直到找完保留字表 */
		if  (i - 1 > j) /* 如果i - 1 > j表明在保留字表中找到相应的项,id中存的是保留字 */
			sym = wsym[k]; /* 找到保留字,把sym置为相应的保留字值 */
		else
			sym = ident; /* 未找到保留字,把sym置为ident类型,表示是标识符 */
	} /* 至此读出字符为字母即对保留字或标识符的处理结束 */
	else /* 如果读出字符不是字母 */
		if  (isdigit(ch)) /* 如果读出字符是数字 */
		{ /* number */ /* 开始对数字进行处理 */
			k = 0 ; /* 数字位数 */
			num = 0 ; /* 数字置为0 */
			sym = number ; /* 置sym为number,表示这一次读到的是数字 */
			do { /* 这个循环依次从源文件中读出字符,组成数字 */
				num = 10 * num + (ord(ch) - ord('0')) ; /* num * 10加上最近读出的字符ASCII减'0'的ASCII得到相应的数值 */
				++k ; /* 数字位数加一 */
				getch();
			}while (isdigit(ch)); /* 直到读出的字符不是数字为止 */
			if  (k > nmax) /* 如果组成的数字位数大于最大允许的数字位数 */
				error(30); /* 发出30号错 */
		} /* 至此对数字的识别处理结束 */
		else
			if  (ch == ':')/* 如果读出的不字母也不是数字而是冒号 */
			{
				getch(); /* 再读一个字符 */
				if  (ch == '=')/* 如果读到的是等号,正好可以与冒号构成赋值号 */
				{
					sym = becomes ; /* sym的类型设为赋值号becomes */
					getch( );/* 再读出下一个字 */
				}
				else
					sym = nul ; /* 如果不是读到等号,那单独的一个冒号就什么也不是 */
			}/* 以上完成对赋值号的处理 */
			else /* 如果读到不是字母也不是数字也不是冒号 */
				if  (ch == '<')/* 如果读到小于号 */
				{
					getch(); /* 再读一个字符 */
					if  (ch == '=')/* 如果读到等号 */
					{
						sym = leq ; /* 购成一个小于等于号 */
						getch( );/* 读一个字符 */
					}
					else /* 如果小于号后不是跟的等号 */
						sym = lss; /* 那就是一个单独的小于号 */
				}
				else /* 如果读到不是字母也不是数字也不是冒号也不是小于号 */
					if  (ch == '>')/* 如果读到大于号,处理过程类似于处理小于号 */
					{
						getch(); /* 再读一个字符 */
						if  (ch == '=')/* 如果读到等号 */
						{
							sym = geq ; /* 购成一个大于等于号 */
							getch( );/* 读一个字符 */
						}
						else /* 如果大于号后不是跟的等号 */
							sym = gtr; /* 那就是一个单独的大于号 */
					}
					else/* 如果读到不是字母也不是数字也不是冒号也不是小于号也不是大于号 */
					{ /* 那就说明它不是标识符/保留字,也不是复杂的双字节操作符,应该是一个普通的符号 */
						sym = ssym[ch-' '] ; /* 直接成符号表中查到它的类型,赋给sym */
						getch( );/* 读下一个字符 */
					}
					/* 整个if语句判断结束 */
}/* getsym */
/* 词法分析过程getsym总结:从源文件中读出若干有效字符,组成一个token串,识别它的类型
为保留字/标识符/数字或是其它符号。如果是保留字,把sym置成相应的保留字类型,如果是
标识符,把sym置成ident表示是标识符,于此同时,id变量中存放的即为保留字字符串或标识
符名字。如果是数字,把sym置为number,同时num变量中存放该数字的值。如果是其它的操作符,
则直接把sym置成相应类型。经过本过程后ch变量中存放的是下一个即将被识别的字符 */


 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值