📄 pl0.c
字号:
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 + -