⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pl0.c

📁 pl0
💻 C
📖 第 1 页 / 共 4 页
字号:

///////////////////////////////////////////////////////////////////////////////////////////
// 测试当前单词符号是否合法
void test(symset s1, symset s2, int n)
//s1:当语法分析进入或退出某一语法单元时当前单词符合应属于的集合 
// s2:在某一出错状态下,可恢复语法分析正常工作的补充单词集合 
// n:出错信息编号,当当前符号不属于合法的s1集合时发出的出错信息 
{
	symset s;
	if (! inset(sym, s1))//如果当前符号不在s1中
	{
		showset(s1);
		showset(s2);
		printf("sym=%d, id=%s\n", sym, id);
		error(n);//发出n号错误
		s = uniteset(s1, s2);// 把s2集合补充进s1集合
		while(! inset(sym, s))//通过循环找到下一个合法的符号,以恢复语法分析工作
			getsym();
		destroyset(s);
	}
} 


///////////////////////////////////////////////////////////////////////////////////////////
//分程序分析处理
void block(symset fsys)//fsys:用于出错恢复的单词集合
{
	int cx0; //记录本层开始时代码段分配位置
	mask* mk;
	int block_dx;//数据段内存分配指针,指向下一个被分配空间在数据段中的偏移位置
	int savedTx;//记录本层开始时符号表位置
	symset set1, set;
	dx = 3;//地址指示器给出每层局部量当前已分配到的相对位置。
    //置初始值为3的原因是:每一层最开始的位置有三个空间用于存放静态链SL、动态链DL和返回地址RA 
	block_dx = dx;
	mk = (mask*) &table[tx];
	mk->address = cx; //符号表当前位置记下当前层代码的开始位置
	gen(JMP, 0, 0);  //产生一行跳转指令,跳转位置暂时未知填0 
	if (level > MAXLEVEL)  //如果当前过程嵌套层数大于最大允许的套层数
		error(32); 
	do
	{//开始循环处理源程序中所有的声明部分
		if (sym == SYM_CONST)  //如果当前token是const保留字,开始进行常量声明
		{ 
			getsym();  //取下一个token,正常应为用作常量名的标识符
			do
			{  //反复进行常量声明
				constdeclaration(); //声明以当前token为标识符的常量
				while (sym == SYM_COMMA)  //如果遇到了逗号则反复声明下一个常量
				{
					getsym();  //获取下一个token,这里正好应该是标识符
					constdeclaration();  //声明以当前token为标识符的常量 
				}
				if (sym == SYM_SEMICOLON)//如果常量声明结束,应遇到分号
					getsym();  //获取下一个token,为下一轮循环做好准备 
				else
					error(5); //如果常量声明语句结束后没有遇到分号则发出5号错误
			}while (sym == SYM_IDENTIFIER);  //如果遇到非标识符,则常量声明结束
		} 
		if (sym == SYM_VAR)  //如果当前token是var保留字,开始进行变量声明,与常量声明类似
		{ 
			getsym();  // 获取下一个token,此处正常应为用作变量名的一个标识符
			do
			{  //反复进行变量声明
				vardeclaration();  //以当前token为标识符声明一个变量
				while (sym == SYM_COMMA)//如果遇到了逗号则反复声明下一个变量
				{ 
					getsym();  //获取下一个token,这里正好应该是标识符
					vardeclaration();  //声明以当前token为标识符的变量
				}
				if (sym == SYM_SEMICOLON)  //如果变量声明结束,应遇到分号
					getsym();  //获取下一个token,为下一轮循环做好准备
				else
					error(5); //如果变量声明语句结束后没有遇到分号则发出5号错误
			}while (sym == SYM_IDENTIFIER);  //如果遇到非标识符,则变量声明结束
			block_dx = dx;	
		}
		while (sym == SYM_PROCEDURE)  //循环声明各子过程
		{ 
			getsym();  //获取下一个token,此处正常应为作为过程名的标识符 
			if (sym == SYM_IDENTIFIER)  //如果token确为标识符
			{
				enter(ID_PROCEDURE);  //把这个过程登录到名字表中
				getsym();  //获取下一个token,正常情况应为分号
			}
			else
				error(4);
			if (sym == SYM_SEMICOLON) //如果当前token为分号
				getsym();  // 获取下一个token,准备进行语法分析的递归调用 
			else
				error(5); 
			level++;
			savedTx = tx;
			set1 = createset(SYM_SEMICOLON, SYM_NULL);
			set = uniteset(set1, fsys);
			block(set); //递归调用语法分析过程,当前层次加一,同时传递表头索引、合法单词符
			destroyset(set1);
			destroyset(set);
			tx = savedTx;
			level--;
			if (sym == SYM_SEMICOLON)  //递归返回后当前token应为递归调用时的最后一个end后的分号
			{
				getsym();  //获取下一个token 
				set1 = createset(SYM_IDENTIFIER, SYM_PROCEDURE, SYM_NULL);
				set = uniteset(statbegsys, set1);
				test(set, fsys, 6);  //检查当前token是否合法,不合法则用fsys恢复语法分析同时抛6号错
				destroyset(set1);
				destroyset(set);
			}
			else
				error(5); //如果过程声明后的符号不是分号,抛出5号错误
		}
		set1 = createset(SYM_IDENTIFIER, SYM_NULL);
		set = uniteset(statbegsys, set1);
		test(set, declbegsys, 7);  //检查当前状态是否合法,不合法则用声明开始符号作出错恢复、抛7号错 
		destroyset(set1);
		destroyset(set);
	}while (inset(sym, declbegsys));  //直到声明性的源程序分析完毕,继续向下执行,分析主程序
	code[mk->address].a = cx;  //把前面生成的跳转语句的跳转位置改成当前位置
	mk->address = cx;  //地址为当前代码分配地址
	cx0 = cx;  //记下当前代码分配位置
	gen(INT, 0, block_dx);  //生成分配空间指令,分配dx个空间
	set1 = createset(SYM_SEMICOLON, SYM_END, SYM_NULL);
	set = uniteset(set1, fsys);
	statement(set);  //处理当前遇到的语句或语句块 
	destroyset(set1);
	destroyset(set);
	gen(OPR, 0, 0); //生成从子程序返回操作指令
	test(fsys, phi, 8); // 用fsys检查当前状态是否合法,不合法则抛8号错
	listcode(cx0, cx);  //列出本层的类PCODE代码
} 


///////////////////////////////////////////////////////////////////////////////////////////
// 登录符号表过程 
void enter(int kind) //kind欲登录到符号表的符号类型
{
	int i;
	mask *mk;
	if ( position(id)> 0 )
		error(26); 
	tx++;//符号表指针指向一个新的空位
	strcpy(table[tx].name, id);//开始登录,name是符号的名字,对于标识符,这里就是标识符的名字
	table[tx].kind = kind;//符号类型,可能是常量0、变量1或过程名2
	switch (kind)//根据不同的类型进行不同的操作
	{
	case ID_CONSTANT://如果是常量名
		if (num > MAXADDRESS)//在常量的数值大于允许的最大值的情况下
		{
			error(30); 
			num = 0;  //实际登录的数字以0代替
		}      
		table[tx].value = num;  //如是合法的数值,就登录到符号表
		printf("符号表:   ");
		for(i=0;i<MAXIDLEN;i++)
			if(&table[tx].name[i]!=NULL)
				printf("%c",table[tx].name[i]);		
		printf("%d  %4d\n",table[tx].kind,table[tx].value);
		break;
	case ID_VARIABLE:  //如果是变量名
		mk = (mask*) &table[tx];
		mk->level = level;//记下它所属的层次号
		mk->address = dx++;//记下它在当前层中的偏移量,偏移量自增一,为下一次做好准备
		printf("符号表:   ");
		for(i=0;i<MAXIDLEN;i++)
			if(&table[tx].name[i]!=NULL)
				printf("%c",table[tx].name[i]); 
		printf("%d  %10d  %4d\n",mk->kind,mk->level,mk->address);
		break;
	case ID_PROCEDURE:  //如果要登录的是过程名
		mk = (mask*) &table[tx];
		mk->level = level;//记录下这个过程所在层次
		printf("符号表:   ");	
		for(i=0;i<MAXIDLEN;i++)
			if(&table[tx].name[i]!=NULL)
				printf("%c",table[tx].name[i]); 
		printf("%d  %10d  %4d\n",mk->kind,mk->level,mk->address);
		break;
	}
} //登录符号过程没有考虑到重复的定义的问题。如果出现重复定义,则以最后一次的定义为准。


///////////////////////////////////////////////////////////////////////////////////////////
// 在符号表中查找指定符号所在位置的函数
int position(char* id)  //id要找的符号,要找的符号在符号表中的位置,如果找不到就返回0
{
	int i;
	strcpy(table[0].name, id);  //先把id放入符号表0号位置 
	i = tx ;//从符号表中当前位置也即最后一个符号开始找
	while (strcmp(table[i].name, id) != 0)  //如果当前的符号与要找的不一致,找前面一个
		i--;
	return i;  //返回找到的位置号,如果没找到则一定正好为0
} 


///////////////////////////////////////////////////////////////////////////////////////////
//常量声明处理过程
void constdeclaration()
{
	if (sym == SYM_IDENTIFIER)  //常量声明过程开始遇到的第一个符号必然应为标识符
	{
		getsym();   //获取下一个token 
		if (sym == SYM_EQU || sym == SYM_BECOMES) //如果是等号或赋值号
		{
			if (sym == SYM_BECOMES)//如果是赋值号(常量生明中应该是等号)
				error(1);  //这里其实自动进行了错误纠正使编译继续进行,把赋值号当作等号处理 
			getsym(); //获取下一个token,等号或赋值号后应接上数字
			if (sym == SYM_NUMBER)  //如果的确是数字
			{
				enter(ID_CONSTANT);  //把这个常量登陆到符号表
				getsym();    //获取下一个token,为后面作准备
			}
			else
				error(2); // 如果等号后接的不是数字,抛出2号错误
		}
		else
			error(3); // 如果常量标识符后接的不是等号或赋值号,抛出3号错误
	}
	else		
		error(4); // 如果常量声明过程遇到的第一个符号不为标识符,抛出4号错误 
} 


///////////////////////////////////////////////////////////////////////////////////////////
//变量说明处理
void vardeclaration(void)
{
	if (sym == SYM_IDENTIFIER)//变量声明过程开始遇到的第一个符号必然应为标识符
	{
		enter(ID_VARIABLE);  //将标识符登陆到符号表中
		getsym();//获取下一个token,为后面作准备
	}
	else
		error(4); // 如果变量声明过程遇到的第一个符号不是标识符,抛出4号错误
} 


///////////////////////////////////////////////////////////////////////////////////////////
//列出当前一层类PCODE目标代码过程listcode 
void listcode(int from, int to)
{//省略用户选择是要列出代码的情况下才列出代码,默认直接列出代码
	int i;	
	printf("目标代码:\n");
	for (i = from; i < to; i++)
	{
		printf("%4d   %s\t%d\t%d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);
		fprintf(outfile, "%4d   %s\t%d\t%d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);
	}
} 


///////////////////////////////////////////////////////////////////////////////////////////
//语句部分分析处理
void statement(symset fsys)  //fsys如果出错可用来恢复语法分析的符号集合 
{
	int i, cx1, cx2, q;
	char ida[MAXIDLEN + 1];
	symset set1, set;
    mask* mk;
	if (sym == SYM_IDENTIFIER)  //所谓"语句"可能是赋值语句,以标识符开头
	{ 		
		if (! (i = position(id)))  //在符号表中没有查到该标识符所在位置
			error(11);
		else 
			if (table[i].kind != ID_VARIABLE)  //如果在符号表中找到该标识符,但该标识符不是变量名
			{
				error(12); 
				i = 0;  //i置0作为错误标志
			}
			getsym();  //获得下一个token,正常应为赋值号
			if (sym == SYM_BECOMES)  //如果的确为赋值号
				getsym();  //获取下一个token,正常应为一个表达式
			else
				error(13); 
			expression(fsys);  //处理表达式
			mk = (mask*) &table[i];
			if (i)  //如果不曾出错,i将不为0,i所指为当前语名左部标识符在符号表中的位置
				gen(STO, level - mk->level, mk->address);//产生一行把表达式值写往指定内存的sto目标代码
	}
	else 
		if (sym == SYM_CALL)  //如果是call语句
		{ 
			getsym(); //获取token,应是过程名型标识符
			if (sym != SYM_IDENTIFIER)  //如果call后跟的不是标识符
				error(14); 
			else//从符号表中找出相应的标识符
			{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -