【Compile】PL/0语言编译器功能扩充
文章目录
- 前言
- if-else
- cpp
- dowhile
- cpp
- for-to/downto
- cpp
前言
编译原理作为计算机科学专业中最难的专业课之一,因为其涉及底层编译器的具体执行过程与实现,较为晦涩难懂。为了能够对这门专业课有更加深刻地体验与认识,本次课程设计将围绕实现一个PL/0语言编译器的部分功能,来深入理解词法分析、语法分析、语义分析和目标代码生成等主要步骤的内部实现机制。
if-else
本节任务为扩充语言成分:“if 条件 then 语句系列1 else 语句系列2”,并给出其编译程序实现。

- 调用逻辑表达式处理过程处理if语句的条件,把相应的真假值放到数据栈顶
- 记录下代码段分配位置cx1(即下面生成的jpc指令的位置),然后生成条件转移jpc指令(遇0或遇假转移),转移地址未知暂时填0
- 调用语句处理过程statement处理then语句后面的语句或语句块,then后的语句处理完后,当前代码段分配指针的位置就应该是上面的jpc指令的转移位置(cx1)。通过前面记录下的jpc指令的位置,把它的跳转位置改成当前的代码段指针位置
- 记录下当前代码段的分配位置cx2(即下面生成的jmp指令无条件跳转的位置),然后生成无条件转移jmp(执行真语句后转移),转移地址即为执行完else后的地址
- then后的statement处理完了之后下一个单词是“;”,所以在处理else之前,需要进行判断,再读取,从而跳过“;”
- 判断下一个符号是否为else,若不是说明是简单if语句,直接跳出if执行下一条;若是if-else语句,则进入else语句体。读取单词,并执行else判断体。else后的语句处理完后,当前代码段分配指针的位置就应该是上面的jmp无条件跳转指令的转移位置(cx2)。通过前面记录下的jmp指令的位置,把它的跳转位置改成当前的代码段指针位置
cpp
if (sym == ifsym) /* 准备按照if语句处理 */
{getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);nxtlev[thensym] = true; /* 后跟符号为then, else或do */nxtlev[dosym] = true;nxtlev[elsesym] = true;conditiondo(nxtlev, ptx, lev); /* 调用条件处理(逻辑运算)函数 */if (sym == thensym){getsymdo;}else{error(16); /* 缺少then */}cx1 = cx; /* 保存当前if条件处理的指令地址 */gendo(jpc, 0, 0); /* 生成条件跳转指令,跳转地址未知,暂时写0 */statementdo(fsys, ptx, lev); /* 处理then后的语句,if为真 */ bool isSemi = false;if (sym == semicolon) // 跳过分号{getsymdo;isSemi = true;}if (sym == elsesym) // 若为else,执行其中语句{ cx2 = cx; // 执行完then语句后需无条件转移,出发地址为cx2gendo(jmp, 0, 0); // 执行if为真后,无条件转移code[cx1].a = cx; // 回填cx1的地址,即if执行为假getsymdo; statementdo(fsys, ptx, lev);code[cx2].a = cx; // 判断正式结束,回填if执行结束时的地址}else { code[cx1].a = cx; // 回填cx1的地址,即if执行为假statementdo(fsys, ptx, lev);}
}
dowhile
本节任务为扩充语言成分:“do while语句系列 until 条件”意即循环执行循环体内的语句系列,直到条件为真为止,并给出其编译程序实现。

- 读取单词并对do-while是否匹配进行判断,未匹配则报错
- 记录下当前代码段分配位置cx1,以便重复执行循环体,同时生成until后跟符号集
- 调用语句处理过程statement()执行循环体
- 处理循环体后的分号
- 执行until后的语句,调用条件判断,把相应的真假值放到数据栈顶,同时生成有条件跳转指令jpc,当数据栈顶值为假时,跳转到cx1位置,即循环开始前
cpp
else if (sym == dosym)
{getsymdo;if (sym == whilesym){cx1 = cx; // 保存循环体前的位置getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);nxtlev[untilsym] = true; // 后跟符号为untilstatementdo(fsys, ptx, lev); /* 执行循环体 */if (sym == semicolon) // 处理循环体后的分号{getsymdo;}if (sym == untilsym){getsymdo;conditiondo(nxtlev, ptx, lev); /* 调用条件处理 */gendo(jpc, 0, cx1);}else{error(26); // do-while后缺少until}}else{error(25); // do后缺少while,没有匹配成功}
}
for-to/downto
本节任务为扩充语言成分:
① “for 变量= 初值 to 终值 do begin 语句系列 end”
② “for 变量= 初值 downto 终值 do begin 语句系列 end”
其中,语句①中循环变量的步长为1,语句②中循环变量的步长为-1。并给出其编译程序实现。

- 读取标识符并获得标识符在符号表中的地址,对标识符的类型进行判断,需要确定标识符为变量
- 读取赋值号,并设置to, downto, do为后跟符号集。
- 读取完赋值表达式,进行变量初始化,并调用sto指令,将数据栈顶内容存入变量
- 根据当前符号为to或者downto(若均不是,按错误处理),分别进行处理
- 记录下循环中条件比较地址为cx1,并调用lod将读取的常量置于栈顶,处理循环边界表达式。调用opr13(opr11)指令判断循环条件是否满足,结果用01值存于数据栈顶以便调用。
- 记录下代码分配段地址cx2,并生成jpc有条件跳转指令,地址需回填
- 执行循环体
- 调用lod指令,将变量i调到数据栈顶
- 另取常量1置于数据栈顶
- 调用opr2(opr3)指令,使栈顶与次栈顶相加
- 将数据栈顶的内容存入变量,完成变量递增(递减)的操作
- 执行无条件跳转jmp,回到条件判断位置cx1
- 回填跳出循环的地址cx2
cpp
else if (sym == forsym)
{getsymdo;if (sym != ident) error(27); // for后非标识符i = position(id, *ptx); // 寻找标识符在名称表中的位置if (i == 0) error(11); // 标识符未声明else{if (table[i].kind != variable){error(12); // 当前标识符非变量i = 0;}else{getsymdo;if (sym != becomes) error(13); // 若不是赋值号":="getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);nxtlev[tosym] = true;nxtlev[downtosym] = true;nxtlev[dosym] = true;expressiondo(nxtlev, ptx, lev); // 变量初始化 gen(sto, lev - table[i].level, table[i].adr); // 将数据栈顶的内容存入变量 if (sym == tosym){cx1 = cx; // 记录循环中比较条件的地址,to后getsymdo;gendo(lod, lev - table[i].level, table[i].adr); // 取以上读取的常量放到栈顶memcpy(nxtlev, fsys, sizeof(bool) * symnum);nxtlev[beginsym] = true;expressiondo(nxtlev, ptx, lev); // 处理循环边界表达式gen(opr, 0, 13); // 判断次栈顶是否小于等于栈顶,结果进栈cx2 = cx; // 记录条件跳转地址,跳出循环位置,do前gen(jpc, 0, 0); // 有条件跳转,地址需回填if (sym == dosym){getsymdo;statementdo(nxtlev, ptx, lev); // 执行循环体 }else error(18); // 缺少dogen(lod, lev - table[i].level, table[i].adr); // 取变量i放到栈顶gen(lit, 0, 1); // 取常量1放到栈顶gen(opr, 0, 2); // 取次栈顶与栈顶相加,结果进栈gendo(sto, lev - table[i].level, table[i].adr); // 将数据栈顶的内容存入变量,即i++gen(jmp, 0, cx1); // 重回条件判断code[cx2].a = cx; // 跳出循环位置,地址回填}else if (sym == downtosym){cx1 = cx;getsymdo; gendo(lod, lev - table[i].level, table[i].adr); // 取以上读取的常量放到栈顶memcpy(nxtlev, fsys, sizeof(bool) * symnum);nxtlev[beginsym] = true;expressiondo(nxtlev, ptx, lev); // 处理循环边界表达式gen(opr, 0, 11); // 判断次栈顶是否大于等于栈顶,结果进栈cx2 = cx; // 记录条件跳转地址,跳出循环位置,do前gen(jpc, 0, 0); // 有条件跳转,地址需回填if (sym == dosym){getsymdo;statementdo(fsys, ptx, lev); // 执行循环体}else error(18); // 缺少dogen(lod, lev - table[i].level, table[i].adr); // 取变量i放到栈顶gen(lit, 0, 1); // 取常量1放到栈顶gen(opr, 0, 3); // 取次栈顶与栈顶相减,结果进栈gendo(sto, lev - table[i].level, table[i].adr); // 将数据栈顶的内容存入变量,即i--gendo(jmp, 0, cx1); // 重回条件判断code[cx2].a = cx; // 跳出循环位置,地址回填} else error(28); // for后无to或downto}}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
