编译原理 实验4 语义分析(基于PL/0,使用C++代码编写)

1 实验任务

审查每一个语法结构的静态语义,即验证语法正确的结构是否有意义。此部分不再借助已有工具,需手写代码来完成。

2 实验内容

(1)实验要求
你的程序需要对输入文件进行语义分析并检查错误进行输出。

(2)输入格式
一个包含源代码的文本文件,程序需要能够接收一个输入文件名作为参数。

(3)输出格式
要求通过标准输出打印程序的运行结果。对于那些没有语义错误的输入文件,你的程序不需要输出任何内容。对于那些存在语义错误的输入文件,你的程序应当输出相应的错误信息,这些信息包括错误类型、出错的行号以及说明文字,其格式为:
Error type [错误类型] at Line [行号]: [说明文字].

3 错误类型声明

自定义错误类型如下(与C–重合的部分不再给出补充说明):

  1. 错误类型 1:变量在使用时未经定义。
  2. 错误类型 2:函数在调用时未经定义。
  3. 错误类型 3:变量出现重复定义,或变量与前面定义过的结构体名字重复。
  4. 错误类型 4:函数出现重复定义(即同样的函数名出现了不止一次定义)。
    说明:在测试此功能时,要注意PL/0不能将变量定义在begin、end内,只能在过程体外部定义。否则会引发过程相关的错误。
  5. 错误类型 5:赋值号两边的表达式类型不匹配。
    说明:改进的PL/0 类型只有常量、变量、过程类型。它们三者互相赋值会导致此错误,事实上,只有左部为变量类型才是合法的赋值。
    同时,常量只能由数字赋初值。
  6. 错误类型 6:操作数类型不匹配或操作数类型与操作符不匹配。
    说明:当因子为过程或其他非法值时,使得操作数不合法或表达式运算不合法。
  7. 错误类型 7:过程声明格式不正确(procedure后不为标识符)(原:return 语句的返回类型与函数定义的返回类型不匹配。)
    说明:PL/0仅有过程体,无返回值。故进行如上修改。
  8. 错误类型 8:read或write函数的调用不完整(括号缺失)(原:函数调用时实参与形参的数目或类型不匹配。)
    说明:PL/0调用过程无参数。但read和write需要参数,故进行如上修改。
  9. 错误类型 9:read函数的参数不是id(原:对非数组型变量使用“[…]”(数组访问)操作符。)
    说明:改进的PL/0无数组。
  10. 错误类型 10:调用函数格式非法(call后不为过程标识符)(原:对普通变量使用“(…)”或“()”(函数调用)操作符。)

说明:PL/0与C–调用方式不同。
前十个错误类型是对C–相关错误的进行一定修改,以下是结合书后代码新增的一些错误类型。有些错误设计后在实际测试时发现不合适就删除了,下面只给出保留部分的声明。

  1. 错误类型 11:缺少赋值符号或赋值符号不正确。
  2. 错误类型 12:一条语句定义(多个)常量或变量时漏掉了逗号或者分号/过程结束时漏了分号。
  3. 错误类型 14:数字过长。
  4. 错误类型 16:if后缺少then语句。
  5. 错误类型 18:while语句缺少do。
  6. 错误类型 19:地址超出上界。
  7. 错误类型 20:逻辑表达式不合法(逻辑符号不合法)。
  8. 错误类型 21:因子标识符为过程。
  9. 错误类型 22:应输入),表达式不完整。
  10. 错误类型 24:程序不以.结尾。

4 文件结构与代码

算法在处理上的词法分析思路与第一个实验一致(专栏实验1),而语法分析则**采用了递归下降(专栏实验2)**的方法,在此基础上做出改进并进行了出错处理。

递归下降子程序包含了所有EBNF的描述,可以根据EBNF描述来写出递归下降子程序。对于错误处理的方法主要是通过在这些递归下降子程序中的相应位置插入error方法,在处理的时候检测是否有错误产生,若有错误则调用error函数进行出错提示和处理。

4.1 代码结构

在这里插入图片描述

4.2 详细代码

pl0.h:

#include<sstream>

# define norw 13 //关键字个数 
# define txmax 100 //名字表容量 
# define nmax 14 //number 的最大位数 
# define al 10 //符号的最大长度
# define levmax 3 //max depth of block nesting 最大允许过程嵌套声明层数[0,lexmax]
enum symbol {
    
    
	nul, ident, number, plus, minus,
	times, slash, oddsym, eql, neq, //slash 斜线
	lss, leq, gtr, geq, lparen, //leq :less than or equal to; gtr: great than; lparen:left parenthesis
	rparen, comma, semicolon, period, becomes,//comma 逗号 semicolon 分号 period 句号 becomes 赋值号
	beginsym, endsym, ifsym, thensym, whilesym,
	writesym, readsym, dosym, callsym, constsym,
	varsym, procsym,
};
#define symnum 32 
enum object {
    
     //object 为三种标识符的类型
	constant,
	variable,
	procedur,
};
FILE * fa1; //输出源文件及其各行对应的首地址
FILE * fas; //输出名字表
bool tableswitch; //显示名字表与否
char ch; //获取字符的缓冲区 ,getch 使用
enum symbol sym; //当前符号
char id[al + 1]; //当前 ident
int num; //当前 number 
int cc, ll; //getch 使用的计数器 ,cc 表示当前字符 (ch)的位置
int lineIndex; //当前行数
char line[81]; //读取行缓冲区
char a[al + 1]; //临时符号
char word[norw][al]; //保留字
enum symbol wsym[norw]; //保留字对应的符号值
enum symbol ssym[256]; //单字符的符号值
bool declbegsys[symnum]; //表示声明开始的符号集合 ,declaring begin symbol set 
bool statbegsys[symnum]; //表示语句开始的符号集 , statement 
bool facbegsys[symnum]; //表示因子开始的符号集合 ,factor 

struct tablestruct//有用的属性是前几个,后面两个属性为生成中间代码
{
    
    
	char name[al]; // 名字 
	enum object kind; // 类型仅三种
	int val; //const数值
	int level; // 所处层,const 不使用
	int adr; //地址
	int size; //需要分配的数据区空间
};
struct tablestruct table[txmax]; //名字表 
FILE * fin; //fin 文本文件用于指向输入的源程序文件
char fname[al];
int err; //错误计数器
#define getsymdo if(-1==getsym())return -1 
#define getchdo if(-1==getch())return -1 
#define expressiondo(a,b,c) if(-1==expression(a,b,c))return -1 
#define factordo(a,b,c) if(-1==factor(a,b,c))return -1 
#define termdo(a,b,c) if(-1==term(a,b,c))return -1 
#define conditiondo(a,b,c) if(-1==condition(a,b,c))return -1 
#define statementdo(a,b,c) if(-1==statement(a,b,c))return -1 
#define constdeclarationdo(a,b,c) if(-1==constdeclaration(a,b,c))return -1 
#define vardeclarationdo(a,b,c) if(-1==vardeclaration(a,b,c))return -1 
void error(int n);
int getsym();
int getch();
void init();
//集合操作声明
//int inset(int e, bool*s);
int addset(bool*sr, bool*s1, bool*s2, int n);
int subset(bool*sr, bool*s1, bool*s2, int n);
int mulset(bool*sr, bool*s1, bool*s2, int n);
//递归下降主要子程序声明
int block(int lev, int tx, bool* fsys);
int factor(bool* fsys, int* ptx, int lev);
int term(bool*fsys, int*ptx, int lev);
int condition(bool*fsys, int*ptx, int lev);
int expression(bool*fsys, int*ptx, int lev);
int statement(bool*fsys, int*ptx, int lev);
int vardeclaration(int* ptx, int lev, int* pdx);
int constdeclaration(int* ptx, int lev, int* pdx);
int position(char* idt, int tx);
void enter(enum object k, int* ptx, int lev, int* pdx);

源.cpp:

#include<stdio.h> 
#include"pl0.h" 
#include"string.h" 
int main()
{
    
    
	bool nxtlev[symnum];
	printf("Input pl/0 file ?");
	scanf("%s", fname); 
	fin = fopen(fname, "r"); 
	if (fin)
	{
    
    
		fa1 = fopen("fa1.txt", "w");
		fprintf(fa1, "Iput pl/0 file ?");
		fprintf(fa1, "%s\n", fname);
		init(); 
		err = 0; //错误计数器置 0 
		cc = lineIndex = ll = 0;
		ch = ' ';
		if (-1 != getsym())
		{
    
    
			fas = fopen("fas.tmp", "w");
			addset(nxtlev, declbegsys, statbegsys, symnum);
			nxtlev[period] = true;
			if (-1 == block(0, 0, nxtlev)) //调用编译程序
			{
    
    
				fclose(fa1);
				fclose(fas);
				fclose(fin);
				printf("\n");
			}
			fclose(fa1);
			fclose(fas);
			if (sym != period)
			{
    
    
				error(24);
			}
			if (err == 0) {
    
    
				printf("\ncomplie sucess!\n");
			}
			else {
    
    
				printf("\n%derrors,complie failed.\n",err);
			}
		}
		fclose(fin);
	}
	else
		printf("File doesn't exist! \n");
	printf("\n");
	getchar();
	getchar();
	return 0;
}
//初始化符号表等内容
void init()
{
    
    
	int i;
	for (i = 0; i <= 255; i++)
	{
    
    
		ssym[i] = nul; //ssym :单字符的符号值
	}
	ssym['+'] = plus;
	ssym['-'] = minus;
	ssym['*'] = times;
	ssym['/'] = slash;
	ssym['('] = lparen;
	ssym[')'] = rparen;
	ssym['='] = eql;
	ssym[','] = comma;
	ssym['.'] = period;
	ssym['#'] = neq;
	ssym[';'] = semicolon;
	/*设置保留字名字 ,按照字母顺序 ,便于折半查找 */
	strcpy(&(word[0][0]), "begin");
	strcpy(&(word[1][0]), "call");
	strcpy(&(word[2][0]), "const");
	strcpy(&(word[3][0]), "do");
	strcpy(&(word[4][0]), "end");
	strcpy(&(word[5][0]), "if");
	strcpy(&(word[6][0]), "odd");
	strcpy(&(word[7][0]), "procedure");
	strcpy(&(word[8][0]), "read");
	strcpy(&(word[9][0]), "then");
	strcpy(&(word[10][0]), "var");
	strcpy(&(word[11][0]), "while");
	strcpy(&(word[12][0]), "write");
	/*设置保留字符号 */
	wsym[0] = beginsym;
	wsym[1] = callsym;
	wsym[2] = constsym;
	wsym[3] = dosym;
	wsym[4] = endsym;
	wsym[5] = ifsym;
	wsym[6] = oddsym;
	wsym[7] = procsym;
	wsym[8] = readsym;
	wsym[9] = thensym;
	wsym[10] = varsym;
	wsym[11] = whilesym;
	wsym[12] = writesym;
	/*设置符号集 */
	for (i = 0; i < symnum; i++)
	{
    
    
		declbegsys[i] = false;
		statbegsys[i] = false;
		facbegsys[i] = false;
	}
	/*设置声明开始符号集 */
	declbegsys[constsym] = true;
	declbegsys[varsym] = true;
	declbegsys[procsym] = true;
	/*设置语句开始符号集 */
	statbegsys[beginsym] = true;
	statbegsys[callsym] = true;
	statbegsys[ifsym] = true;
	statbegsys[whilesym] = true;
	/*设置因子开始符号集 */
	facbegsys[ident] = true;
	facbegsys[number] = true;
	facbegsys[lparen] = true;
}
//集合运算部分
int inset(int e, bool* s)
{
    
    
	return s[e];
}
int addset(bool* sr, bool* s1, bool* s2, int n)
{
    
    
	int i;
	for (i = 0; i < n; i++)
	{
    
    
		sr[i] = s1[i] || s2[i];
	}
	return 0;
}
int subset(bool* sr, bool* s1, bool* s2, int n)
{
    
    
	int i;
	for (i = 0; i < n; i++)
	{
    
    
		sr[i] = s1[i] && (!s2[i]);
	}
	return 0;
}
int mulset(bool* sr, bool* s1, bool* s2, int n)
{
    
    
	int i;
	for (i = 0; i < n; i++)
	{
    
    
		sr[i] = s1[i] && s2[i];
	}
	return 0;
}
//出错处理,打印出错位置和错误描述
void error(int n)
{
    
    
	char space[256];
	memset(space, 0, sizeof(space));
	space[cc - 1] = 0;// 出错时当前符号已经读完,所以 cc-1 
	switch (n)
	{
    
    
		case 1:
			sprintf(space,"Error type [%d] at Line [%d]: [变量%s在使用时未经定义].\n", n, lineIndex,id);
			break;
		case 2:
			sprintf(space,"Error type [%d] at Line [%d]: [函数%s在调用时未经定义].\n", n, lineIndex,id);
			break;
		case 3:
			sprintf(space,"Error type [%d] at Line [%d]: [变量%s出现重复定义,或变量与前面定义过的结构体名字重复].\n", n, lineIndex,id);
			break;
		case 4:
			sprintf(space,"Error type [%d] at Line [%d]: [过程%s出现重复定义].\n", n, lineIndex,id);
			break;
		case 5:
			sprintf(space,"Error type [%d] at Line [%d]: [赋值号两边的表达式类型不匹配].\n", n, lineIndex);
			break;
		case 6:
			sprintf(space,"Error type [%d] at Line [%d]: [操作数类型不匹配或操作数类型与操作符不匹配].\n", n, lineIndex);
			break;
		case 7:
			sprintf(space,"Error type [%d] at Line [%d]: [过程声明格式不正确(procedure后不为标识符)].\n", n, lineIndex);
			break;
		case 8:
			sprintf(space,"Error type [%d] at Line [%d]: [read或write函数的调用不完整(括号缺失)].\n", n, lineIndex);
			break;
		case 9:
			sprintf(space,"Error type [%d] at Line [%d]: [read函数的参数不是id].\n", n, lineIndex);
			break;
		case 10:
			sprintf(space,"Error type [%d] at Line [%d]: [调用函数格式非法(call后不为过程标识符)].\n", n, lineIndex);
			break;
		case 11:
			sprintf(space, "Error type [%d] at Line [%d]: [缺少赋值符号或赋值符号不正确].\n", n, lineIndex);//可能发生在语句定义、常变量定义与赋值时。
			break;
		case 12:
			sprintf(space, "Error type [%d] at Line [%d]: [一条语句定义(多个)常量或变量时漏掉了逗号或者分号/过程结束时漏了分号].\n", n, lineIndex);// 语句结束时或连续定义时缺少终止符号(end, ; )
			break;
		case 14:
			sprintf(space, "Error type [%d] at Line [%d]: [数字过长].\n", n, lineIndex);
			break;
		case 16:
			sprintf(space, "Error type [%d] at Line [%d]: [if后缺少then语句].\n", n, lineIndex);
			break;
		case 18:
			sprintf(space, "Error type [%d] at Line [%d]: [while语句缺少do].\n", n, lineIndex);
			break;
		case 19:
			sprintf(space, "Error type [%d] at Line [%d]: [地址超出上界].\n", n, lineIndex);
			break;
		case 20:
			sprintf(space, "Error type [%d] at Line [%d]: [逻辑表达式不合法(逻辑符号不合法)].\n", n, lineIndex);
			break;
		case 21:
			sprintf(space, "Error type [%d] at Line [%d]: [因子标识符为过程].\n", n, lineIndex);
			break;
		case 22:
			sprintf(space, "Error type [%d] at Line [%d]: [应输入),表达式不完整].\n", n, lineIndex);
			break;
		case 24:
			sprintf(space, "Error type [%d] at Line [%d]: [不以.结尾].\n", n, lineIndex);
			break;
		default:
			sprintf(space, "Error type [%d] at Line [%d]: [未知错误].\n", n, lineIndex);
			break;
	}
	printf("%s", space);
	err++;
}
//读取字符部分,先把字符存到缓冲区,然后进行读取,这里同时进行了行号的记录
int getch()
{
    
    
	if (cc == ll)
	{
    
    
		if (feof(fin)) //读到文件尾
		{
    
    
			printf("program incomplete");
			return -1;
		}
		ll = 0;
		cc = 0;
		printf("%d ", ++lineIndex);
		fprintf(fa1, "%d ", lineIndex);
		ch = ' ';
		while (ch != 10)
		{
    
    
			if (EOF == fscanf(fin, "%c", &ch))
			{
    
    
				line[ll] = 0;
				break;
			}
			printf("%c", ch);
			fprintf(fa1, "%c", ch);
			line[ll] = ch;
			ll++;
		}
	}
	ch = line[cc];
	cc++;
	return 0;
}
// 词法分析 获取一个词法单元
int getsym()
{
    
    
	int i, j, k;
	while (ch == ' ' || ch == 10 || ch == 9) //忽略空白部分
	{
    
    
		getchdo; 

	}
	if (ch >= 'a'&&ch <= 'z')
	{
    
    
		k = 0;
		do {
    
    
			if (k < al)
			{
    
    
				a[k] = ch;
				k++;
			}
			getchdo;
		} while (ch >= 'a'&&ch <= 'z' || ch >= '0'&&ch <= '9');
		a[k] = 0;
		strcpy(id, a);
		i = 0;
		j = norw - 1;
		do {
    
    
			k = (i + j) / 2;
			if (strcmp(id, word[k]) <= 0)
			{
    
    
				j = k - 1;
			}
			if (strcmp(id, word[k]) >= 0)
			{
    
    
				i = k + 1;
			}
		} while (i <= j);
		if (i - 1 > j)
		{
    
    
			sym = wsym[k];
		}
		else
		{
    
    
			sym = ident;
		}
	}
	else
	{
    
    
		if (ch >= '0'&&ch <= '9')
		{
    
    
			k = 0;
			num = 0;
			sym = number;
			do {
    
    
				num = 10 * num + ch - '0';
				k++;
				getchdo;
			} while (ch >= '0'&&ch <= '9'); /* 获取数字的值 */
			k--;
			if (k > nmax)
			{
    
    
				error(14);
			}
		}
		else
		{
    
    
			if (ch == ':') // 检测赋值符号 
			{
    
    
				getchdo;
				if (ch == '=')
				{
    
    
					sym = becomes;
					getchdo;
				}
				else
				{
    
    
					sym = nul; // 不能识别的符号 
				}
			}
			else
			{
    
    
				if (ch == '<') //检测小于或小于等于符号 
				{
    
    
					getchdo;
					if (ch == '=')
					{
    
    
						sym = leq;
						getchdo;
					}
					else
					{
    
    
						sym = lss;
					}
				}
				else
				{
    
    
					if (ch == '>') // 大于或大于等于
					{
    
    
						getchdo;
						if (ch == '=')
						{
    
    
							sym = geq;
							getchdo;
						}
						else
						{
    
    
							sym = gtr;
						}
					}
					else
					{
    
    
						sym = ssym[ch];// 按单字符号处理
						if (sym != period) {
    
    

							getchdo;
						}
					}
				}
			}
		}
	}
	return 0;
}


//在名字表中加入一项
void enter(enum object k, int *ptx, int lev, int *pdx)
{
    
    
	(*ptx)++;
	strcpy(table[(*ptx)].name, id); //全局变量 id 中已存有当前名字的名字
	table[(*ptx)].kind = k;
	switch (k)
	{
    
    
		case constant: 
			if (num >= 100000000000000)
			{
    
    
				error(19);
				num = 0;
			}
			table[(*ptx)].val = num;
			break;
		case variable: /* 变量名字 */
			table[(*ptx)].level = lev;
			table[(*ptx)].adr = (*pdx);
			(*pdx)++;
			break; /* 过程名字 */
		case procedur:
			table[(*ptx)].level = lev;
			break;
	}
}
//查找ident在表中的位置
int position(char * idt, int tx)
{
    
    
	int i;
	strcpy(table[0].name, idt);
	i = tx;
	while (strcmp(table[i].name, idt) != 0)
	{
    
    
		i--;
	}
	return i;
}
#pragma region 递归下降
//分程序
int block(int lev, int tx, bool* fsys)
{
    
    
	int i;
	int dx; /* 名字分配到的相对地址 */
	int tx0; /*保留初始 tx*/
	bool nxtlev[symnum]; /* 在下级函数的参数中,符号集合均为值参,但由于使用数组实现,传递进来的是指针, 为防止下级函数改变上级函数的集合,开辟新的空间传递给下级函数 */
	dx = 3;
	tx0 = tx; /* 记录本层名字的初始位置 */
	if (lev > levmax)
	{
    
    
		error(15);
	}
	do {
    
    
		if (sym == constsym) /* 收到常量声明符号,开始处理常量声明 */
		{
    
    
			getsymdo;
			do {
    
    
				constdeclarationdo(&tx, lev, &dx); /*dx 的值会被 constdeclaration 改变,使用指针 */
				while (sym == comma)
				{
    
    
					getsymdo;
					constdeclarationdo(&tx, lev, &dx);
				}
				if (sym == semicolon)
				{
    
    
					getsymdo;
				}
				else
				{
    
    
					error(12); /* 漏掉了逗号或者分号 */
				}
			} while (sym == ident);
		}
		if (sym == varsym)/* 收到变量声名符号,开始处理变量声名 */
		{
    
    
			getsymdo;
			do {
    
    
				vardeclarationdo(&tx, lev, &dx);
				while (sym == comma)
				{
    
    
					getsymdo;
					vardeclarationdo(&tx, lev, &dx);
				}
				if (sym == semicolon)
				{
    
    
					getsymdo;
				}
				else
				{
    
    
					error(12);
				}
			} while (sym == ident);
		}
		while (sym == procsym)/* 收到过程声名符号,开始处理过程声名 */
		{
    
    
			getsymdo;
			if (sym == ident)
			{
    
    
				int i = position(id, dx);
				if (i != 0)
				{
    
    
					if (table[i].kind == procedur)//过程重定义
						error(4);
				}
				enter(procedur, &tx, lev, &dx);/* 记录过程名字 */
				getsymdo;
			}
			else
			{
    
    
				error(7);/*procedure 后应为标识符 */
				getsymdo;
			}
			if (sym == semicolon)
			{
    
    
				getsymdo;
			}
			else
			{
    
    
				error(12);/* 漏掉了分号 */
			}
			memcpy(nxtlev, fsys, sizeof(bool)*symnum);
			nxtlev[semicolon] = true;
			if (-1 == block(lev + 1, tx, nxtlev))
			{
    
    
				return -1;/* 递归调用 */
			}
			if (sym == semicolon)
			{
    
    
				getsymdo;
				memcpy(nxtlev, statbegsys, sizeof(bool)*symnum);
				nxtlev[ident] = true;
				nxtlev[procsym] = true;
			}
			else
			{
    
    
				error(12); /* 漏掉了分号 */
			}
		}
		memcpy(nxtlev, statbegsys, sizeof(bool)*symnum);
		nxtlev[ident] = true;
		nxtlev[period] = true;
	} while (inset(sym, declbegsys)); /* 直到没有声明符号 */
	table[tx0].size = dx; /* 声明部分中每增加一条声明都会给dx 增加 1,声明部分已经结束 ,dx 就是当前过程数据的 size*/
	if (tableswitch) /* 输出名字表 */
	{
    
    
		printf("TABLE:\n");
		if (tx0 + 1 > tx)
		{
    
    
			printf("NULL\n");
		}
		for (i = tx0 + 1; i <= tx; i++)
		{
    
    
			switch (table[i].kind)
			{
    
    
			case constant:
				printf("%d const %s", i, table[i].name);
				printf("val=%d\n", table[i].val);
				fprintf(fas, "%d const %s", i, table[i].name);
				fprintf(fas, "val=%d\n", table[i].val);
				break;
			case variable:
				printf("%d var%s", i, table[i].name);
				printf("lev=%d addr=%d\n", table[i].level, table[i].adr);
				fprintf(fas, "%d var %s", i, table[i].name);
				fprintf(fas, "lev=%d addr=%d\n", table[i].level, table[i].adr);
				break;
			case procedur:
				printf("%d proc%s", i, table[i].name);
				printf("lev=%d addr=%d size = %d\n", table[i].level, table[i].adr, table[i].size);
				fprintf(fas, "%d proc%s", i, table[i].name);
				fprintf(fas, "lev=%d adr=%d size=%d \n", table[i].level, table[i].adr, table[i].size);
				break;
			}
		}
		printf("\n");
	}
	nxtlev[semicolon] = true;
	nxtlev[endsym] = true;
	statementdo(nxtlev, &tx, lev);
	return 0;
}

//常量声明处理
int constdeclaration(int * ptx, int lev, int * pdx)
{
    
    
	if (sym == ident)
	{
    
    
		if (position(id, *ptx) != 0) //重定义
			error(3);
		getsymdo;
		if (sym == eql || sym == becomes)//为=或:=
		{
    
    
			if (sym == becomes)//重复写赋值符号
			{
    
    
				error(11);
			}
			getsymdo;
			if (sym == number)//是常量说明,则向下
			{
    
    
				enter(constant, ptx, lev, pdx);
				getsymdo;
			}
			else
			{
    
    
				error(5); //常量声明后面不为数字,导致错误5
				getsymdo;
			}
		}
		else
		{
    
    
			error(11); /* 常量说明标识后应是 =*/
			getsymdo;
		}
	}
	else
	{
    
    
		error(8); /*const 后应是标识 */
	}
	return 0;
}
//变量定义
int vardeclaration(int * ptx, int lev, int * pdx)
{
    
    
	if (sym == ident)
	{
    
    
		if (position(id, *ptx) != 0) //重定义
			error(3);
		enter(variable, ptx, lev, pdx);// 填写名字表
		getsymdo;
	}
	else
		error(8);
	return 0;
}
//语句
int statement(bool* fsys, int * ptx, int lev)
{
    
    
	int i, cx1, cx2;
	bool nxtlev[symnum];
	if (sym == ident)
	{
    
    
		i = position(id, *ptx);
		if (i == 0)//未定义
		{
    
    
			error(1);
		}
		else if (table[i].kind != variable)
		{
    
    
			error(5);//赋值号两边的表达式类型不匹配(左部)
			i = 0;
		}
		getsymdo;
		if (sym == becomes)
		{
    
    
			getsymdo;
		}
		else
		{
    
    
			error(11);
		}
		memcpy(nxtlev, fsys, sizeof(bool)* symnum);
		expressiondo(nxtlev, ptx, lev);
	}
	else
	{
    
    
		if (sym == readsym)
		{
    
    
			getsymdo;
			if (sym != lparen)
			{
    
    
				error(8);//缺失左括号
			}
			else
			{
    
    

				getsymdo;
			}
			do {
    
    
				if (sym == ident)
				{
    
    
					i = position(id, *ptx);
					if (i == 0)
					{
    
    
						error(1);//使用变量时未定义
					}
					//else if(table[i].kind!=)
				}
				else
					error(9);//read函数的参数不为id
				getsymdo;
			} while (sym == comma); /* 一条 read 语句可读多个变量 */
			if (sym != rparen)
				error(8); //缺失右括号
			else
			{
    
    

				getsymdo;
			}
		}
		else
		{
    
    
			if (sym == writesym) /* 准备按照 write 语句处理,与 read 类似 */
			{
    
    
				getsymdo;
				if (sym != lparen) 
					error(8);//缺失左括号
				else {
    
    

					getsymdo;
				}
				do {
    
    
					memcpy(nxtlev, fsys, sizeof(bool)*symnum);
					nxtlev[rparen] = true;
					nxtlev[comma] = true; /* write 的后跟符号为) or,*/
					expressiondo(nxtlev, ptx, lev);/* 调用表达式处理,此处与 read 不同, read 为给变量赋值 */
				} while (sym == comma);
				if (sym != rparen)
					error(8);//缺失有括号
				else {
    
    

					getsymdo;
				}
			}
			else
			{
    
    
				if (sym == callsym) /* 准备按照 call 语句处理 */
				{
    
    
					getsymdo;
					if (sym != ident)
					{
    
    
						error(10); /*call 后应为标识符 */
						getsymdo;
					}
					else
					{
    
    
						i = position(id, *ptx);
						if (i == 0)
							error(2); /* 过程未找到 */
						else
						{
    
    
							if (table[i].kind != procedur)
								error(10); /*call 后标识符应为过程 */
							
						}
						getsymdo;
					}
				}
				else
				{
    
    
					if (sym == ifsym) /* 准备按照 if 语句处理 */
					{
    
    
						getsymdo;
						memcpy(nxtlev, fsys, sizeof(bool)*symnum);
						nxtlev[thensym] = true;
						nxtlev[dosym] = true; /* 后跟符号为 then 或 do*/
						conditiondo(nxtlev, ptx, lev); /*调用条件处理(逻辑运算)函数
						*/
						if (sym == thensym) {
    
    

							getsymdo;
						}
						else
						{
    
    
							error(16); /*缺少 then*/
							//getsymdo;
						}
						statementdo(fsys, ptx, lev); /* 处理 then 后的语句 */
					}
					else
					{
    
    
						if (sym == beginsym) /* 准备按照复合语句处理 */
						{
    
    
							getsymdo;
							memcpy(nxtlev, fsys, sizeof(bool)*symnum);
							nxtlev[semicolon] = true;
							nxtlev[endsym] = true;/* 后跟符号为分号或 end*/
							/* 循环调用语句处理函数, 直到下一个符号不是语句开始符号或收到 end*/
							statementdo(nxtlev, ptx, lev);
							while (inset(sym, statbegsys) || sym == semicolon)
							{
    
    
								if (sym == semicolon) 
								{
    
    

									getsymdo;
								}
								else
								{
    
    
									error(12);/* 缺少分号 */
									getsymdo;
								}
								statementdo(nxtlev, ptx, lev);
							}
							if (sym == endsym)
							{
    
    

								getsymdo;
							}
							else
							{
    
    
								error(12); /* 缺少 end 或分号 */
								getsymdo;
							}
						}
						else
						{
    
    
							if (sym == whilesym)/* 准备按照 while 语句处理 */
							{
    
    
								getsymdo;
								memcpy(nxtlev, fsys, sizeof(bool)*symnum);
								nxtlev[dosym] = true;/* 后跟符号为 do*/
								conditiondo(nxtlev, ptx, lev); /*调用条件处理 */
								if (sym == dosym)
								{
    
    
									getsymdo;
								}
								else
								{
    
    
									error(18); /* 缺少 do*/
									//return 0;
								}
								statementdo(fsys, ptx, lev); /* 循环体 */							
							}
						}
					}
				}
			}
		}
	}
	return 0;
}
//表达式
int expression(bool*fsys, int*ptx, int lev)
{
    
    
	enum symbol addop; /* 用于保存正负号 */
	bool nxtlev[symnum];
	if (sym == plus || sym == minus) /* 开头的正负号,此时当前表达式被看作一个正的或负的项 */
	{
    
    
		addop = sym; /* 保存开头的正负号 */
		getsymdo;
		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
		nxtlev[plus] = true;
		nxtlev[minus] = true;
		termdo(nxtlev, ptx, lev); /* 处理项 */
	}
	else /* 此时表达式被看作项的加减 */
	{
    
    
		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
		nxtlev[plus] = true;
		nxtlev[minus] = true;
		termdo(nxtlev, ptx, lev); /* 处理项 */
	}
	while (sym == plus || sym == minus)
	{
    
    
		addop = sym;
		getsymdo;
		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
		nxtlev[plus] = true;
		nxtlev[minus] = true;
		termdo(nxtlev, ptx, lev); /* 处理项 */
	}
	return 0;
}
//项
int term(bool*fsys, int *ptx, int lev)
{
    
    
	enum symbol mulop; /* 用于保存乘除法符号 */
	bool nxtlev[symnum];
	memcpy(nxtlev, fsys, sizeof(bool)*symnum);
	nxtlev[times] = true;
	nxtlev[slash] = true;
	factordo(nxtlev, ptx, lev); /* 处理因子 */
	while (sym == times || sym == slash)
	{
    
    
		mulop = sym;
		getsymdo;
		factordo(nxtlev, ptx, lev);
	}
	return 0;
}

//因子
int factor(bool*fsys, int *ptx, int lev)
{
    
    
	int i;
	bool nxtlev[symnum];
	if (sym == ident) /* 因子为常量或者变量 */
	{
    
    
		i = position(id, *ptx); /* 查找名字 */
		if (i == 0)
		{
    
    
			error(1); /* 标识符未声明 */
			//getsymdo;
			//break;
		}
		else if(table[i].kind==procedur)
			error(6); //因子为过程时,使得操作数不合法或表达式运算不合法,导致错误6:操作数类型不匹配或操作数类型与操作符不匹配
		getsymdo;
	}
	else if (sym == number) /* 因子为数*/
	{
    
    
		if (num >= 100000000000000)
		{
    
    
			error(14);
			num = 0;
		}
		getsymdo;
	}	
	else if (sym == lparen) /* 因子为表达式 */
	{
    
    
		getsymdo;
		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
		nxtlev[rparen] = true;
		expressiondo(nxtlev, ptx, lev);
		if (sym == rparen)
		{
    
    
			getsymdo;
		}
		else
		{
    
    
			error(22); /* 缺少右括号 */
			getsymdo;
		}
	}
	return 0;
}
//条件
int condition(bool* fsys, int* ptx, int lev)
{
    
    
	enum symbol relop;
	bool nxtlev[symnum];
	if (sym == oddsym) // 准备按照 odd 运算处理
	{
    
    
		getsymdo;
		expressiondo(fsys, ptx, lev);
	}
	else
	{
    
    
		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
		nxtlev[eql] = true;
		nxtlev[neq] = true;
		nxtlev[lss] = true;
		nxtlev[leq] = true;
		nxtlev[gtr] = true;
		nxtlev[geq] = true;
		expressiondo(nxtlev, ptx, lev);
		if (sym != eql && sym != neq && sym != lss && sym != leq && sym != gtr && sym != geq)
		{
    
    
			error(20);
		}
		else
		{
    
    
			relop = sym;
			getsymdo;
			expressiondo(fsys, ptx, lev);
		}
	}
	return 0;
}

#pragma endregion

4.3 递归下降子程序的声明

分程序:
int block(int lev, int tx, bool* fsys)
常量声明处理:
int constdeclaration(int * ptx, int lev, int * pdx)
变量定义:
int vardeclaration(int * ptx, int lev, int * pdx)
语句:
int statement(bool* fsys, int * ptx, int lev)
表达式:
int expression(bool*fsys, int*ptx, int lev)
项:
int term(bool*fsys, int *ptx, int lev)
因子:
int factor(bool*fsys, int *ptx, int lev)
条件:
int condition(bool* fsys, int* ptx, int lev)

5 常变量说明

变量:
在这里插入图片描述

常量:

enum symbol {}//包含32种类型符号的枚举
enum object { 	constant,	variable,	procedur};
//object 为三种标识符的类型
struct tablestruct{//名字表结构
	char name[al]; // 名字 
	enum object kind; // 类型仅三种
};

在这里插入图片描述

6 运行结果

测试的txt文件需要与debug的exe文件在同一目录
(1)测试一:对错误1、2、3、4进行测试。
测试代码t1.txt:

var m,n,q;
procedure gcd;
begin
	while r#0 do
	begin
		q := m / n;
		r := m - q * n;
		m := n;
		n := r;
	end;
end;
procedure gcd;
begin
end;
var m;
begin
	call gcd;
	call fun;
end.

在这里插入图片描述

(2)测试二:对错误5、6、7进行测试。
测试代码t2.txt:

var m,n,q;
const con = 5;
procedure var;
begin
end;
procedure fun;
const co = fun;
const con = 6;
begin
end;
begin
	m:=fun+2;
	fun:=1;
	m:=1;
	con:=2;
	con:=con;
	m:=con;	
end.

在这里插入图片描述
(3)测试三:对错误8、9、10进行测试。
测试代码t3.txt:

var m;
procedure fun;
begin
end;
begin
	read m;
	write(m;
	write(m);
	read(m);
	read(var);
	call fun;
	call var;
	call m;
end.

在这里插入图片描述
(4)测试四:对错误12、14、16、18、20进行测试。
测试代码t4.txt:

var m;
var a b;
const con=1234567891234567;
procedure fun;
begin
end;
begin
	if m=5
	then m:=m+1;
	if m=4
	m:=m-1;
	while m>=7
	do m:=m-1;
	while m=6
	m:=1+2;	
	if m-m
	then write(m);
end.

在这里插入图片描述
(5)测试五:对错误22、24进行测试。
测试代码t5.txt:

var m;
var a b;
const con=1234567891234567;
procedure fun;
begin
end;
begin
	m:=(6+8)/5+(4);
	m:=(5+7;
end

在这里插入图片描述
(6)测试六:无错误测试。
测试代码t6.txt:

var m, n, r, q;
procedure gcd;
begin
while r#0 do
begin
q := m / n;
r := m - q * n;
m := n;
n := r;
end;
end;
begin
read(m);
read(n);
if m < n then
begin
r := m;
m := n;
n := r;
end;
begin
r:=1;
call gcd;
write(m);
if odd r
then write(m)
end;
end.

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43399489/article/details/121524197