/* 目标代码生成过程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 */;
编译原理程序设计实践(七)解释器的相关代码
最新推荐文章于 2022-09-01 21:41:47 发布