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

编译原理大作业-PL0语言编译器 编译原理大作业-PL0语言编译器一、实验目的二、源码说明1、头文件pl0.h(1 词法分析主要数据结构(通过enum symbol类实现)1、保留字(13+4个):2、运算符及界符(16+2个):3、标识符4、无符号整数5、字符串(2 中间代码生成与解释执行数据结构(3 符号表管理数据结构2、源文件pl0.cpp(1 初始化init()(2 main函数(3 词法分析getch()函数getsym()函数(4 语法分析+语义分析block()函数statement ()函数constdeclaration 阅读详情
AI摘要

本文介绍了一个PL/0语言编译器的设计与实现,重点讲解了if-else语句、do-while循环及for循环的编译程序实现过程。

前言

编译原理作为计算机科学专业中最难的专业课之一,因为其涉及底层编译器的具体执行过程与实现,较为晦涩难懂。为了能够对这门专业课有更加深刻地体验与认识,本次课程设计将围绕实现一个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
		}
	}
}

基于C++的PL0语言编译器及功能扩充 基于C++实现PL0语言的编译,并完成PL0语言的IDE配置 阅读详情

相关推荐

pl0运行栈中间代码类P-code解析

目录PL/0源程序分析中间代码及注释 PL/0源程序 var n,i,apple; procedure fordiy; const a=7; var b,c; begin while i>0 do begin apple:=(apple+1)*2; i:=i-1; end; end; begin read(n); i:=n; apple:=1; call fordiy; write(apple);

weixin_42815846的博客 3787

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

编译原理课程设计

weixin_47500703的博客 8048

zynq 使用AXI_dma 传输==pl到ps,linux驱动开发,应用层处理DMA数据(一)

zynq axi_dma pl到ps数据传输 axi_dma驱动 linux应用层使用dma

ZYH10140的博客 9402

PL/0语言

一、设计任务 1.1程序实现要求 PL/0语言可以看成PASCAL语言的子集,它的编译程序是一个编译解释执行系统。PL/0的目标程序为假想栈式计算机的汇编语言,与具体计算机无关。 PL/0的编译程序和目标程序的解释执行程序都是用JAVA语言书写的,因此PL/0语言可在配备JDK的任何机器上实现。 其编译过程采用一趟扫描方式,以语法分析程序为核心,词法分析和代码生成程序都作为一个独立的过程,当语法分析需要读单词时就调用词法分析程序,而当语法分析正确需要生成相应的目标代码时,则调用代码生成程序。 用表

weixin_46623752的博客 3502

c语言实现pl0编译器的扩充

c语言实现pl0编译器的扩充: if 条件 then 语句 else 语句 for 变量=初始值 to 终止值 begin 语句 end dowhile 语句 until 条件

PL/0-编译原理课程设计-PL/0编译器的扩充

1、基本内容(成绩范围:“中”、“及格”或“不及格”) (1)扩充赋值运算:+= 和 -= (2)扩充语句(Pascal的FOR语句): ①FOR := TO DO ②FOR := DOWNTO DO 其中,语句①的循环变量的步长为2, 语句②的循环变量的步长为-2。 2、选做内容(成绩评定范围扩大到:“优”和“良”) (1)增加运算:++ 和 --。 (2)增加类型:① 字符类型; ② 实数类型。 (3)扩充函数:① 有返回值和返回语句;② 有参数函数。 (4)增加一维数组类型(可增加指令)。 (5)其他典型语言设施。

pl0编译器扩展注释功能c语言,C语言编写的pl0语言的编译器.docx

C语言编写的pl0语言的编译器.docx由C语言编写的pl0语言的编译器源代码头文件pl0.h#include #define norw 13 // no. of reserved words#define txmax 100 // length of identifier table#define nmax 1...

weixin_36468140的博客 703

pl0语言扩充源代码

比较全面的pl0实验扩充 何为PL/0语言 - PL/0语言是PASCAL语言的子集. 具备了一般高级语言的必备部分. (如: read,write,if-then,do,while,call,begin-end,赋值语句) • PL/0语言中的数据类型只有整型,没有浮点数,所以圆周率只能近似为3。数字最多为14位。标识符的有效长度是10 • PL/0语言允许过程嵌套定义和递归调用的。过程最多可嵌套三层。 • 过程可以引用自己定义的局部标识符,也可以引用包围在它的外过程(包括主程序)定义的标识符。

pl0词法分析器c语言_词法分析器

前言在这一系列文章中,我们将会使用Python语言来实现PL0编译器。首先我们来实现编译器中的词法分析的功能。我们使用正则表达式来对源程序的程序语句筛选为:关键字Keywords,数字Numbers,变量Variables,分隔符Separatores和运算符Operatores五种类别。词法分析器的输出是源程序中的单词和它所对应的类别组成的二元组。正则表达式表达式的定义:例如:'a.':表示匹配...

weixin_39902598的博客 654

编译原理课程设计——对PL/0作出修改扩充

对PL/0作以下修改扩充: 基本内容: (1)扩充赋值运算:+= 和 -= (2)扩充语句(Pascal的FOR语句): ①FOR := TO DO ②FOR := DOWNTO DO 其中,语句①的循环变量的步长为1,语句②的循环变量的步长为-1。 选做内容: (1)增加运算:++ 和 --。(实现) (2)增加类型:① 字符类型; ② 实数类型。(未实现) (3)扩充函数:① 有返回值和返回语句;② 有参数函数。(未实现) (4)增加一维数组类型(可增加指令)。(实现) (5)其他典型语言设施。

基于PL/0语言的扩充——if-then-else,do-while-until,for

编译原理课程设计,是对PL0语言的扩充。 主要包括if-then-else,do-while-until,for 资源里有我写的报告和源码,c++实现。 需要自己运行测试

学习使用的PL/0编译器增强版PL/0plusplusCompiler(三)加入“man” 功能

Linux中很赞的工具man,查看命令或者工具的帮助手册manual。 在PL0.h中声明help方法,void help();在PL0.c中实现help这个方法,/*显示帮助文档*/ void help(){ printf("\n\nPL0 plus plus Compiler:\n"); printf("编译源码: pl0 test.pl0\n"); printf

珍惜时间 1215

数理逻辑1 -- 命题演算1

一直对逻辑的东西很感兴趣,尤其最近做的东西和AI相关,因此下定决心系统地学习数理逻辑。这些笔记并不是严谨的描述,只是作为一些概念的快速参考,以及加入一些个人认为好玩的公式、定理和推导。 简单来说,数理逻辑系统是由一串“符号”组成的系统,基本元素是那一堆“符号”,由“符号”组成“式子”。逻辑系统并不关心“式子”是否对错,只关心“式子”是否存在,也即是假定存在某些“式子“,再确定”推导规则“(Rule

Jason渣神的专栏 1830

学习使用的PL/0编译器增强版PL/0plusplusCompiler(四)使用gdb调试PL0词法分析

默认已经把编译器编译成pl0c可执行程序了 1. 使用gdb把程序运行起来 gdb ./pl0c 2. 加断点 break getsym 意思是在getsym这个函数加断点 3. 加参数执行 r test.pl0 因为需要分析test.pl0这个源代码文件,所以需要这个参数 r是run 4. continue执行

珍惜时间 1032

第1章>>1.5:神电测控为什么开发LabVIEW ZYNQ FPGA软件工具包?(ZYNQ芯片的特点与优势)《LabVIEW ZYNQ FPGA宝典》

ZYNQ是赛灵思公司(Xilinx)推出的新一代全可编程片上系统(APSoC,All-Programmable SoC),它将处理器的软件可编程性与FPGA的硬件可编程性进行完美整合,以提供无与伦比的系统性能、灵活性与可扩展性。Zynq-7000系列是Xilinx于2010年4月推出的行业第一个可扩展处理平台,旨在为视频监视、汽车驾驶员辅助、工厂自动化、医疗器械等高端嵌入式应用提供所需的处理能力与计算性能。这款基于ARM处理器的SoC可满足复杂嵌入式系统的高性能、低功耗和多核处理能力等要求。

qq_40694786的博客 1646

使用3dmax Morpher制作Unity表情动画

关于游戏人物的面部表情动画 方案一:如果面部骨骼数丰富的话,可以直接做骨骼动画 方案二:采用变形器(3dmax是Morpher,maya是blendshape)修改相应通道的权重值来做表情动画 本文简述方案二的做法。 说明:本文截图中3dsMax版本为2017。笔者第一次使用3dsMax,欢迎指正。 3dmax中导入人物模型,选择需要变形的对象,即head。当前对象附带skin信息。 ...

写作帮助思考 2万+

Maven 生命周期:compile/package/install/deploy 4 阶段核心产出与 IDEA 集成实践

本文深入解析Maven生命周期中的compile、package、install和deploy四个核心阶段,结合IntelliJ IDEA的集成环境,提供从编译到部署的完整实践指南。通过详细的配置示例和常见问题排查,帮助开发者掌握Maven构建流程,提升项目管理和自动化构建效率。

weixin_34221775的博客 376

Verilog除法器(32位无符号+带符号)

32位无符号除法器设计 恢复余数法: 恢复余数除法的基本思路是从“部分余数”中减去除数,如果结果为负(不够减),则恢复原来的部分余数,商0. 寄存器使用: reg_r 存放被除数a reg_b 存放除数b reg_r 存放余数(初始清零) 具体做法: 做减法时,减数是reg_b中的除数,被减数是reg_r中的余数左移一位,最低位由reg_q(被除数)的最高位补充。为了能够判断相减结果的正...

qq_38600065的博客 1万+

CORBA Programming with TAO - 2.Compile Source Code(编译ACE/TAO/CIAO)

<br />摘要:<br />简要介绍如何在Windows平台上编译ACE/TAO/CIAO,及编译过程中需注意的问题。<br />正文:<br />随着ACE/TAO/CIAO研究者研究的不断深入,整个平台变得越来越庞大,涉及的工程及目录数目也越来越多,下面简要介绍一下如何在Windows平台上编译ACE/TAO/CIAO,其过程在其它平台上是类似的。<br />一、目录结构<br />在开始之前,先简要介绍一下整个产品的目录结构。ACE(Adaptive Communication Environmen

xinpo66的专栏 804

vasp-自旋轨道耦合(SOC)计算步骤

在VASP中执行自旋轨道耦合(SOC)计算,具体的计算步骤如下: 结构优化获取CONTCAR文件 自洽计算(collinear normal VASP calculations)获取IBZKPT,CHGCAR和WAVECAR文件。 SCF+SOC计算,在此步骤中,将步骤2中的IBZKPT复制为KPOINTS,以便两个步骤具有相同的k网格,因此可以读取步骤2中的CHGCAR和WAVECAR文件。 能带结构计算,在此步骤中,将KPOINTS改为高对称点,读取步骤3中的CHGCAR和WAVECAR文件。

懒散的鱼与消失的猫 3万+

DC竞赛 国能日新功率预测题 rank21 解决方案(适合新手)

DataCastle国能日新光伏功率预测 数据异常处 本题在测试集中提供了时间、辐照度、风速、风向、温度、压强、湿度、实发辐照度8个特征,以及标签**实际功率**。 而在训练集中只有前七个特征,无实发辐照度。 策略 在观察了数据之后,我们确定实发辐照度是一个强力特征,于是我们采取了stacking的策略: 1. 第一次训练以实发辐照度为标签,预测 测试集的实发辐照度 2....

yyhhlancelot的博客 7293

Gaudi3/Versal/Axion/TPUv5四大AI芯片工程落地实战对比

AI芯片并非单纯追求峰值算力的硬件,而是软硬协同的系统级工程载体。其核心价值在于能否在真实业务中实现高吞吐、低延迟、强确定性与生态可持续性。从晶体管物理极限到编译器栈成熟度,从内存带宽瓶颈到散热供电约束,AI加速器的实际效能取决于功耗墙突破能力与PyTorch/Torch.compile等主流框架的深度适配水平。Gaudi3强调通用性工程量化,Versal以自适应异构架构支撑边缘部署,Axion聚焦ASIL-D级确定性保障,TPUv5则体现云原生垂直整合优势。本文基于金融风控、实时视频分析、大模型服务等典型

weixin_34391445的博客 435

人群仿真软件:Pathfinder_(14).与其他软件的集成与互操作

在人群仿真软件的二次开发中,与其他软件的集成和互操作是非常重要的。通过数据导入导出、接口调用和脚本编写,可以显著提升仿真的准确性和实用性。Pathfinder提供了多种数据格式的支持和丰富的API,使得与其他软件的集成变得简单高效。无论是建筑模型的导入、仿真结果的导出,还是实时数据的交换,Pathfinder都能满足用户的需求。

kkchenjj的博客 1015
上一篇: 【Compile】算符优先文法分析(C++)
下一篇: 【山头斜照却相迎】初入计算机学习计划推荐
CodeSlogan
博客等级 码龄6年 4227粉丝 130原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

CodeSlogan

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

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

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

打赏作者

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

抵扣说明:

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

余额充值