实验报告.docx
- 文档编号:10353224
- 上传时间:2023-02-10
- 格式:DOCX
- 页数:42
- 大小:88.09KB
实验报告.docx
《实验报告.docx》由会员分享,可在线阅读,更多相关《实验报告.docx(42页珍藏版)》请在冰豆网上搜索。
实验报告
编译原理实验报告
课程名称:
编译原理与技术
学院名称:
计算机科学与技术学院
专业班级:
计算机
学生姓名:
学号:
指导教师:
实验一:
PL/0语言建立一个词法分程序GETSYM(函数)
1.实验目的:
加深对课堂教学的理解;提高词法分析方法的实践能力。
2.实验要求:
把关键字、算符、界符称为语言固有的单词,标识符、常量称为用户自定义的单词。
为此设置三个全程量:
SYM,ID,NUM。
SYM:
存放每个单词的类别,为内部编码的表示形式。
ID:
存放用户所定义的标识符的值,即标识符字符串的机内表示。
NUM:
存放用户定义的数。
GETSYM要完成的任务:
1.滤掉单词间的空格。
2.识别关键字,用查关键字表的方法识别。
当单词是关键字时,将对应的类别放在SYM中。
如IF的类别为IFSYM,THEN的类别为THENSYM。
3.识别标识符,标识符的类别为IDENT,IDRNT放在SYM中,标识符本身的值放在ID中。
关键字或标识符的最大长度是10。
4.拼数,将数的类别NUMBER放在SYM中,数本身的值放在NUM中。
5.拼由两个字符组成的运算符,如:
>=、<=等等,识别后将类别存放在SYM中。
6.打印源程序,边读入字符边打印。
由于一个单词是由一个或多个字符组成的,所以在词法分析程序GETSYM中定义一个读字符过程GETCH。
3.程序设计:
(1)首先将标识符,变量,常量,运算符,关键字,标点符号分别放入数组中。
(2)构造实验所需要的方法:
如下源代码中所示(包括文本的搜索和读入等)。
(3)根据要编译的程序中的空格将其中的单词等分辨出来,并将其保存在相应的数组中。
(4)将结果写入文件。
实验二:
PL/0语言建立一个语法分析程序BLOCK(函数)
实验内容:
PL/0编译程序采用一遍扫描的方法,所以语法分析和代码生成都有在BLOCK中完成。
BLOCK的工作分为两步:
a)说明部分的处理
说明部分的处理任务就是对每个过程(包括主程序,可以看成是一个主过程)的说明对象造名字表。
填写所在层次(主程序是0层,在主程序中定义的过程是1层,随着嵌套的深度增加而层次数增大。
PL/0最多允许3层),标识符的属性和分配的相对地址等。
标识符的属性不同则填写的信息不同。
所造的表放在全程量一维数组TABLE中,TX为指针,数组元素为结构体类型数据。
LEV给出层次,DX给出每层的局部量的相对地址,每说明完一个变量后DX加1。
例如:
一个过程的说明部分为:
consta=35,b=49;
varc,d,e;
procedurep;
varg;
对它的常量、变量和过程说明处理后,TABLE表中的信息如下:
NAME:
a
NAME:
b
NAME:
c
NAME:
d
NAME:
e
NAME:
p
KIND:
CONSTANT
KIND:
CONSTANT
KIND:
VARIABLE
KIND:
VARIABLE
KIND:
VAEIABLE
KIND:
PROCEDURE
VAL:
35
VAL:
49
LEVEL:
LEV
LEVEL:
LEV
LEVEL:
LEV
LEVEL:
LEV
ADR:
DX
ADR:
DX+1
ADR:
DX+2
ADR:
NAME:
g
。
。
。
KIND:
VARIABLE
。
。
。
LEVEL:
LEV+1
。
。
。
ADR:
DX
。
。
。
对于过程名的ADR域,是在过程体的目标代码生成后返填过程体的入口地址。
TABLE表的索引TX和层次单元LEV都是以BLOCK的参数形式出现,在主程序调用BLOCK时实参的值为0。
每个过程的相对起始位置在BLOCK内置初值DX=3。
2.语句处理和代码生成
对语句逐句分析,语法正确则生目标代码,当遇到标识符的引用则去查TABLE表,看是否有过正确的定义,若有则从表中取出相关的信息,供代码生成用。
PL/0语言的代码生成是由过程GEN完成。
GEN过程有三个参数,分别代表目标代码的功能码、层差、和位移量。
生成的目标代码放在数组CODE中。
CODE是一维数组,数组元素是结构体类型数据。
PL/0语言的目标指令是一种假想的栈式计算机的汇编语言,其格式如下:
fl
a
其中f代表功能码,l代表层次差,a代表位移量。
目标指令有8条:
①LIT:
将常数放到运栈顶,a域为常数。
②LOD:
将变量放到栈顶。
a域为变量在所说明层中的相对位置,l为调用层与说明层的层差值。
③STO:
将栈顶的内容送到某变量单元中。
a,l域的含义与LOD的相同。
④CAL:
调用过程的指令。
a为被调用过程的目标程序的入中地址,l为层差。
⑤INT:
为被调用的过程(或主程序)在运行栈中开辟数据区。
a域为开辟的个数。
⑥JMP:
无条件转移指令,a为转向地址。
⑦JPC:
条件转移指令,当栈顶的布尔值为非真时,转向a域的地址,否则顺序执行。
⑧OPR:
关系和算术运算。
具体操作由a域给出。
运算对象为栈顶和次顶的内容进行运算,结果存放在次顶。
a域为0时是退出数据区。
实验主要步骤:
1.首先运行词法分析器,逐个读取文件中的单词,
2.然后识别每个单词,判定每个单词的类别
3.之后根据单词的类别来选择不同的处理方式
4.而且在语法分析函数的block函数中定义了两个可变数组,分别存放table表和code代码。
5.另外定义了error数组,分别存放出错的类型,但是数量有限,不能完全识别所有错误。
实验三:
建立一个解释执行目标程序的函数
实验内容:
编译结束后,记录源程序中标识符的TABLE表已退出内存,内存中只剩下用于存放目标程序的CODE数组和运行时的数据区S。
S是由解释程序定义的一维整型数组。
解释执行时的数据空间S为栈式计算机的存储空间。
遵循后进先出的规则,对每个过程(包括主程序)当被调用时,才分配数据空间,退出过程时,则所分配的数据空间被释放。
为解释程序定义四个寄存器:
1.I:
指令寄存器,存放当前正在解释的一条目标指令。
2.P:
程序地址寄存器,指向下一条要执行的目标指令(相当于CODE数组的下标)。
3.T:
栈顶寄存器,每个过程运行时要为它分配数据区(或称为数据段),该数据区分为两部分。
静态部分:
包括变量存放区和三个联单元。
动态部分:
作为临时工作单元和累加器用。
需要时临时分配,用完立即释放。
栈顶寄存器T指出了当前栈中最新分配的单元(T也是数组S的下标)。
4.B:
基地址寄存器,指出每个过程被调用时,在数据区S中给出它分配的数据段起始地址,也称为基地址。
每个过程被调用时,在栈顶分配三个联系单元。
这三个单元的内容分别是:
SL:
静态链,它是指向定义该过程的直接外过程运行时数据段的基地址。
DL:
动态链,它是指向调用该过程前正在运行过程的数据段的基地址。
RA:
返回地址,记录调用该过程时目标程序的断点,即当时的程序地址寄存器P的值。
具体的过程调用和结束,对上述寄存器及三个联系单元的填写和恢复由下列目标指令完成。
1.INT0a
a:
为局部量个数加3
2.OPR00
恢复调用该过程前正在运行过程(或主程序)的数据段的基地址寄存器的值,恢复栈顶寄存器T的值,并将返回地址送到指令寄存器P中。
3.CALla
a为被调用过程的目标程序的入口,送入指令地址寄存器P中。
CAL指令还完成填写静态链,动态链,返回地址,给出被调用过程的基地址值,送入基址寄存器B中。
部分源代码如下:
packagepl0;
importjava.util.Vector;
publicclassgenerate
{
publicwordanalysisCiFa;
publicwordword;
publicStringid=null;//用于登录名字表时的word名字
publicintlineNum;//用于错误处理时的行数记录
publicinterrorNumber=0;//用于保存语法分析中的错误数目
publicErrorerror=newError();
intcx;//VectorCode'ssize
intcx0;//VectorCode'ssize,保存当前代码的地址
intdx;//给出每层的局部量的相对地址
intlev=-1;//记录层次
interrorNum=0;
VectorCODE=newVector();;//生成一个vector矢量!
//此矢量用作存放CodeElement的数组
VectorTABLE=newVector();//生成一个存放矢量作为名字表;
publicgenerate(wordanalysisCF)//语法分析构造函数
{
CiFa=CF;//从主程序传入一个词法分析对象,将其赋给CiFa
word=CiFa.GetWord();//获得一个word
analyse();//然后调用分析程序
}
publicintgetErroNumber()
{
returnerrorNumber;
}
publicvoidprintTable()//用来查看符号表内容的
{
for(intt=1;t
{
tabname=(tab)TABLE.get(t);
if(name.kind.equals("constant"))
name.showConst();
elseif(name.kind.equals("variable"))
name.show();
elseif(name.kind.equals("procedure"))
{
//?
?
?
t++
t++;
tabtempname=(tab)TABLE.get(t);//此时的name.adr为0
name.adr=tempname.adr;//?
?
?
name.show();
//System.out.println("tempname:
");
//tempname.show();
}
}
}
publicvoidprintTable(inti)//用来查看符号表内容的
{
intt=i;
tabname=(tab)TABLE.get(t);
if(name.kind.equals("constant"))
name.showConst();
elseif(name.kind.equals("variable"))
name.show();
elseif(name.kind.equals("procedure"))
{
//?
?
?
t++
t++;
tabtempname=(tab)TABLE.get(t);//此时的name.adr为0
name.adr=tempname.adr;//?
?
?
name.show();
//System.out.println("tempname:
");
//tempname.show();
}
}
publicvoidprintCode()//CodeElement的显示方法,如果没有错误,则打印CodeElement代码
{
for(intk=1;k { Codecode=(Code)CODE.get(k); code.print(k); //System.out.println(n+"\t"+code.getF()+"\t"+code.getL() //+"\t"+code.getA()); //Codecode=(Code)CODE.get(n); //System.out.println(n+"\t"+code.getF()+"\t"+code.getL() //+"\t"+code.getA()); } } publicvoidprintCode(inti)//CodeElement的显示方法,如果没有错误,则打印CodeElement代码 { intk=i; Codecode=(Code)CODE.get(k); code.print(k); //System.out.println(n+"\t"+code.getF()+"\t"+code.getL() //+"\t"+code.getA()); //Codecode=(Code)CODE.get(n); //System.out.println(n+"\t"+code.getF()+"\t"+code.getL() //+"\t"+code.getA()); } /** *语法分析 *调用常量声明、变量声明、过程声明 *递归调用analyse */ publicvoidanalyse() { inttx0; lev++; dx=3;//初始ADR,以后每遇到一个变量,dx+1 tx0=TABLE.size();//用tx0保存当前层的符号在符号表中的起始位置 TABLE.addElement(newtab("","",0,0,0));//在这里给符号表填加一个元素,否则下一条代码会运行出界 ((tab)TABLE.get(tx0)).setAdr(CODE.size());//在上面加的那个元素里的私有字段adr里保存当前层代码的开始位置 CODE.addElement(newCode("jmp",0,0));//生成跳转指令 由于跳转位置未知 暂时添0 System.out.println("1: "+CODE.size()); while(word.getSym().equals("constsym") ||word.getSym().equals("varsym") ||word.getSym().equals("procsym")) { if(word.getSym().equals("constsym"))//常量处理 { word=CiFa.GetWord(); constDefine();//调用常量声明,注意常量声明完毕时还要再次读取一个word //若在常量声明后读取的word为逗号,则要继续进行常量声明,这里采用while循环,直到word不是逗号 while(word.getSym().equals(",sym")) { word=CiFa.GetWord(); constDefine();//调用常量声明,注意常量声明完毕时还要再次读取一个word } if(word.getSym().equals(";sym"))//若word不是逗号,则应该是分号了,标志常量声明结束 { word=CiFa.GetWord(); }else { errorNumber++; error.error(word.getLineNum(),5);//如果常量声明完毕后没有遇到分号;则抛出15号错误 } } //变量处理 elseif(word.getSym().equals("varsym")) { word=CiFa.GetWord();//读取一个word,应该是一个ident标识符 varDefine();//调用变量声明 while(word.getSym().equals(",sym")) { word=CiFa.GetWord(); varDefine();//调用变量声明,注意常量声明完毕时还要再次读取一个word } if(word.getSym().equals(";sym")) { word=CiFa.GetWord(); }else { errorNumber++; error.error(word.getLineNum(),5);//如果变量声明完毕后没有遇到分号;则调用错误处理程序抛出5号错误 } } //循环声明各个子过程 //这里为什么要用while? ? ? 改成if语句结果也正确,主要是例子里面没有嵌套的过程吧。 while(word.getSym().equals("procsym")) { word=CiFa.GetWord(); if(word.getSym().equals("ident")) { id=word.getName(); add("procedure",id); word=CiFa.GetWord(); }else { errorNumber++; error.error(word.getLineNum(),4);//过程名不是标识符,抛出4号错误 } if(word.getSym().equals(";sym")) { word=CiFa.GetWord(); }else { errorNumber++; error.error(word.getLineNum(),5); }//过程名后没有分号 //保存当前的ADE和lev inttempdx=dx; intlevv=lev; //在这里递归调用下一层的分程序段分析,接下来如果不是常量、变量、过程声明, //那么就直接跳过analyse开始的while循环,开始进行语句的分析了 analyse(); dx=tempdx; lev=levv; if(word.getSym().equals(";sym")) { word=CiFa.GetWord(); //若end;后的符号不在ident,procsym,begin,call,if,while中,则调用6号错误 Stringsym=word.getSym(); if(! (sym.equals("ident")||sym.equals("procsym") ||sym.equals("beginsym")||sym.equals("callsym") ||sym.equals("ifsym")||sym.equals("whilesym"))) { errorNumber++; error.error(word.getLineNum(),6); } }else { errorNumber++; error.error(word.getLineNum(),5); } } } //? ? ? 下面又不懂了啊! ! ! //把前面生成的跳转指令的跳转位置改成当前位置 System.out.println(CODE.size()); printCode(CODE.size()-1); ((Code)CODE.get(((tab)TABLE.get(tx0)).getAdr())).setA(CODE.size()); System.out.println("CODE"+((tab)TABLE.get(tx0)).getAdr()); printCode(((tab)TABLE.get(tx0)).getAdr()); //在符号表中,对于过程名,adr应填入编译该过程所生成的CodeElement指令序列的入口地址 //目前TABLE.get(tx0))里面还没有什么内容。 ((tab)TABLE.get(tx0)).setAdr(CODE.size()); System.out.println("TABLEtx0: "+tx0); printTable(tx0); cx0=CODE.size();//在cx0中保存当前代码的分配地址 CODE.addElement(newCode("int",0,dx));//生成空间分配指令,分配dx个空间(3个空间+变量的数目) //printCode(cx0-2); //printCode(cx0-1); //printCode(cx0); //? ? ? statement();//处理当前遇到的语句; CODE.addElement(newCode("opr",0,0));//生成子程序返回指令; } /** *常量声明 * */ publicvoidconstDefine()//常量声明的方法 { if(word.getSym().equals("ident"))//在analyse调用常量声明时,上一个word为const,这一个应该是ident标识符 { id=word.getName();//调用Word类的getNam方法,获得当前word的内容,这里将其保存在id中,为的是一会进行的符号表插入工作 word=CiFa.GetWord();//然后再读取一个word,应该是等号"="了 if(word.getSym().equals(": =sym")) { errorNumber++; error.error(word.getLineNum(),1);//如果不是等号,而是赋值符号: =,抛出1号错误 }elseif(word.getSym().equals("=sy
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验 报告
