编译原理PL0教程
PL0源程序-编译原理实验代码

程序清单Main.c#include<stdio.h> #include<stdlib.h> #include<string.h> void error(int n); void getsym();//void enter(enum object k,int *ptx,int lev,int *pdx); int position(char*idt,int tx);int constdeclaration(int *ptx,int lev,int *pdx); int vardeclaration(int *ptx,int lev,int *pdx); int factor(int*ptx,int lev); int term(int *ptx,int lev); int expression(int *ptx,int lev); int statement(int *ptx,int lev); int block();enum object{constant,variable,procedure}; struct tab{char name[14];enum object kind; int val; int level; int adr; int size;}table[100]; enum symbol{nul,ident,number,plus,minus,times,slash,oddsym,eql,neq,lss,leq,gtr,geq,lparen,r paren,comma,semicolon,period,becomes,beginsym,endsym,ifsym,thensym,whilesym,writesym,readsym,dosym,ca llsym,constsym,varsym, procsym,progsym,}; enum symbol sym=nul; enum symbol mulop; enum symbol wsym[14]; enum symbol ssym[256]; char ch=' '; char *str; charword[14][10]={"begin","call","const","do","end","if","odd","procedure","program ","read","then","var","while","write"};// 设置保留字名字int num; int lev=0;附件 1小组成员:int tx=0; int k; int *mm;//=(int *)malloc (sizeof (int )); char *id,sign[14]; char a[14];FILE *fp1,*fp2,*fp3; void error (int n ){switch (n ){case 1:printf (" 常数说明中的 \"=\" 写成了 \":=\" 。
PL0教学课件

PL/0语言及编译器PL/0语言及编译器2.1 PL/0语言和类pcode的描述2.2 PL/0编译程序的结构2.3 PL/0编译程序的语法语义分析2.4 PL/0编译程序的错误处理2.5 类pcode代码解释器本章目的:以PL/0为实例,学习编译程序实现的 基本步骤和相关技术PL/0语言简介z“PL/0语言的编译程序”是世界著名计算机科学家N.Wirth先生编写的。
由于PL/0语言功能简单、结构清晰、可读性强,又具备了一般高级语言的必须部分,因而PL/0语言的编译程序能充分体现一个高级语言编译程序实现的基本技术和步骤,是一个非常合适的编译程序教学模型。
PL/0语言PL/0语言z PL/0程序示例z PL/0的语法描述图z PL/0语言文法的EBNF表示z PL/0语言:PASCAL语言的子集PL/0程序示例PL/0程序示例PL/0语言文法的EBNF表示PL/0语言文法的EBNF表示EBNF 引入的符号(元符号):< > 用左右尖括号括起来的语法成分为非终结符 ∷= (→) ‘定义为’ ∷=(→) 的左部由右部定义| ‘或’{ } 表示花括号内的语法成分可重复任意次或限 定次数[ ] 表示方括号内的语法成分为任选项( ) 表示圆括号内的成分优先例:用EBNF描述<整数>的定义<整数>∷=[+|-]<数字>{<数字>}<数字>∷=0|1|2|3|4|5|6|7|8|9或更好的写法:<整数>∷=[+|-]<非零数字>{<数字>}|0 <非零数字>∷=1|2|3|4|5|6|7|8|9<数字>∷=0|<非零数字>语言是PASCAL PASCAL语言的语言的子集PL/0PL/0语言是语言是PASCAL 语言的子集同PASCAL作用域规则(内层可引用包围它的外层定义的标识符),上下文约束,过程可嵌套定义,可递归调用子集z数据类型,只有整型z数据结构 ,只有简变和常数z数字最多为14位z标识符的有效长度是10z语句种类z 过程最多可嵌套三层PL/0编译程序的总体设计z其编译过程采用一趟扫描方式以语法、、语义分析程序为核心z以语法词法分析程序和代码生成程序都作为一个过程,当语法分析需要读单词时就调用词法分析程序,而当语法、语义分析正确,需要生成相应的目标代码时,则调用代码生成程序。
编译原理课程设计---PL0编辑器扩充

编译原理课程设计题目 __ _PL0编辑器扩充__ __ 学院计算机学院专业软件工程2013 年 1 月 4 日一.课程设计目的与要求1、课程设计目的:在分析理解一个教学型编译程序(如PL/0)的基础上,对其词法分析程序、语法分析程序和语义处理程序进行部分修改扩充。
达到进一步了解程序编译过程的基本原理和基本实现方法的目的。
2、课程设计要求:基本内容(成绩范围:“中”、“及格”或“不及格”)(1)扩充赋值运算:*= 和/=扩充语句(Pascal的FOR语句):①FOR <变量>:=<表达式> TO <表达式> DO <语句>②FOR <变量>:=<表达式> DOWNTO <表达式> DO <语句>其中,语句①的循环变量的步长为2,语句②的循环变量的步长为-2。
(3)增加运算:++ 和--。
选做内容(成绩评定范围扩大到:“优”和“良”)(1)增加类型:①字符类型;②实数类型。
(2)扩充函数:①有返回值和返回语句;②有参数函数。
(3)增加一维数组类型(可增加指令)。
(4)其他典型语言设施。
二、结构设计方案1、结构设计说明:PL/0的编译程序以语法分析程序为核心,词法分析程序和代码生成程序都作为一个独立的过程,当语法分析需要读单词时就用词法分析程序,而当语法分析正确需生成相应的目标代码时,则调用代码生成程序。
此外,用表格管理程序建立变量,常量和过程标识符的说明与引用之间的信息联系。
用出错处理程序对词法和语法分析遇到的错误给出在源程序中出错的位置和错误性质。
2、各功能模块图示:3. 各功能模块作用表:3. 符号名字表结构:struct tablestruct{char name[al]; /*名字*/enum object kind; /*类型:const,var,array or procedure*/ int val; /*数值,仅const使用*/int level; /*所处层,仅const不使用*/int adr; /*地址,仅const不使用*/int size; /*需要分配的数据区空间,仅procedure使用*/ };4. 保留关键字枚举结构:enum symbol{nul, ident, number, plus, minus,times, slash, oddsym, eql, neq,lss, leq, gtr, geq, lparen,rparen, comma, semicolon, period, becomes,beginsym, endsym, ifsym, thensym, whilesym,writesym, readsym, dosym, callsym, constsym,varsym, procsym, elsesym, forsym, tosym,downtosym, returnsym, pluseql, minuseql, plusplus,minusminus, };5.名字表中标识符枚举类型:enum object{constant, /*常量*/variable, /*变量*/procedur, /*过程*/};6.运行时存储组织和管理对于源程序的每一个过程(包括主程序),在被调用时,首先在数据段中开辟三个空间,存放静态链SL、动态链DL和返回地址RA。
第2章 PL0编译程序的实现

2
PL/0语言的语法描述图
程序 分程序
分程序
.
常量说明部分
变量说明部分 过程说明部分 语句
3
PL/0语言的语法描述图
分程序
常量说明部分 变量说明部分 过程说明部分 语句
4
5
例子
因子
a 10 a b*10 a a+10 a+b*10 -a+2*(x+10)-10*(b-10)
项
<分程序>::=[<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句>
9
由语法描述图转化到BNF范式
常量定义
<常量说明部分>::=CONST<常量定义>{,<常量定义>}; <常量定义>::=<标识符>=<无符号整数> <无符号整数>::=<数字>{<数字>}
10
回家作业
1 编写一个PL0语言的程序,要求:
a) 声明2个常数,c1为2,c2为10 b) 声明3个变量,num1、count和sum c) 第一条语句是赋值语句,将c1赋给num1 d) 第二条语句是条件语句,如果num1>0,则执行第三条语句, 否则将num1减1 e) 第三条语句是赋值语句,将0赋给sum e) 第四条语句是循环语句,循环变量count从1到c2,每次将 count值加到sum中
回家作业
2 编写一个PL0语言的程序,要求: a) 包含一个过程procedure 过程名 字为GetSum, 功能是计算累加和 const c1=0,c2=10; var count,sum; procedure GetSum; begin sum:=sum+count end begin sum:=0; count=c1; while count<=c2 do begin count:=count+1; call GetSum; end; end.
pl0编译程序文本c语言版使用,编译原理pl0c语言版pl0.h文件

pl0编译程序⽂本c语⾔版使⽤,编译原理pl0c语⾔版pl0.h⽂件#include# define norw 13 /*关键字个数*/# define txmax 100 /*名字表容量*/# define nmax 14 /*number的最⼤位数*/# define al 10 /*符号的最⼤长度*/# define amax 2047 /*地址上界*/# define levmax 3 /*最⼤允许过程嵌套声明层数[0,lexmax]*/# define cxmax 200 /*最多的虚拟机代码数*//*符号*/enum symbol{nul, ident, number, plus, minus,times, slash, oddsym, eql, neq,lss, leq, gtr, geq, lparen,rparen, comma, semicolon, period, becomes,beginsym, endsym, ifsym, thensym, whilesym,writesym, readsym, dosym, callsym, constsym,varsym, procsym,};#define symnum 32/*-------------*/enum object{constant,variable,procedur,};/*--------------*/enum fct{lit, opr, lod, sto, cal, inte, jmp, jpc,};#define fctnum 8/*--------------*/struct instruction{enum fct f;int l;int a;};FILE *fas;FILE *fa;FILE *fa1;FILE *fa2;bool tableswitch;bool listswitch;char ch;enum symbol sym;char id[al + 1];int num;int cc, ll;int cx;char line[81];char a[al + 1];struct instruction code[cxmax];char word[norw][al];enum symbol wsym[norw];enum symbol ssym[256];char mnemonic[fctnum][5];bool declbegsys[symnum];bool statbegsys[symnum];bool facbegsys[symnum];/*------------------------------*/struct tablestruct{char name[al]; /*名字*/enum object kind; /*类型:const,var,array or procedure*/ int val; /*数值,仅const使⽤*/int level; /*所处层,仅const不使⽤*/int adr; /*地址,仅const不使⽤*/int size; /*需要分配的数据区空间,仅procedure使⽤*/};struct tablestruct table[txmax]; /*名字表*/FILE * fin;FILE* fout;char fname[al];int err; /*错误计数器*//*当函数中会发⽣fatal error时,返回-1告知调⽤它的函数,最终退出程序*/#define getsymdo if(-1==getsym())return -1#define getchdo if(-1==getch())return -1#define testdo(a,b,c) if(-1==test(a,b,c))return -1#define gendo(a,b,c) if(-1==gen(a,b,c))return -1#define expressiondo(a,b,c) if(-1==expression(a,b,c))return -1#define factordo(a,b,c) if(-1==factor(a,b,c))return -1#define termdo(a,b,c) if(-1==term(a,b,c))return -1#define conditiondo(a,b,c) if(-1==condition(a,b,c))return -1#define statementdo(a,b,c) if(-1==statement(a,b,c))return -1#define constdeclarationdo(a,b,c) if(-1==constdeclaration(a,b,c))return -1 #define vardeclarationdo(a,b,c) if(-1==vardeclaration(a,b,c))return -1void error(int n);int getsym();int getch();void init();int gen(enum fct x, int y, int z);int test(bool*s1, bool*s2, int n);int inset(int e, bool*s);int addset(bool*sr, bool*s1, bool*s2, int n);int subset(bool*sr, bool*s1, bool*s2, int n);int mulset(bool*sr, bool*s1, bool*s2, int n);int block(int lev, int tx, bool* fsys);void interpret();int factor(bool* fsys, int* ptx, int lev);int term(bool*fsys, int*ptx, int lev);int condition(bool*fsys, int*ptx, int lev);int expression(bool*fsys, int*ptx, int lev);int statement(bool*fsys, int*ptx, int lev);void listcode(int cx0);int vardeclaration(int* ptx, int lev, int* pdx);int constdeclaration(int* ptx, int lev, int* pdx);int position(char* idt, int tx);void enter(enum object k, int* ptx, int lev, int* pdx); int base(int l, int* s, int b);。
第二章 PL0编译程序的实现

编译原理
◇ 建议用一个PL/0源程序的例子为导引作为 阅读PL/0语言编译程序文本的入门,然后再逐步 全面读懂。 ◇ 通过对PL/0语言编译程序某些指定功能的 扩充,加深对编译程序构造步骤和实现技术的理 解,并能在实践中应用。 【教学方式】 以学生自学为主。
编译原理
【难 重 点】 重点: ◇ 弄清源语言(PL/0)目标语言(类 pcode)实现 语言(pascal) 这3个语言之间的关系和作用。 ◇ 掌握用语法图和扩充的巴科斯-瑙尔范式 (EBNF)对一个高级程序设计语言的形式描述。 ◇ 了解PL/0语言编译程序的语法分析技术采用 的是自顶向下递归子程序法。 ◇ 掌握PL/0语言编译程序的整体结构和实现步 骤,并弄清词法分析、语法分析、语义分析、代码 生成及符号表管理每个过程的功能和相互联系。
编译原理
第2章 PL/0编译程序的实现
【学习目标】 本章目的:以PL/0语言编译程序为实例,学习编 译程序实现的基本步骤和相关技术,对编译程序的构 造和实现得到一些感性认识和建立起整体概念,为后 面的原理学习打下基础。 ◇ 了解用语法图和扩充的巴科斯-瑙尔范式 (EBNF)对 PL/0语言的形式描述。 ◇ 了解PL/0语言编译程序构造和实现的基本技 术和步骤。 ◇ 了解PL/0语言编译程序的目标程序在运行时 数据空间的组织管理。
返回目录
Байду номын сангаас
编译原理
【学习指南】 ◇ 要求读者阅读PL/0语言编译程序文本,了 解一个编译程序构造的必要步骤和实现技术。一个 编译程序的实现比较复杂,读懂一个典型的程序从 设计思想到实现技术也有一定难度,特别是入门开 始需要耐心。一但读懂,不仅了解编译程序的实现 方法和技术,还可学到许多编程技巧和好的编程风 格。 ◇ 阅读PL/0语言编译程序文本时,应从整体 结构开始逐步细化,弄清楚每个过程的功能和实现 方法及过程之间的相互关系。
编译原理PL0课程设计报告

课程设计班级:21301学号:1361080108姓名:马瑞泽百度一.课程设计目的在分析理解一个教学型编译程序(如PL/0)的基础上,对其词法分析程序、语法分析程序和语义处理程序进行部分修改扩充。
达到进一步了解程序编译过程的基本原理和基本实现方法的目的。
二.课程设计要求1. 基本内容(1)扩充赋值运算:+= 和 -=(2)扩充语句(Pascal的FOR语句):①FOR <变量>:=<表达式> TO <表达式> DO <语句>②FOR <变量>:=<表达式> DOWNTO <表达式> DO <语句>其中,语句①的循环变量的步长为2,语句②的循环变量的步长为-2。
2. 选做内容(1)增加运算:++ 和 --。
(2)增加类型:①字符类型;②实数类型。
(3)扩充函数:①有返回值和返回语句;②有参数函数。
(4)增加一维数组类型(可增加指令)。
(5)其他典型语言设施。
3.本人在课程设计中已实现的功能(1)增加单词:保留字 ELSE,FOR,TO,DOWNTO, REPEAT, UNTIL, RETURN运算符 +=,-=,++,--(2)修改单词:不等号# 改为 <>(3)增加条件语句的ELSE子句(4)扩充赋值运算:+= 和 -=(5)扩充语句①FOR <变量>:=<表达式> TO <表达式> DO <语句>②FOR <变量>:=<表达式> DOWNTO <表达式> DO <语句>(6)增加运算:++ 和 --(包括前后++、--运算)(7)增加一维数组类型(8)其他典型语言设施:REPEAT 语句 UNTIL 语句三.课程设计环境与工具(1)计算机及操作系统:PC机,Win7(2)实现工具:VC++ 6.0, C语言(3)教学型编译程序:PL/0四.结构设计说明1)PL/0编译程序的结构图2)PL/0编译程序的过程或函数的功能表4) 词法分析词法分析是编译的第一个阶段,它的主要任务是从左向右逐个字符地对源程序进行扫描,产生一个个单词序列用于语法分析。
PL0编译原理词法语法分析介绍

PL0编译原理词法语法分析介绍PL/0语言是Pascal语言的一个子集,我们这里分析的PL/0的编译程序包括了对PL/0语言源程序进行分析处理、编译生成类PCODE 代码,并在虚拟机上解释运行生成的类PCODE代码的功能。
PL/0语言编译程序采用以语法分析为核心、一遍扫描的编译方法。
词法分析和代码生成作为独立的子程序供语法分析程序调用。
语法分析的同时,提供了出错报告和出错恢复的功能。
在源程序没有错误编译通过的情况下,调用类PCODE解释程序解释执行生成的类PCODE 代码。
词法分析子程序分析:词法分析子程序名为getsym,功能是从源程序中读出一个单词符号(token),把它的信息放入全局变量sym、id和num中,语法分析器需要单词时,直接从这三个变量中获得。
(注意!语法分析器每次用完这三个变量的值就立即调用getsym子程序获取新的单词供下一次使用。
而不是在需要新单词时才调用getsym过程。
)getsym过程通过反复调用getch子过程从源程序过获取字符,并把它们拼成单词。
getch过程中使用了行缓冲区技术以提高程序运行效率。
词法分析器的分析过程:调用getsym时,它通过getch过程从源程序中获得一个字符。
如果这个字符是字母,则继续获取字符或数字,最终可以拼成一个单词,查保留字表,如果查到为保留字,则把sym变量赋成相应的保留字类型值;如果没有查到,则这个单词应是一个用户自定义的标识符(可能是变量名、常量名或是过程的名字),把sym置为ident,把这个单词存入id变量。
查保留字表时使用了二分法查找以提高效率。
如果getch获得的字符是数字,则继续用getch 获取数字,并把它们拼成一个整数,然后把sym置为number,并把拼成的数值放入num变量。
如果识别出其它合法的符号(比如:赋值号、大于号、小于等于号等),则把sym则成相应的类型。
如果遇到不合法的字符,把sym置成nul。
语法分析子程序分析:语法分析子程序采用了自顶向下的递归子程序法,语法分析同时也根据程序的语意生成相应的代码,并提供了出错处理的机制。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
序言 (1)第一部分PL/0语言及其编译器 (2)1.PL/0语言介绍 (2)1.1 PL/0语言的语法图 (3)2.PL/0语言编译器 (6)2.1词法分析 (7)2.2 语法分析 (7)2.3 语义分析 (9)2.4 代码生成 (9)2.5 代码执行 (11)2.6 错误诊断处理 (13)2.7 符号表管理 (15)2.8其他 (16)第二部分上机实践要求 (17)第三部分PL/0语言编译器源程序 (19)1.一个例子 (19)1.1 PL/0语言源程序 (19)1.2 生成的代码(片段) (21)2.PL/0语言编译器源程序 (21)编译原理实践教程序言《编译原理和技术》的课程实践至少有两种可能的安排。
其一,为配合编译课程教学,而安排多次小型实践,分别支持编译程序的各个阶段。
其二,针对某一规模适中的语言来设计和实现一个相对完整、独立编译器。
《编译原理实践教程》作为《编译原理和技术》课程的延伸,其目的是让大家动手设计和实现某一规模适中的语言的编译器,该编译器不仅涉及编译程序的各个阶段,而且也强调了编译的总体设计、各个阶段的接口安排等等。
通过上机实践,来设计这个相对完整的编译器,一方面可以使学生增加对编译程序的整体认识和了解——巩固《编译原理和技术》课程所学知识,另一方面,通过上机练习,学生也可以学到很多程序调试技巧和设计大型程序一般的原则,如模块接口的协调,数据结构的合理选择等等。
为了使学生能尽早动手实践,我们建议把实践分成三部分,首先阅读本教程第一部分,在这部分就PL/0语言的语法及其编译程序的各个阶段作了简单介绍,以便对PL/0编译程序有个初步的印象。
其次要认真阅读理解第三部分所给出的PL/0编译器源程序,使上一阶段的初步印象得以加深、具体化。
最后按照第二部分的实验要求扩充PL/0语言的功能并加以实现。
第一部分 PL/0语言及其编译器1. PL/0语言介绍PL/0程序设计语言是一个较简单的语言,它以赋值语句为基础,构造概念有顺序、条件和重复(循环)三种。
PL/0有子程序概念,包括过程定义(可以嵌套)与调用且有局部变量说明。
PL/0中唯一的数据类型是整型,可以用来说明该类型的常量和变量。
当然PL/0也具有通常的算术运算和关系运算。
具体的PL/0语法图如下。
1.1PL/0语言的语法图程序程序体语句序列程序体.const ident number=var;identprocedure,ident ;;程序体;语句语句;,条件表达式identcallbeginwhileendident:=ifdothen 语句语句表达式语句序列条件条件表达式odd表达式= <> < > <= >=表达式项项+- -+因子因子因子/*identnumber表达式( )2. PL/0语言编译器本书所提供的PL/0语言编译器的基本工作流程如图1-1所示:语法分析词法分析语义分析代码生成 代码执行 源程序执行结果符号表管理错误诊断处理图1-1 PL/0编译器基本工作流程2.1词法分析PL/0的语言的词法分析器将要完成以下工作:(1)跳过分隔符(如空格,回车,制表符);(2)识别诸如begin,end,if,while等保留字;(3)识别非保留字的一般标识符,此标识符值(字符序列)赋给全局量id,而全局量sym赋值为SYM_IDENTIFIER。
(4)识别数字序列,当前值赋给全局量NUM,sym则置为SYM_NUMBER;(5)识别:=,<=,>=之类的特殊符号,全局量sym则分别被赋值为SYM_BECOMES,SYM_LEQ,SYM_GEQ等。
相关过程(函数)有getsym(),getch(),其中getch()为获取单个字符的过程,除此之外,它还完成:(1)识别且跳过行结束符;(2)将输入源文件复写到输出文件;(3)产生一份程序列表,输出相应行号或指令计数器的值。
2.2 语法分析我们采用递归下降的方法来设计PL/0编译器。
以下我们给出该语言的FIRST 和FOLLOW集合。
非终结符(S)FIRST(S) FOLLOW(S) 程序体const var procedure ident call. ;if begin while语句ident call begin if while . ; end条件odd + - ( ident number then do表达式+ - ( ident number . ; ) R end then do项ident number ( . ; ) R + - end then do因子ident number ( . ; ) R + - * / end then do 注:表中R代表六个关系运算符。
不难证明,PL/0语言属于LL(1)文法。
(证明从略。
)以下是我们给出如何结合语法图编写(递归下降)语法分析程序的一般方法。
假定图S所对应的程序段为T(S),则:(1)用合适的替换将语法约化成尽可能少的单个图;(2)将每一个图按下面的规则(3)-(7)翻译成一个过程说明;(3)顺序图对应复合语句:S1 S2 Sn对应:begin T(S1); T(S2); ...; T(Sn) end(4)选择:S1S2S3对应:case语句或者条件语句:case ch of if ch in L1 then T(S1) elseL1: T(S1); if ch in L2 then T(S2) elseL2: T(S2); 或 ...... if ch in Ln then T(Sn) elseLn: T(Sn); error其中Li∈FIRST(Si),ch为当前输入符号。
(下同)(5)循环S对应:while ch in L do T(S)(6)表示另一个图A的图:A对应:过程调用A。
(7)表示终结符的单元图:x对应:if ch == x then read(ch) else error相关过程有:block(), constdeclaration(), vardeclaration(), statement(), condition(), expression(), term(), factor()等。
它们之间依赖关系如图1-2:程序程序体语句条件表达式项因子图1-2 语法分析过程依赖关系2.3 语义分析PL/0的语义分析主要进行以下检查:(1)是否存在标识符先引用未声明的情况;(2)是否存在己声明的标识符的错误引用;(3)是否存在一般标识符的多重声明。
2.4 代码生成PL/0编译程序不仅完成通常的词法分析、语法分析,而且还产生中间代码和“目标”代码。
最终我们要“运行”该目标码。
为了使我们的编译程序保持适当简单的水平,不致陷入与本课程无关的实际机器的特有性质的考虑中去,我们假想有台适合PL/0程序运行的计算机,我们称之为PL/0处理机。
PL/0处理机顺序解释生成的目标代码,我们称之为解释程序。
注意:这里的假设与我们的编译概念并不矛盾,在本课程中我们写的只是一个示范性的编译程序,它的后端无法完整地实现,因而只能在一个解释性的环境下予以模拟。
从另一个角度上讲,把解释程序就看成是PL/0机硬件,把解释执行看成是PL/0的硬件执行,那么我们所做的工作:由PL/0源语言程序到PL/0机器指令的变换,就是一个完整的编译程序。
PL/0处理机有两类存贮,目标代码放在一个固定的存贮数组code中,而所需数据组织成一个栈形式存放。
PL/0处理机的指令集根据PL/0语言的要求而设计,它包括以下的指令:(1)LIT /* 将常数置于栈顶 */(2)LOD /* 将变量值置于栈顶 */(3)STO /* 将栈顶的值赋与某变量 */(4)CAL /* 用于过程调用的指令 */(5)INT /* 在数据栈中分配存贮空间 */(6)JMP, JPC /* 用于if, while语句的条件或无条件控制转移指令 */ (7)OPR /* 一组算术或逻辑运算指令 */上述指令的格式由三部分组成:F L A其中,f, l, a的含义见下表:F L aINT ———常量LIT ———常量LOD 层次差数据地址STO 层次差数据地址CAL 层次差程序地址JMP ———程序地址JPC ———程序地址OPR ———运算类别表2-1 PL/0 处理机指令上表中,层次差为变量名或过程名引用和声明之间的静态层次差别,程序地址为目标数组code的下标,数据地址为变量在局部存贮中的相对地址。
PL/0的编译程序为每一条PL/0源程序的可执行语句生成后缀式目标代码。
这种代码生成方式对于表达式、赋值语句、过程调用等的翻译较简单。
如赋值语句X := Y op Z(op为某个运算符),将被翻译成下面的目标代码序列:(设指令计数从第100号开始)No. f L a100 LOD Level_diff_Y Addr_Y101 LOD Level_diff_Z Addr_Z102 OPR ——————op103 STO Level_diff_X Addr_X而对if和while语句稍繁琐一点,因为此时要生成一些跳转指令,而跳转的目标地址大都是未知的。
为解决这一问题,我们在PL/0编译程序中采用了回填技术,即产生跳转目标地址不明确的指令时,先保留这些指令的地址(code 数组的下标),等到目标地址明确后再回过来将该跳转指令的目标地址补上,使其成为完整的指令。
下表是if、while语句目标代码生成的模式。
(L1,L2是代码地址)if C then S While C do S条件C的目标代码JPC -- L1语句S的目标代码L1: ... L1: 条件C的目标代码JPC – L2语句S的目标代码JMP L1L2: ...相关过程(函数)有:gen(),其任务是把三个参数f、l、a组装成一条目标指令并存放于code数组中,增加CX的值,CX表示下一条即将生成的目标指令的地址。
2.5 代码执行为了简单起见,我们假设有一个PL/0处理机,它能够解释执行PL/0编译程序所生成的目标代码。
这个PL/0处理机有两类存贮、一个指令寄存器和三个地址寄存器组成。
程序(目标代码)存贮称为code,由编译程序装入,在目标代码执行过程中保持不变,因此它可被看成是“只读”存贮器。
数据存贮S组织成为一个栈,所有的算术运算均对栈顶元和次栈顶元进行(一元运算仅作用于栈顶元),并用结果值代替原来的运算对象。
栈顶元的地址(下标)记在栈顶寄存器T中,指令寄存器I包含着当前正在解释执行的指令,程序地址寄存器P指向下一条将取出的指令。
PL/0的每一个过程可能包含着局部变量,因为这些过程可以被递归地调用,故在实际调用前,无法为这些局部变量分配存贮地址。