编译原理课程设计-对pl0语言进行扩充

  • Post author:
  • Post category:其他




一、 设计目的及要求



1.1 设计目的

根据所学的编译原理理论知识,在符合 PL/0 语言基本词法、语法规则的前提下,以原PL/0 编译程序C语言版本代码为基础,对 PL/0 语言的功能进行扩充。使理论与实践相结合,加深对编译原理的理解。



1.2 设计要求



1.2.1 要求一

1)扩充语言成份:“if 条件 then 语句系列1 else 语句系列2”。

2)写出相应的编译程序。



1.2.2 要求二

1)扩充语言成份:“do while 语句系列 until 条件”,即循环执行循环体内的语句系列,直到条件为真为止。

2)写出相应的编译程序



1.2.3 要求三

1)扩充语言成份:

①“for 变量= 初值 to 终值 do begin 语句系列 end”

②“for 变量= 初值 downto 终值 do begin 语句系列 end”

其中,语句①中循环变量的步长为1,语句②中循环变量的步长为-1。

2)写出相应的编译程序



二、程序设计



2.1 程序的组织结构



2.1.1 PL/0编译程序函数定义层次结构

表1 层次结构图
在这里插入图片描述



2.1.2 PL/0编译程序函数过程或函数功能表

表 2函数功能表

在这里插入图片描述



2.1.3 PL/0指令功能表

表 3指令功能表

在这里插入图片描述



2.1.4 PL/0语言的出错信息表

表 4 出错信息表

在这里插入图片描述



2.2程序流程图



2.2.1 PL/0编译程序和解释执行过程

在这里插入图片描述

图 1 解释执行过程



2.2.2 PL/0程序语法分析

在这里插入图片描述

图 2 语法分析流程图



2.2.3 else语句流程图

在这里插入图片描述

图 3 else语句流程

在这里插入图片描述

表 5 else语句四元式序列



2.2.4 do-while-until语句流程图

在这里插入图片描述

图 4 do-while-until 语句流程

在这里插入图片描述

表 6 do-while-until 语句四元式序列



2.2.5 for语句流程图

在这里插入图片描述

图 5 for语句流程

在这里插入图片描述

表 7 for语句四元式序列



2.3 目标代码生成模式



2.3.1 if-then-else

在这里插入图片描述



2.3.2 do while-until

在这里插入图片描述



2.3.3 for-to/downto-do

在这里插入图片描述



三、系统实现



3.1 else语句扩充

本部分程序首先判断then语句块的最后一个单词是否为“;”。如果then最后一个字符是“;”,则需要跳过该符号之后再执行else及之后的语句。接下来的符号是else时,先记录当前jmp指令的位置,并为jpc指令赋值,使if为false时能直接跳转到else语句之后进行代码执行,然后调用函数处理else之后的语句,最后为jmp指令赋值,使if执行结束时能直接跳转到if-else结束位置。

在这里插入图片描述

在这里插入图片描述

图 6 else语句执行流程和代码结构



3.2 do-while-until语句扩充

本部分程序首先判断当前符号是否为do,然后读取下一个字符并判断是否为while。接着记录当前位置,此时为while循环体开始位置。接下来,允许while后能接的字符为until,然后执行while循环体内的语句。接着判断while的最后一个字符是否是“;”,是,则跳过该符号执行until及之后的语句。当下一个符号是until时,执行判断语句。当判断条件为false时,执行条件跳转,跳转到while循环体的开始位置继续运行。

在这里插入图片描述

在这里插入图片描述

图 7 do-while-until执行流程和代码结构



3.3 for语句扩充

首先识别当前符号为for,然后执行变量定位与赋值语句。



3.3.1 downto-do-end

识别当前符号为downto,记录当前位置,即判断当前变量与终值大小关系的位置。将循环判断变量取出并放到栈顶后,判断循环变量条件并生成比较指令,判断此战顶是否大于等于栈顶,往后退两个栈元素,结果值进栈。再次记录当前位置,即do循环开始的位置。识别当前符号是否为do,并在成立的情况下运行do内循环体。然后将循环变量取出放到栈顶,再将步长1取出放到栈顶,执行指令,次栈顶减去栈顶,退两个栈元素,结果值进栈,最后将栈顶的值存入循环变量。这就完成了变量的自减。然后执行无条件跳转语句,跳转到比较变量与终值大小关系的位置。最后地址回填,将jpc的目的地址赋值为do执行完后的位置,即当次栈顶小于栈顶时,结束for循环语句。

在这里插入图片描述

在这里插入图片描述

图 8 do-while-until语句执行流程和代码结构



3.3.2 to-do-end

与上述downto-do-end基本逻辑一致,只是将判断的大于等于改为了小于等于,将变量的自减改为了自增。

在这里插入图片描述

在这里插入图片描述

图 9 for语句执行流程和代码流程



四、程序源代码



4.1 else语句扩充

解决对编译程序if-else的扩展,补充了代码中缺失的else语句块

//添加处理else语句 
cx2=cx;  /*记录jmp指令的位置,执行完then语句后需要无条件转移,出发地址为cx2 */ 
gendo(jmp,0,0); /*执行完if为真后,无条件转移,将来会直接跳转到else后 */ 
bool isSemi=false; //分号;跳过 
code[cx1]a = cx;   /* 经statement处理后,cx为else语句执行开始的位置,它正是前面未定的跳转地址 (回填cx1的地址,即if执行为假) 为jpc指令赋值 */
if(sym==semicolon)   //如果then语句块的最后一个单词为“;”,则跳过
	{
		getsymdo;   //取下一个单词 
		isSemi=true;  //有";" 
	}
 //接下来的符号是else 
if(sym==elsesym)  //若为else,执行其中的语句
	{
		getsymdo;
		statementdo(fsys,ptx,lev); //处理else后的语句 
		code[cx2]a=cx;  /*当前是else后语句的结束位置,判断正式结束,回填if执行结束时的地址,为jmp指令赋值,if语句执行后应跳转至此*/ 
} 
//上述为添加的else语句处理过程 



4.2 do-while- until语句扩充

解决了do while until 语句的扩充,在程序中添加了do while until语句块。

//添加do while until 语句 
if (sym == dosym) //此时识别的符号是do 
{
	getsymdo;
	if (sym == whilesym) //do while 语句 
	{
		cx1 = cx; //记录当前的位置,即while循环体开始位置 
		getsymdo;
		memcpy(nxtlev, fsys, sizeof(bool)* symnum);
		nxtlev[untilsym] = true; //后跟符号为until 
		statementdo(fsys, ptx, lev); //执行while循环体 
		if (sym == semicolon) //分号,跳过 
		{
			getsymdo;
		}
		if (sym == untilsym) //为until 执行条件语句 
		{
			getsymdo;
			conditiondo(nxtlev, ptx, lev); //执行条件判断 
			gendo(jpc, 0, cx1); //条件跳转,为flase是跳转到while循环体内 
		}
	}
//以上为添加的do while until 语句 



4.3 for语句扩充

解决对编译程序for语句的扩展,补充了for-to和for-downto语句块



4.3.1 “变量=初值”部分代码

找到变量位置并为变量赋初值

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;
								memcpy(nxtlev,fsys,sizeof(bool)*symnum); 
									nxtlev[tosym]=true;
									nxtlev[downtosym]=true;
									nxtlev[dosym]=true;
									expressiondo(nxtlev,ptx,lev);
								}
								else
								{
									error(13);  //没有检测到赋值符号 
								}
								if(i!=0)
								{
									gen(sto,lev-table[i]level,table[i]adr);
								}



4.3.2 “downto-do-end”部分代码

if(sym==downtosym)
{
		cx1=cx; //记录当前位置,即downto后开始判断当前变量与终值大小关系的位置 
		getsymdo;												   
       gendo(lod,lev-table[i]level,table[i]adr); //将循环判断变量取出放到栈顶 
		nxtlev[beginsym]=true; //接下来识别的符号为begin 
		expressiondo(nxtlev,ptx,lev); //判断循环变量条件 
		gen(opr,0,11); //生成比较指令 次栈顶是否大于等于栈顶,退两个栈元素,结果值进栈 
		cx2=cx; //记录循环开始地址
		gen(jpc,0,0); //条件跳转,暂时未知目的地址 ,用0代替 
													
		if(sym==dosym)
		{
				getsymdo ;
				statementdo ( fsys, ptx, lev) ;	//运行do内代码												
				gen (lod, lev - table [i] level, table[i]adr); //将循环变量取出放在栈顶 
				gen (lit, 0, 1); //常数1添加到栈顶 即将步长取到栈顶 
				gen (opr, 0, 3); //循环变量减少1 次栈顶减去栈顶,退两个栈元素,结果值进栈
				gendo (sto, lev - table[i] level, table[i]adr);  //将栈顶的值存入循环变量 
				gendo(jmp , 0, cx1); //无条件跳转,到判断变量与终值大小关系的位置 
				code[cx2]a=cx; /*地址回填, jpc的目的地址时do执行完后的位置 
即当次栈顶小于栈顶时,结束for循环语句*/ 
		} 
		else
		{
				error(18);
		}
}



4.3.3 “to-do-end”部分代码

if(sym==tosym)
{
	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,13);  //生成条件比较表达式
	cx2=cx;  //记录条件跳转地址
	gen(jpc,0,0);
												
	if(sym==dosym)
	{
		getsymdo;
																statementdo(nxtlev,ptx,lev);
	gen(lod,lev-table[i]level,table[i]adr);  //变量提到栈顶
	gen(lit,0,1);  //常数1加到栈顶
	gen(opr,0,2);  //循环变量增加到1
	gendo(sto,lev-table[i]level,table[i]adr);  //赋值
	gen(jmp,0,cx1);  //回条件判断
	code[cx2]a=cx;  //跳出循环 
	} 
	else
	{
		error(18);  /* 缺少do */
	}	
}



五、系统测试



5.1 else语句扩充

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



5.2 do-while-until语句扩充

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



5.3 for语句扩充

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



六、设计总结

经历了长达一周的课程设计工作,我们小组终于完成了本次课程设计的代码编写及论文编撰任务。我们在确认合作意向之后,很快就确定下来了各自的任务分工,在独立完成各自任务的同时,我们也常常合作工作,指出对方的不足并解决,对方的一些问题很庆幸能与默契地搭档一起进行本次课程设计。

在课程设计的过程中,我们首先遇到的问题是庞大源代码的阅读,我们需要先初步阅读并理解老师给出的基础代码之后,再在此基础上进行代码的扩充,基础代码的行数已经多达上千行,我们采取分工的方式,一人读懂一部分函数的功能,然后再以口述的方式给对方讲解,节省理解代码的时间。

接下来,我们先进行的是if-else语句的扩充这一部分的难点是理清jpc和jmp的跳转以及地址的回填如何进行。我们根据老师给出的指导,仔细分析了代码执行流程和结构,确定了jpc和jmp的地址并根据网上查找到的资料,确定了地址回填的编写方式在编写过程中,我们起初将cx2=cx语句写在了if(sym=elsesym)语句内部,后来检查的时候,我们经过讨论,认为应该放到if前,因为then的语句执行完之后即可跳转到else语句末尾,而不必先识别else再跳转。最后,我们还是放在了外部,因为如果放在外部,在编译时无需再编译else语句,能节省时间

dowhile语句的扩充因为有else的语句扩充作为基础,写代码并不困难,基本的逻辑思路是一致的,但是我们犯了一个逻辑上的错误,我们把题目的do-while-until理解成了do-while的结构,导致地址的跳转逻辑错误。

for语句的难点是变量和终值大小的比较如何实现,以及变量的自增与自减如何实现我们首先确定了for语句的代码大致执行顺序与结构,但是接着的代码编写部分有些无从下笔我们决定先整理出函数表以及指令功能表,以便接下来的编写能更清晰地调用相关函数实现目标功能。for后的“变量=初值”部分的代码直接使用的base代码中已给出的赋值代码,并未做修改。变量与终值的比较及变量的自增自减,我们在查找了相关的资料之后,才确定了gen的函数调用方式。

通过本次课设,我们进一步巩固了编译原理的理论知识,在课设过程中以实践的方式,进一步加深了对相关知识的理解与应用。



七、致谢

在本次课程设计中,首先我们由衷地感谢台安老师对于编译原理课程的教授与指导。从课程设计正式开始到后期的完成,都离不开台老师的悉心指导。台老师严谨求时的治学态度,踏实坚韧的工作精神,平易近人的待人方式给我们许多启发和帮助。也正是因为老师的认真负责,为我们编译原理的课设奠定了坚实的理论基础。在此,我们再次向台老师致以诚挚的谢意和崇高的敬意。

其次,在本次课设中还要感谢我亲爱的搭档,我们两个人分工明确,在确定了思路后就立刻开始完成自己的部分,在不理解的地方一起进行讨论,最终在规定的时间内完成。

最后再次感谢所有在本次课程设计中帮助过我们的老师和同学们!



源码与测试用例下载

https://download.csdn.net/download/weixin_47500703/85901408



版权声明:本文为weixin_47500703原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。