编译原理程序设计实践(七)解释器的相关代码
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 */;
相关文章推荐
- 编译原理程序设计实践(三) 错误处理和词法分析代码
- 编译原理程序设计实践(五) 语法分析的相关函数
- 编译原理程序设计实践(六) 语句和子过程的处理代码
- 编译原理程序设计实践(八)— 主程序代码和makefile文件
- 编译原理程序设计实践(四)一些辅助函数
- 编译原理程序设计实践(九) 编译和测试
- 关于Basic程序解释器及编译原理的简单化(1)---Basic器的语法分析及主要代码
- 编译原理及实践教材TINY编译器代码解析
- 编译原理程序设计实践(一) 项目描述
- 编译原理程序设计实践(二) 全局变量定义
- 编译原理及实践教材TINY编译器代码解析
- 关于Basic程序解释器及编译原理的简单化(1)---Basic器的语法分析及主要代码
- 编译原理及实践 课本代码的使用说明
- C++程序设计原理与实践__记录1
- 少走弯路:学习编译原理的相关建议
- 编译原理课程设计_C--编译器_语法分析&代码生成 - Justin
- 少走弯路:学习编译原理的相关建议
- Maven编译代码的相关命令
- 编译原理相关书籍
- 编译原理实验,实现一个代码解析程序