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

📄 pl0.c

📁 pl0
💻 C
📖 第 1 页 / 共 4 页
字号:
	destroyset(set);
} 


///////////////////////////////////////////////////////////////////////////////////////////
//项分析处理
void term(symset fsys)//fsys如果出错可用来恢复语法分析的符号集合
{
	int mulop;
	symset set;	
	set = uniteset(fsys, createset(SYM_TIMES, SYM_SLASH, SYM_NULL));
	factor(set);  //每一个项都应该由因子开始,因此调用factor子程序分析因子
	while (sym == SYM_TIMES || sym == SYM_SLASH) //一个因子后应当遇到乘号或除号
	{
		mulop = sym;  //保存当前运算符
		getsym();  //获取下一个token 
		factor(set);  //运算符后应是一个因子,故调factor子程序分析因子
		if (mulop == SYM_TIMES) //如果刚才遇到乘号
			gen(OPR, 0, 4);  //生成乘法指令
		else
			gen(OPR, 0, 5);//不是乘号一定是除号,生成除法指令
	} 
	destroyset(set);
} 


///////////////////////////////////////////////////////////////////////////////////////////
//因子分析处理
void factor(symset fsys)//fsys如果出错可用来恢复语法分析的符号集合
{
	void expression();
	int i;
	symset set;	
	test(facbegsys, fsys, 24); // 开始因子处理前,先检查当前token是否在facbegsys集合中
   //如果不是合法的token,抛24号错误,并通过fsys集恢复使语法处理可以继续进行
	while (inset(sym, facbegsys))  //循环处理因子 
	{
		if (sym == SYM_IDENTIFIER)  //如果遇到的是标识符
		{//查符号表,找到当前标识符在符号表中的位置
			if ((i = position(id)) == 0)   //如果查符号表返回为0,表示没有找到标识符
				error(11); // Undeclared identifier.
			else
			{
				switch (table[i].kind)  //如果在符号表中找到了当前标识符的位置,开始生成相应代码
				{
					mask* mk;
				case ID_CONSTANT:
					gen(LIT, 0, table[i].value);  //如果这个标识符对应的是常量,值为val,生成lit指令,把val放到栈顶
					break;
				case ID_VARIABLE:
					mk = (mask*) &table[i];
					gen(LOD, level - mk->level, mk->address);//如果标识符是变量名,生成lod指令
					break;//把位于距离当前层level的层的偏移地址为adr的变量放到栈顶
				case ID_PROCEDURE:
					error(21); //如果在因子处理中遇到的标识符是过程名,出错了,抛21号错
					break;
				} 
			}
			getsym();//获取下一token,继续循环处理
		}
		else 
			if (sym == SYM_NUMBER)  //如果因子处理时遇到数字
			{
				if (num > MAXADDRESS)//如果数字的大小超过允许最大值amax
				{
					error(30); 
					num = 0;//把数字按0值处理
				}
				gen(LIT, 0, num);  //生成lit指令,把这个数值字面常量放到栈顶
				getsym();  //获取下一token 
			}
			else 
				if (sym == SYM_LPAREN)  // 如果遇到的是左括号
				{
					getsym();  //获取一个token 
					set = uniteset(createset(SYM_RPAREN, SYM_NULL), fsys);
					expression(set); //递归调用expression子程序分析一个子表达式
					destroyset(set);
					if (sym == SYM_RPAREN)//子表达式分析完后,应遇到右括号
						getsym();  //如果的确遇到右括号,读取下一个token
					else
						error(22); 
				}
				else
					test(fsys, createset(SYM_LPAREN, SYM_NULL), 23); //一个因子处理完毕,遇到的token应在fsys集合中
		//如果不是,抛23号错,并找到下一个因子的开始,使语法分析可以继续运行下去
	} 
} 


///////////////////////////////////////////////////////////////////////////////////////////
//条件分析处理 
void condition(symset fsys) //fsys如果出错可用来恢复语法分析的符号集合
{
	int relop;  //用于临时记录token的内容
	symset set;
	if (sym == SYM_ODD)  //如果是odd运算符(一元)
	{
		getsym();  //获取下一个token 
		expression(fsys);  //对odd的表达式进行处理计算
		gen(OPR, 0, 6);  //生成6号操作指令:奇偶判断运算
	}
	else  //如果不是odd运算符
	{
		set = uniteset(relset, fsys);
		expression(set);  //对表达式左部进行处理计算
		destroyset(set);
		if (! inset(sym, relset))  // 如果token不是逻辑运算符中的一个
			error(20);
		else
		{
			relop = sym;  //记录下当前的逻辑运算符
			getsym();  //获取下一个token 
			expression(fsys);  //对表达式右部进行处理计算 
			switch (relop)  //如果刚才的运算符是下面的一种
			{
			case SYM_EQU:  //等号:产生8号判等指令
				gen(OPR, 0, 8);
				break;
			case SYM_NEQ:  //不等号:产生9号判不等指令
				gen(OPR, 0, 9);
				break;
			case SYM_LSS: //小于号:产生10号判小指令
				gen(OPR, 0, 10);
				break;
			case SYM_GEQ: //大于等号号:产生11号判不小于指令 
				gen(OPR, 0, 11);
				break;
			case SYM_GTR:  //大于号:产生12号判大于指令
				gen(OPR, 0, 12);
				break;
			case SYM_LEQ:  //小于等于号:产生13号判不大于指令
				gen(OPR, 0, 13);
				break;
			} 
		} 
	} 
} 
			
///////////////////////////////////////////////////////////////////////////////////////////
//PL/0编译器产生的类PCODE目标代码解释运行过程
void interpret()
{
	int pc;        // pc为程序指令指针,指向下一条要运行的代码
	int stack[STACKSIZE];  //stack为栈式计算机的数据内存区
	int top;       //top为栈顶寄存器,类PCODE是在一种假想的栈式计算上运行的,这个变量记录这个计算机的当前栈顶位置
	int b;         // b为基址指针,指向每个过程被调用时数据区中分配给它的局部变量数据段基址
	instruction i; // i变量中存放当前在运行的指令
 
	printf("\n开始执行PL/0程序:\n");
	fprintf(outfile, "\n开始执行PL/0程序:\n");

	pc = 0;  //从0号代码开始执行程序
	b = 1; // 数据段基址为1
	top = 0;  //程序开始运行时栈顶寄存器置0 
	stack[1] = stack[2] = stack[3] = 0;  //数据内存中为SL,DL,RA三个单元均为0,标识为主程序
	do
	{  //开始依次运行程序目标代码
		i = code[pc];//获取一行目标代码,指令指针加一,指向下一条代码
		pc=pc+1;
		switch (i.f) //如果i的f,即指令助记符是下面的某种情况,执行不同的功能
		{
		case LIT:  //如果是lit指令
			stack[++top] = i.a; //该单元的内容存放i指令的a操作数,即实现了把常量值放到运行栈栈顶
			break;
		case OPR:  //如果是opr指令
			switch (i.a) // 根据a操作数不同,执行不同的操作
			{
			case 0: // 0号操作为从子过程返回操作
				top = b - 1;  //释放这段子过程占用的数据内存空间
				pc = stack[top + 3];  //把指令指针取到RA的值,指向的是返回地址
				b = stack[top + 2];  //把数据段基址取到DL的值,指向调用前子过程的数据段基址
				break;
			case 1:  //1号操作为栈顶数据取反操作 
				stack[top] = -stack[top];  //对栈顶数据进行取反
				break;
			case 2:  //2号操作为栈顶两个数据加法操作
				top--;  //栈顶指针下移
				stack[top] =stack[top] + stack[top + 1];  //把两单元数据相加存入栈顶
				break;
			case 3:  //3号操作为栈顶两个数据减法操作
				top--;  //栈顶指针下移
				stack[top] =stack[top] - stack[top + 1];  //把两单元数据相减存入栈顶
				break;
			case 4:  //4号操作为栈顶两个数据乘法操作
				top--;  //栈顶指针下移
				stack[top]=stack[top] * stack[top + 1];  //把两单元数据相乘存入栈顶
				break;
			case 5:  //5号操作为栈顶两个数据除法操作
				top--;  //栈顶指针下移
				if (stack[top + 1] == 0)
				{
					fprintf(stderr, "\n运行错误:被零除!\n");
					break;
				}
				stack[top]=stack[top] / stack[top + 1]; //把两单元数据相整除存入栈顶
				break;
			case 6:  //6号操作为判奇操作
				stack[top] =(stack[top] % 2);  //数据栈顶的值是奇数则把栈顶值置1,否则置0
				break;
			case 8:  //8号操作为栈顶两个数据判等操作
				top--;  //栈顶指针下移 
				stack[top] = (stack[top] == stack[top + 1]);  //判等,相等栈顶置1,不等置0
				break;
			case 9:  //9号操作为栈顶两个数据判不等操作 
				top--;  //栈顶指针下移
				stack[top] = (stack[top] != stack[top + 1]);  //判不等,不等栈顶置1,相等置0
				break;
			case 10:  //10号操作为栈顶两个数据判小于操作
				top--;  //栈顶指针下移
				stack[top] = (stack[top] < stack[top + 1]);  //判小于,如果下面的值小于上面的值,栈顶置1,否则置0
				break;
			case 11:  //11号操作为栈顶两个数据判大于等于操作
				top--;  //栈顶指针下移
				stack[top] = (stack[top] >= stack[top + 1]);  //判大于等于,如果下面的值大于等于上面的值,栈顶置1,否则置0
				break;
			case 12:  //12号操作为栈顶两个数据判大于操作
				top--;  //栈顶指针下移
				stack[top] = (stack[top] > stack[top + 1]);  //判大于,如果下面的值大于上面的值,栈顶置1,否则置0
				break;
			case 13:  //13号操作为栈顶两个数据判小于等于操作
				top--;  //栈顶指针下移
				stack[top] = (stack[top] <= stack[top + 1]);//判小于等于,如果下面的值小于等于上面的值,栈顶置1,否则置0
				break;
			} 
			break;
		case LOD:  // 如果是lod指令:将变量放到栈顶
			stack[++top] = stack[base(stack, b, i.l) + i.a]; 
            //栈顶上移,开辟空间,通过数据区层差l和偏移地址a找到变量的数据,存入上面开辟的新空间(即栈顶)
			break;
		case STO:  //如果是sto指令
			stack[base(stack, b, i.l) + i.a] = stack[top];  //把栈顶的值存入位置在数据区层差l偏移地址a的变量内存
			//printf("%d\n", stack[top]);
			fprintf(outfile, "%d\n", stack[top]);
			top--;  //栈项下移,释放空间
			break;
		case CAL:  //如果是cal指令
			stack[top + 1] = base(stack, b, i.l);  //在栈顶压入静态链SL 
			stack[top + 2] = b;  //然后压入当前数据区基址,作为动态链DL 
			stack[top + 3] = pc;  //最后压入当前的断点,作为返回地址RA 
			b = top + 1;  //把当前数据区基址指向SL所在位置 
			pc = i.a;  //从a所指位置开始继续执行指令,即实现了程序执行的跳转
			break;
		case INT:  //如果是int指令
			top += i.a;  //栈顶上移a个空间,即开辟a个新的内存单元 
			break;
		case JMP:  //如果是jmp指令
			pc = i.a;  //把jmp指令操作数a的值作为下一次要执行的指令地址,实现无条件跳转
			break;
		case JPC:  //如果是jpc指令
			if (stack[top] == 0)//判断栈顶值
				pc = i.a;  //如果是0就跳转,否则不跳转
			top--;  //释放栈顶空间
			break;
		case RED://如果是red指令
			//把栈顶的值存入位置在数据区层差l偏移地址a的变量内存
			printf("输入:\n");
			scanf("%d",&stack[base(stack, b, i.l) + i.a]);
			break;
		case WRT://如果是wrt指令
			printf("输出:%d\n", stack[top]);
			fprintf(outfile, "输出:%d\n", stack[top]);
			top++;//栈顶上移,开辟空间
			break;
		} 
	}while (pc!=0);
	printf("PL/0程序运行结束!\n");
	fprintf(outfile, "PL/0程序运行结束!\n");
} 


///////////////////////////////////////////////////////////////////////////////////////////
//通过静态链求出数据区基地址的函数
int base(int stack[], int currentLevel, int levelDiff)  //levelDiff要求的数据区所在层与当前层的层差
{
	int b = currentLevel;	
	while (levelDiff>0)
	{
		b = stack[b];
		levelDiff--;
	}
	return b;//把找到的要求的数据区基址返回
} 


⌨️ 快捷键说明

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