PL/0编译程序的扩充

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;//新增repeat7)设置因子开始符号集 
 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语句。

图4 if-then-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中将各种出错原因进行罗列,排序,便于在出错时显示具体原因。

  • 4
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据上机练习一给出的PL/0语言扩充的巴克斯范式语法描述,我们主要利用递归下降来对语法进行分析和解析。递归下降是一种自顶向下的语法分析方法,可以根据产生式规则来递归地分析输入的代码。 首先,我们需要扩展原有的巴克斯范式语法描述,增加新的非终结符和产生式规则。例如,我们可以添加一个非终结符declaration来表示声明语句,以及相应的产生式规则。 在编写递归下降分析器时,我们首先要定义一些函数来对不同的非终结符进行分析。例如,我们可以编写一个函数parseDeclaration来分析声明语句。在这个函数中,我们可以首先判断当前的token是否符合声明语句的语法规则,如果是的话我们可以进一步解析该声明。 在函数中,我们可以根据产生式规则递归地调用其他的分析函数,例如parseExpression来解析表达式,parseStatement来解析语句等等。这样,我们可以根据产生式的规则来逐步解析整个程序。 当遇到终结符时,我们可以根据终结符的类型来进行相应的操作和判断。例如,当遇到标识符时,我们可以将其加入符号表中;当遇到数字时,我们可以将其转换为相应的值。 递归下降分析的过程中,我们需要进行语法错误的处理。例如,当发现输入的代码不符合语法规则时,我们可以输出错误信息并进行相应的纠正。 总的来说,根据上机练习一给出的PL/0语言扩充的巴克斯范式语法描述,我们可以利用递归下降分析方法来解析和分析该语言。通过定义适当的函数和产生式规则,我们可以逐步解析整个程序,并进行相应的语义处理和错误处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值