您的位置:首页 > 编程语言

编译原理程序设计实践(七)解释器的相关代码

2013-03-13 21:54 453 查看
/* 目标代码生成过程gen */
/* 参数:x:要生成的一行代码的助记符 */
/*       y, z:代码的两个操作数 */
/* 本过程用于把生成的目标代码写入目标代码数组,供后面的解释器解释执行 */
void gen(fct x,int y, int z)
{
	if  (cx > cxmax) /* 如果cx>cxmax表示当前生成的代码行号大于允许的最大代码行数 */
	{
		cout<<"program too long"; /* 输出"程序太长",退出 */
		throw(99);
	}
	code[cx].f = x ;
	code[cx].l = y ;
	code[cx].a = z ;
	
	++cx; /* 移动cx指针指向下一个空位 */
}  /* gen */
/* 列出当前一层类PCODE目标代码过程listcode */ 
void listcode(const int& cx0)
{ /* list code generated for this block */
	if  (listswitch) /* 如果用户选择是要列出代码的情况下才列出代码 */
	{
		for (int i = cx0; i<=cx - 1;++i) /* 从当前层代码开始位置到当前代码位置-1处,即为本分程序块 */
		{
			cout << left << setw(4)<<i;
			fa	<< left <<setw(4)<<i;
			for(int j=0;j<5;++j)
			{
				cout<< mnemonic[code[i].f][j];
				fa<< mnemonic[code[i].f][j];
			}
			cout << left << setw(3) << code[i].l
				 << left << setw(5) << code[i].a 	
				 << endl
				; /* 显示出第i行代码的助记符和L与A操作数 */
			fa	<< left << setw(3) << code[i].l
				<< left << setw(5) << code[i].a 	
				<< endl
				;

			 /* 同时把屏显打印到文件 */
		}
	}
} /* listcode */
/* 通过静态链求出数据区基地址的函数base */
/* 参数说明:l:要求的数据区所在层与当前层的层差 */
/* 返回值:要求的数据区基址 */
int base(int l,int b,const int s[])
{
	int b1;
	b1 = b ; /* find base 1 level down */ /* 首先从当前层开始 */
	while(l > 0 ) /* 如果l大于0,循环通过静态链往前找需要的数据区基址 */
	{
		b1 = s[b1] ; /* 用当前层数据区基址中的内容(正好是静态链SL数据,为上一层的基址)的作为新的当前层,即向上找了一层 */
		l = l - 1; /* 向上了一层,l减一 */
	}
	return  b1; /* 把找到的要求的数据区基址返回 */
} /* base */;

/* PL/0编译器产生的类PCODE目标代码解释运行过程interpret */
void interpret(void)
{
	const int stacksize = 500; /* 常量定义,假想的栈式计算机有500个栈单元 */
	int p, b, t; /* program base topstack registers */ 
	/* p为程序指令指针,指向下一条要运行的代码 */
	/* b为基址指针,指向每个过程被调用时数据区中分配给它的局部变量数据段基址 */
	/* t为栈顶寄存器,类PCODE是在一种假想的栈式计算上运行的,这个变量记录这个计算机的当前栈顶位置 */
	instruction i; /* i变量中存放当前在运行的指令 */
	int s[stacksize]; /* datastore */ /* s为栈式计算机的数据内存区 */

	cout<<"start pl0"<<endl; /* PL/0程序开始运行 */
	t = 0 ; /* 程序开始运行时栈顶寄存器置0 */
	b = 0 ; /* 数据段基址为0 */
	p = 0 ; /* 从0号代码开始执行程序 */
	s[0] = 0 ; 
	s[1] = 0 ;
	s[2] = 0 ; /* 数据内存中为SL,DL,RA三个单元均为0,标识为主程序 */
	do 
	{ /* 开始依次运行程序目标代码 */
		i = code[p] ; /* 获取一行目标代码 */
		++p ; /* 指令指针加一,指向下一条代码 */

		switch (i.f) /* 如果i的f,即指令助记符是下面的某种情况,执行不同的功能 */
		{
		case lit: /* 如果是lit指令 */
			++t ; /* 栈顶指针上移,在栈中分配了一个单元 */
			s[t] = i.a; /* 该单元的内容存放i指令的a操作数,即实现了把常量值放到运行栈栈顶 */
			break;

		case opr: /* 如果是opr指令 */
			switch (i.a) /* operator */ /* 根据a操作数不同,执行不同的操作 */
			{
			case 0: /* 0号操作为从子过程返回操作 */
				/* return */
				t = b - 1 ; /* 释放这段子过程占用的数据内存空间 */
				p = s[t + 3] ; /* 把指令指针取到RA的值,指向的是返回地址 */
				b = s[t + 2]; /* 把数据段基址取到DL的值,指向调用前子过程的数据段基址 */
				break;
			case 1: /* 1号操作为栈顶数据取反操作 */
				s[t] = -s[t] ; /* 对栈顶数据进行取反 */
				break;
			case 2: /* 2号操作为栈顶两个数据加法操作 */
				t = t - 1 ; /* 栈顶指针下移 */ 
				s[t] = s[t] + s[t + 1]; /* 把两单元数据相加存入栈顶 */
				break;
			case 3: /* 3号操作为栈顶两个数据减法操作 */
				t = t - 1 ; /* 栈顶指针下移 */ 
				s[t] = s[t] - s[t + 1]; /* 把两单元数据相减存入栈顶 */
				break;
			case 4: /* 4号操作为栈顶两个数据乘法操作 */
				t = t - 1 ; /* 栈顶指针下移 */
				s[t] = s[t] * s[t + 1]; /* 把两单元数据相乘存入栈顶 */
				break;
			case  5: /* 5号操作为栈顶两个数据除法操作 */
				t = t - 1 ; /* 栈顶指针下移 */
				s[t] = s[t] / s[t + 1]; /* 把两单元数据相整除存入栈顶 */
				break;
			case 6: /* 6号操作为判奇操作 */
				s[t] = s[t]%2 ; /* 数据栈顶的值是奇数则把栈顶值置1,否则置0 */
				break;
			case 8: /* 8号操作为栈顶两个数据判等操作 */
				t = t - 1 ; /* 栈顶指针下移 */
				s[t] = (s[t] == s[t + 1]); /* 判等,相等栈顶置1,不等置0 */
					break;
			case 9: /* 9号操作为栈顶两个数据判不等操作 */
				t = t - 1 ; /* 栈顶指针下移 */
				s[t] = (s[t] != s[t + 1]); /* 判不等,不等栈顶置1,相等置0 */
					break;
			case 10: /* 10号操作为栈顶两个数据判小于操作 */
				t = t - 1 ; /* 栈顶指针下移 */
				s[t] = (s[t] < s[t + 1]); /* 判小于,如果下面的值小于上面的值,栈顶置1,否则置0 */
					break;
			case 11: /* 11号操作为栈顶两个数据判大于等于操作 */
				t = t - 1 ; /* 栈顶指针下移 */
				s[t] = (s[t] >= s[t + 1]); /* 判大于等于,如果下面的值大于等于上面的值,栈顶置1,否则置0 */
					break;
			case 12: /* 12号操作为栈顶两个数据判大于操作 */
				t = t - 1 ; /* 栈顶指针下移 */
				s[t] = (s[t] > s[t + 1]); /* 判大于,如果下面的值大于上面的值,栈顶置1,否则置0 */
					break;
			case 13: /* 13号操作为栈顶两个数据判小于等于操作 */
				t = t - 1 ; /* 栈顶指针下移 */
				s[t] = ord(s[t] <= s[t + 1]); /* 判小于等于,如果下面的值小于等于上面的值,栈顶置1,否则置0 */
					break;
			case   14: /* 14号操作为输出栈顶值操作 */
				cout<<(s[t]); /* 输出栈顶值 */
				fa2<<s[t]; /* 同时打印到文件 */
				t = t - 1; /* 栈顶下移 */
					break;
			case 15: /* 15号操作为输出换行操作 */
				cout<<endl; /* 输出换行 */
				fa2<<endl; /* 同时输出到文件 */
				break;
			case 16: /* 16号操作是接受键盘值输入到栈顶 */
				++t ; /* 栈顶上移,分配空间 */
				cout << '?'; /* 屏显问号 */
				fa2 << '?'; /* 同时输出到文件 */
				cin >> s[t]; /* 获得输入 */
				fa2 << s[t]<<endl;; /* 把用户输入值打印到文件 */
				break;
			} /* opr指令分析运行结束 */
			break;
		case lod: /* 如果是lod指令:将变量放到栈顶 */
			++t ; /* 栈顶上移,开辟空间 */
			s[t] = s[base(i.l,b,s) + i.a]; /* 通过数据区层差l和偏移地址a找到变量的数据,存入上面开辟的新空间(即栈顶) */
			break;

		case sto: /* 如果是sto指令 */
			s[base(i.l,b,s) + i.a] = s[t] ; /* 把栈顶的值存入位置在数据区层差l偏移地址a的变量内存 */
			t = t - 1; /* 栈项下移,释放空间 */
			break;
		case cal: /* 如果是cal指令 */
			/* generat new block mark */
			s[t + 1] = base(i.l,b,s) ; /* 在栈顶压入静态链SL */
			s[t + 2] = b ; /* 然后压入当前数据区基址,作为动态链DL */
			s[t + 3] = p ; /* 最后压入当前的断点,作为返回地址RA */
			/* 以上的工作即为过程调用前的保护现场 */
			b = t + 1 ; /* 把当前数据区基址指向SL所在位置 */
			p = i.a ; /* 从a所指位置开始继续执行指令,即实现了程序执行的跳转 */
			break;
		case _int: /* 如果是int指令 */
			t = t + i.a ; /* 栈顶上移a个空间,即开辟a个新的内存单元 */
			break;
		case jmp: /* 如果是jmp指令 */
			p = i.a ; /* 把jmp指令操作数a的值作为下一次要执行的指令地址,实现无条件跳转 */
			break;
		case jpc: /* 如果是jpc指令 */
			if  (s[t] == 0) /* 判断栈顶值 */
				p = i.a ; /* 如果是0就跳转,否则不跳转 */
			t = t- 1; /* 释放栈顶空间 */
			break;

		}
	}while (p != 0); /* 如果p等于0,意味着在主程序运行时遇到了从子程序返回指令,也就是整个程序运行的结束 */
	fa2.close( ); /* 关闭用于记录屏幕输入输出的fa2文件 */
	/* PCODE代码的解释执行过程结束 */
}/* interpret */;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: