【Compile】PL/0语言编译器功能扩充

前言

编译原理作为计算机科学专业中最难的专业课之一,因为其涉及底层编译器的具体执行过程与实现,较为晦涩难懂。为了能够对这门专业课有更加深刻地体验与认识,本次课程设计将围绕实现一个PL/0语言编译器的部分功能,来深入理解词法分析、语法分析、语义分析和目标代码生成等主要步骤的内部实现机制。

if-else

本节任务为扩充语言成分:“if 条件 then 语句系列1 else 语句系列2”,并给出其编译程序实现。
if-else流程图

  1. 调用逻辑表达式处理过程处理if语句的条件,把相应的真假值放到数据栈顶
  2. 记录下代码段分配位置cx1(即下面生成的jpc指令的位置),然后生成条件转移jpc指令(遇0或遇假转移),转移地址未知暂时填0
  3. 调用语句处理过程statement处理then语句后面的语句或语句块,then后的语句处理完后,当前代码段分配指针的位置就应该是上面的jpc指令的转移位置(cx1)。通过前面记录下的jpc指令的位置,把它的跳转位置改成当前的代码段指针位置
  4. 记录下当前代码段的分配位置cx2(即下面生成的jmp指令无条件跳转的位置),然后生成无条件转移jmp(执行真语句后转移),转移地址即为执行完else后的地址
  5. then后的statement处理完了之后下一个单词是“;”,所以在处理else之前,需要进行判断,再读取,从而跳过“;”
  6. 判断下一个符号是否为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语句后需无条件转移,出发地址为cx2
		gendo(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 条件”意即循环执行循环体内的语句系列,直到条件为真为止,并给出其编译程序实现。
dowhile流程图

  1. 读取单词并对do-while是否匹配进行判断,未匹配则报错
  2. 记录下当前代码段分配位置cx1,以便重复执行循环体,同时生成until后跟符号集
  3. 调用语句处理过程statement()执行循环体
  4. 处理循环体后的分号
  5. 执行until后的语句,调用条件判断,把相应的真假值放到数据栈顶,同时生成有条件跳转指令jpc,当数据栈顶值为假时,跳转到cx1位置,即循环开始前

cpp

else if (sym == dosym)
{
	getsymdo;
	if (sym == whilesym)
	{
		cx1 = cx;  // 保存循环体前的位置
		getsymdo;
		memcpy(nxtlev, fsys, sizeof(bool) * symnum);
		nxtlev[untilsym] = true; // 后跟符号为until
		statementdo(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。并给出其编译程序实现。

for-to/downto流程图

  1. 读取标识符并获得标识符在符号表中的地址,对标识符的类型进行判断,需要确定标识符为变量
  2. 读取赋值号,并设置to, downto, do为后跟符号集。
  3. 读取完赋值表达式,进行变量初始化,并调用sto指令,将数据栈顶内容存入变量
  4. 根据当前符号为to或者downto(若均不是,按错误处理),分别进行处理
  5. 记录下循环中条件比较地址为cx1,并调用lod将读取的常量置于栈顶,处理循环边界表达式。调用opr13(opr11)指令判断循环条件是否满足,结果用01值存于数据栈顶以便调用。
  6. 记录下代码分配段地址cx2,并生成jpc有条件跳转指令,地址需回填
  7. 执行循环体
  8. 调用lod指令,将变量i调到数据栈顶
  9. 另取常量1置于数据栈顶
  10. 调用opr2(opr3)指令,使栈顶与次栈顶相加
  11. 将数据栈顶的内容存入变量,完成变量递增(递减)的操作
  12. 执行无条件跳转jmp,回到条件判断位置cx1
  13. 回填跳出循环的地址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);  // 缺少do

				gen(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);  // 缺少do
				gen(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
		}
	}
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeSlogan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值