1 扩充功能的总体概述
增加处理更多语法成分的功能:一维数组,++,+=,–,-=,*=,/=,<>,for-to,repeat-util,if-then-else,floor向下取整,ceil向上取整,pow2求二次方,pow3求三次方,float类型,出错展示等。
1、修改PL/0编译程序的头文件pl0.h:
(1)关键字的个数norw扩展到24,其中新增已经实现else, for, repeat, to, until, array,float,floor,ceil,pow2,pow3;
(2)在enum symbol中新加入共19个单词种别(numberf,elsesym,forsym,tosym,repeatsym,untilsym,twoplus,pluseql,twominus,minuseql,timeseql,slasheql,lbrack,rbrack,flosym,floorsym,ceilsym,pow2sym,pow3sym),同时将symnum改为51。
(3)在名字表中的类型enum object中加入array,flo。
(4)在虚拟机代码中新增STA,LDA,LTF用于一维数组,同时将fctnum改为11。
2、修改PL/0编译程序的源文件pl0.c初始化部分:
(1)设置新增单字符符号
ssym['['] = lbrack;
ssym[']'] = rbrack;
(2)重新设置保留字名字,并按照字母顺序重新排列,便于折半查找
strcpy(&(word[0][0]), "begin");
strcpy(&(word[1][0]), "call");
strcpy(&(word[2][0]), "ceil");//新增ceil
strcpy(&(word[3][0]), "const");
strcpy(&(word[4][0]), "do");
strcpy(&(word[5][0]), "else");//新增else
strcpy(&(word[6][0]), "end");
strcpy(&(word[7][0]), "float");//新增float
strcpy(&(word[8][0]), "floor");//新增floor
strcpy(&(word[9][0]), "for");//新增for
strcpy(&(word[10][0]), "if");
strcpy(&(word[11][0]), "odd");
strcpy(&(word[12][0]), "pow2");//新增pow2
strcpy(&(word[13][0]), "pow3");//新增pow3
strcpy(&(word[14][0]), "procedure");
strcpy(&(word[15][0]), "read");
strcpy(&(word[16][0]), "repeat");//增加repeat
strcpy(&(word[17][0]), "then");
strcpy(&(word[18][0]), "to");//新增to
strcpy(&(word[19][0]), "until");//新增until
strcpy(&(word[20][0]), "var");
strcpy(&(word[21][0]), "while");
strcpy(&(word[22][0]), "write");
(3)设置新增保留字符号
wsym[0] = beginsym;
wsym[1] = callsym;
wsym[2] = ceilsym;
wsym[3] = constsym;
wsym[4] = dosym;
wsym[5] = elsesym;
wsym[6] = endsym;
wsym[7] = flosym;
wsym[8] = floorsym;
wsym[9] = forsym;
wsym[10] = ifsym;
wsym[11] = oddsym;
wsym[12] = pow2sym;
wsym[13] = pow3sym;
wsym[14] = procsym;
wsym[15] = readsym;
wsym[16] = repeatsym;
wsym[17] = thensym;
wsym[18] = tosym;
wsym[19] = untilsym;
wsym[20] = varsym;
wsym[21] = whilesym;
wsym[22] = writesym;
(4)设置新增指令名称
strcpy(&(mnemonic[sta][0]), "sta");
strcpy(&(mnemonic[lda][0]), "lda");
strcpy(&(mnemonic[ltf][0]), " ltf");
(5)设置声明开始符号集
declbegsys[flosym] = true;
(6)设置语句开始符号集
statbegsys[pow2sym] = true;
statbegsys[pow3sym] = true;
statbegsys[forsym] = true;//新增for
statbegsys[repeatsym] = true;//新增repeat
(7)设置因子开始符号集
facbegsys[floorsym] = true;
facbegsys[ceilsym] = true;
facbegsys[pow2sym] = true;
facbegsys[pow3sym] = true;
facbegsys[twoplus] = true;//新增自增
facbegsys[twominus] = true;//新增自减
2 if-then-else功能新增
在处理完条件语句后,有一个有条件跳转JPC,当条件为假时,跳过then语句跳转到语句2,当条件为真时,顺序执行。在语句1后面有一个无条件JMP跳转,顺序执行完后跳过else语句。
在语句处理statement函数添加if-then-else功能代码:
- if (sym == ifsym) /* 准备按照if语句处理 */
- {
- getsymdo;
- memcpy(nxtlev, fsys, sizeof(bool) * symnum);
- nxtlev[thensym] = true;
- nxtlev[dosym] = true; /* 后跟符号为then或do */
- conditiondo(nxtlev, ptx, lev); /* 调用条件处理(逻辑运算)函数 */
- if (sym == thensym) {
- getsymdo;
- } else {
- error(16); /* 缺少then */
- }
- cx1 = cx; /* 保存当前指令地址 */
- gendo(jpc, 0, 0); /* 生成条件跳转指令,跳转地址未知,暂时写0 */
- statementdo(fsys, ptx, lev); /* 处理then后的语句 */
- if(sym==elsesym)/*如果取到else*/
- {
- getsymdo;
- cx2=cx; /*保存当前指令地址*/
- gendo(jmp,0,0); /*生成无条件跳转指令,跳转地址暂时写0*/
- code[cx1].a=cx; /*回填条件跳转指令,
- 条件为假时跳到else的语句块执行*/
- statementdo(fsys,ptx,lev); /*执行else的语句块*/
- code[cx2].a=cx; /*回填无条件跳转指令,
- 执行了then语句块之后跳过else语句块*/
- }else{
- code[cx1].a = cx; /* 经statement处理后,cx为then后语句 执行完的位置,它正是前面未定的跳转地址 */
- }
- }
3 repeat-until功能新增
repeat语句系列的扩充仿照while-do语句,但repeat语句系列没有无条件跳转,while-do语句有。程序按顺序执行,在条件语句后存在一个条件跳转指令,跳转地址未定,当条件判断为假时回到repeat后面的循环开始位置,所以循环体语句开始位置要保存记录,当条件为真时,语句结束。
在语句处理statement函数添加repeat-until功能代码:
- if(sym==repeatsym){
- cx1=cx;//记录循环体开始位置
- getsymdo;//根据词法规则识别及组合单词,进行词法检查
- memcpy(nxtlev, fsys, sizeof(bool)*symnum);//内存拷贝函数
- nxtlev[untilsym] = true; //后跟符号为until
- statementdo(nxtlev, ptx, lev);//处理循环体语句
- if(sym == untilsym)
- {
- getsymdo;
- conditiondo(nxtlev, ptx, lev); /* 调用条件处理 */
- gendo(jpc,0,cx1);
- //生成条件跳转指令 ,不满足条件时则跳转至循环开始位置
- }
- else
- {
- error(19);//缺少until
- }
- }
4 for-to功能新增
语言成分:for 变量 = 初值 to 终值 BEGIN 语句
在语句处理statement函数添加for-to功能代码:
if(sym==forsym){ /*检测到for语句*/
getsymdo;
if(sym==ident)
{
i=position(id,*ptx);
if(i==0){
error(11); /*变量未找到*/
}
else
{
if(table[i].kind!=variable)
//赋值语句中,赋值号左部标识符属性应是变量
{
error(12);
i=0;
}
else
{
getsymdo;
if(sym=becomes){//赋值语句左部标识符后应是赋值号:=
getsymdo;
}else{
error(13);
}
memcpy(nxtlev,fsys,sizeof(bool)*symnum);
expressiondo(nxtlev,ptx,lev);
//处理赋值语句右部的表达式E1
if(i != 0)
{
gendo(sto, lev-table[i].level, table[i].adr);
cx1=cx;//保存循环开始位置
if(sym == tosym)
{
getsymdo;
gendo(lod,lev-table[i].level,table[i].adr);
//将循环判断变量取出放到栈顶
memcpy(nxtlev,fsys,sizeof(bool)*symnum);
//处理to后表达式
nxtlev[dosym]=true;//后跟符号为to
expressiondo(nxtlev,ptx,lev);//判断循环变量条件
gendo(opr,0,13); //生成比较指令
cx2=cx;//保存循环结束位置
gendo(jpc,0,0);//条件跳转指令,跳出循环的地址未知
if(sym==dosym)//处理循环体
{
getsymdo;
statement(fsys,ptx,lev);
gendo(lod,lev-table[i].level,table[i].adr);
//将循环变量取出放在栈顶
gendo(lit,0,1);//将步长取到栈顶
gendo(opr,0,2);//循环变量加步长
gendo(sto,lev-table[i].level,table[i].adr);
//将栈顶的值存入循环变量
gendo(jmp,0,cx1);//无条件跳转到循环开始位置
code[cx2].a=cx; //地址回填
}
}
else
{
error(29); //for语句中少了do
}
}
}
}
}
else
{
error(19); //for语句后跟赋值语句,赋值语句左部是变量,缺少变量
}
}
5 <>表示不等号功能新增
在词法分析中的GETSYM函数中修改添加代码如下:
- if (ch == '<') /* 检测小于或小于等于符号 */
- {
- getchdo;
- if (ch == '=') {
- sym = leq;
- getchdo;
- } else{
- if(ch=='>'){ /*检测不等于号*/
- sym=neq;
- getchdo;
- }else{
- sym=lss;
- }
- }
- }
6 += ++ -= – *= /=功能新增
在词法分析中的GETSYM函数中添加代码如下(这里以+=和++为例):
- else if(ch == '+'){
- getchdo;
- if(ch == '='){ /* '+'后面是'='就把pluseql赋给sym */
- sym = pluseql;
- getchdo;
- }else if (ch == '+'){ /* '+'后面是'+'就把twoplus赋给sym */
- sym = twoplus;
- getchdo;
- }else{
- sym = plus;
- }
- }
在语句处理statement函数修改添加+= ++ -= – *= /=功能代码(以+=为例):
else{
getsymdo;
if(sym == becomes)
{
getsymdo;
memcpy(nxtlev,fsys,sizeof(bool)*symnum);
expressiondo(nxtlev,ptx,lev);
if(i!=0)
{
gendo(sto,lev-table[i].level,table[i].adr);
}
}else if(sym==pluseql){ /* 新增+= */
getsymdo;
memcpy(nxtlev,fsys,sizeof(bool)* symnum);
expressiondo(nxtlev,ptx,lev);
if(i!=0)
{
gendo(lod,lev-table[i].level,table[i].adr);
gendo(opr,0,2);
gendo(sto,lev-table[i].level,table[i].adr);
}
}
7 一维数组功能新增
对于一维数组,要将[ ]加入符号集,在输出名字表中新增:
case array:
printf(" %d array %s ", i, table[i].name);
printf("kind = %s ", word[table[i].val]);
printf("lev = %d addr = %d size = %d\n", table[i].level, table[i].adr, table[i].size);
fprintf(fas, " %d array %s ", i, table[i].name);
fprintf(fas, "kind = %s ", word[table[i].val]);
fprintf(fas, "lev = %d addr = %d\n", table[i].level, table[i].adr);
break;
登录名字表过程中新增一个enterArray:
void enterArray(int* ptx, int lev, int* pdx, int size, int val_kind)
{
(*ptx)++;
strcpy(table[(*ptx)].name, id);
table[(*ptx)].kind = array; // 数组类型
table[(*ptx)].level = lev; // 层次
table[(*ptx)].adr = (*pdx); // 数组首地址
table[(*ptx)].size = size; // 数组长度
table[(*ptx)].val = val_kind; // 元素的数据类型
(*pdx) += size;
}
在语句处理中涉及变量的位置上增加判断数组:
if (sym == ident) /* 准备按照赋值语句处理 */
{
i = position(id, *ptx);
if (i == 0) {
error(11); /* 变量未找到 */
} else {
if (table[i].kind == array) {
getsymdo;
if (sym == lbrack) {
getsymdo;
} else
error(40);
memcpy(nxtlev, fsys, sizeof(bool) * symnum);
expressiondo(nxtlev, ptx, lev); /* 计算下标 */
if (sym != rbrack)
error(39);
} else if (table[i].kind != variable && table[i].kind != flo) {
error(12); /* 赋值语句格式错误 */
i = 0;
}
getsymdo;
}
扩充指令LDA、STA:
case lda: /* 新增,加载数组元素 */
s[t - 1] = s[base(i.l, s, b) + i.a + (int)s[t - 1]];
break;
case sta: /* 新增,存储数组元素 */
t--;
s[base(i.l, s, b) + i.a + (int)s[t - 1]] = s[t];
break;
8 平方立方功能新增
对于二次方和三次方,需要在起始符号集中将其设置为真,在语句处理中添加相关运算,还要对OPR指令进行扩充。
if (sym == pow2sym || sym == pow3sym) {
int op = sym;
getsymdo;
if (sym != lparen) {
error(34); /* 格式错误,应是左括号 */
} else {
getsymdo;
if (sym == ident) {
int i = position(id, *ptx);
if (i == 0) {
error(11); /* 变量未找到 */
}
factordo(nxtlev, ptx, lev);
if (op == pow2sym){
gendo(opr, 0, 21);
}else if(op == pow3sym){
gendo(opr, 0, 22);
}
if (table[i].kind == array) {
gendo(sta, lev - table[i].level, table[i].adr);
} else if (i != 0) {
gendo(sto, lev - table[i].level, table[i].adr);
}
}
}
//对于OPR中指令扩充
case 21: // 新增,二次方
s[t - 1] = s[t - 1] * s[t - 1];
break;
case 22: // 新增,三次方
s[t - 1] = s[t - 1] * s[t - 1] * s[t - 1];
break;
9 浮点数功能新增
在block()<分程序>中新增接收浮点数声明符号:
if (sym == flosym) /* 收到浮点数声明符号,开始处理浮点数声明 */
{
getsymdo;
flodeclarationdo(&tx, lev, &dx);
while (sym == comma) {
getsymdo;
flodeclarationdo(&tx, lev, &dx);
}
if (sym == semicolon) {
getsymdo;
} else {
error(5);
}
}
输出名字表中新增:
case flo:
printf(" %d float %s ", i, table[i].name);
printf("lev = %d addr = %d\n", table[i].level, table[i].adr);
fprintf(fas, " %d float %s ", i, table[i].name);
fprintf(fas, "lev = %d addr = %d\n", table[i].level, table[i].adr);
break; 登录名字表过程enter中新增:
case flo: /* 浮点数名字 */
table[(*ptx)].level = lev;
table[(*ptx)].adr = (*pdx);
printf("name = %s , lev = %d\n", id, table[(*ptx)].level);
(*pdx)++;
break; 浮点数声明处理:
int flodeclaration(int* ptx, int lev, int* pdx)
{
if (sym == ident) {
getsymdo;
if (sym == lbrack) {
getsymdo;
if (sym == number) {
getsymdo;
enterArray(ptx, lev, pdx, num, 6);
} else
error(25);
if (sym == rbrack) {
getsymdo;
} else
error(39);
} else
enter(flo, ptx, lev, pdx); // 填写名字表
} else {
error(4); /* var后应是标识 */
}
return 0;
}
10 向上向下取整功能新增
//对于向上向下取整,需要在起始因子中将其设置为真
//在因子处理中添加相关运算,还要对OPR指令进行扩充
else if (sym == floorsym) { /* 新增,向下取整 */
getsymdo;
memcpy(nxtlev, fsys, sizeof(bool) * symnum);
nxtlev[floorsym] = true;
factordo(nxtlev, ptx, lev);
gendo(opr, 0, 19);
} else if (sym == ceilsym) { /* 新增,向上取整 */
getsymdo;
memcpy(nxtlev, fsys, sizeof(bool) * symnum);
nxtlev[ceilsym] = true;
factordo(nxtlev, ptx, lev);
gendo(opr, 0, 20);
}
//OPR中新增指令
case 19: // 新增,float向下取整
s[t - 1] = floor(s[t - 1]);
break;
case 20: // 新增,float向上取整
s[t - 1] = ceil(s[t - 1]);
break;
11 出错展示功能新增
在头文件新增#include “err_msg.h”,与此同时在err_msg.h中将各种出错原因进行罗列,排序,便于在出错时显示具体原因。