广工编译原理实验报告.docx
《广工编译原理实验报告.docx》由会员分享,可在线阅读,更多相关《广工编译原理实验报告.docx(20页珍藏版)》请在冰豆网上搜索。
广工编译原理实验报告
<<编译原理>>
课内实验报告
工程名称PL/0编译器
学院____计算机学院_______
专业__
年级班别________
学号_
学生姓名__________
辅导老师_______
成绩______________
一、课内实验的内容------------------------------------------4
二、实验修改局部--------------------------------------------4
三、概述-------------------------------------------------11
四、构造设计说明-------------------------------------------11
五、各功能模块描绘-----------------------------------------14
六、主要成份描绘------------------------------------------14
七、测试用例----------------------------------------------16
八、开发过程和完成情况--------------------------------------21
一、课内实验的内容
对PL/0作以下修改扩大:
〔1〕增加单词:
保存字ELSE,FOR,STEP,RETURN
运算符+=,-=,++,--,&,|,~
〔2〕修改单词:
不等号#改为<>
〔3〕增加条件语句的ELSE子句
二、实验修改局部:
1、增加四个保存字和七个运算符,共十一个单词。
修改局部:
#definesymnum43//保存字从32增加到43个
2、增加五个保存字:
ELSE,FOR,STEP,RETURN
头文件pl0.h
enumsymbol{新增加单词:
elsesym,forsym,stepsym,returnsym,pluseq/*+=*/,plusone/*++*/,plus/*+*/,minuseq/*-=*/,minusone/*--*/,minus/*-*/,and,or,not}
头文件pl0.h
#definenorw24//关键字从13增加到24个
PL0.cpp
init();
新增加:
〔增加后数组的内容要再次根据字母顺序重新排列〕
strcpy(&(word[0][0]),"begin");
strcpy(&(word[1][0]),"call");
strcpy(&(word[2][0]),"const");
strcpy(&(word[3][0]),"do");
strcpy(&(word[4][0]),"else");/*增加单词:
保存字else*/
strcpy(&(word[5][0]),"end");
strcpy(&(word[6][0]),"for");/*增加单词:
保存字for*/
strcpy(&(word[7][0]),"if");
strcpy(&(word[8][0]),"odd");
strcpy(&(word[9][0]),"procedure");
strcpy(&(word[10][0]),"read");
strcpy(&(word[11][0]),"return");/*增加单词:
保存字return*/
strcpy(&(word[12][0]),"step");/*增加单词:
保存字step*/
strcpy(&(word[13][0]),"then");
strcpy(&(word[14][0]),"while");
strcpy(&(word[15][0]),"write");
wsym[0]=beginsym;
wsym[1]=callsym;
wsym[2]=constsym;
wsym[3]=dosym;
wsym[4]=elsesym;/*else*/
wsym[5]=endsym;
wsym[6]=forsym;/*for*/
wsym[7]=ifsym;
wsym[8]=oddsym;
wsym[9]=procsym;
wsym[10]=readsym;
wsym[11]=returnsym;/*return*/
wsym[12]=stepsym;/*step*/
wsym[13]=thensym;
wsym[14]=whilesym;
wsym[15]=writesym;
3、增加四个运算符:
+=,-=,++,--,∧,∨,┓
●PL0.cpp
getsym();
增加对+,-,++,--,+=,-=的识别;
Statement();
增加对+,-,++,--,-=的语句的处理;
Init〔〕中改动:
ssym[‘&’]=and;
ssym[‘|’]=or;
ssym[‘~’]=not;
facbegsys[plusone]=true;//添加前自加运算
facbegsys[minusone]=true;//添加前自减运算
Getsym〔〕增加的内容:
intgetsym()
{
inti,j,k;
while(ch==''||ch==10||ch==9)
{
getchdo;
}
if(ch>='a'&&ch<='z')
{
k=0;
do{
if(k{
a[k]=ch;
k++;
}
getchdo;
}while(ch>='a'&&ch<='z'||ch>='0'&&ch<='9');
a[k]=0;
strcpy(id,a);
i=0;
j=norw-1;
do{
k=(i+j)/2;
if(strcmp(id,word[k])<=0)
{
j=k-1;
}
if(strcmp(id,word[k])>=0)
{
i=k+1;
}
}while(i<=j);
if(i-1>j)
{
sym=wsym[k];
}
else
{
sym=ident;
}
}
else
{
if(ch>='0'&&ch<='9')
{
k=0;
num=0;
sym=number;
do{
num=10*num+ch-'0';
k++;
getchdo;
}while(ch>='0'&&ch<='9');/*获取数字的值*/
k--;
if(k>nmax)
{
error(30);
}
}
else
{
if(ch==':
')/*检测赋值符号*/
{
getchdo;
if(ch=='=')
{
sym=becomes;
getchdo;
}
else
{
sym=nul;/*不能识别的符号*/
}
}
else
{
if(ch=='<')/*检测小于或小于等于符号*/
{
getchdo;
if(ch=='=')
{
sym=leq;
getchdo;
}
else
{
sym=lss;
}
}
elseif(ch=='>')/*检测大于或大于等于符号*/
{
getchdo;
if(ch=='=')
{
sym=geq;
getchdo;
}
else
{
sym=gtr;
}
}
/*这里之间为添加的内容*/
elseif(ch=='+'){/*检测+,+=,++符号*/
getchdo;
if(ch=='='){
sym=pluseq;
getchdo;
}elseif(ch=='+'){
sym=plusone;
getchdo;
}else{
sym=plus;
}
}elseif(ch=='-'){/*检测-,-=,--符号*/
getchdo;
if(ch=='=')
{
sym=minuseq;
getchdo;
}
elseif(ch=='-')
{
sym=minusone;
getchdo;
}
else
{
sym=minus;
}
}
/*这里之间为添加的内容*/
else{
sym=ssym[ch];/*当符号不满足上述条件时,全部按照单字符号处理*/
//getchdo;
//richard
if(sym!
=period)
{
getchdo;
}
//endrichard
}
}
}
}
return0;
}
Statement()增加的内容:
(将本来“if(sym==becomes)……〞局部的内容修改为处理++,+=,--,-=),并在Statement()中定义变量intsym2;
if(sym==becomes||sym==pluseq||sym==minuseq||
sym==plusone||sym==minusone)
{
sym2=sym;
getsymdo;
gendo(lod,lev-table[i].level,table[i].adr);
}
else
{
error(13);
}
if(sym2==plusone||sym2==minusone)/*准备按照a++、a--语句
处理,与read类似*/
{
if(i!
=0)
{
if(sym2==plusone)
{
gendo(lit,0,1);
gendo(opr,0,2);
gendo(sto,lev-table[i].level,table[i].adr);
}
if(sym2==minusone)
{
gendo(lit,0,1);
gendo(opr,0,3);
gendo(sto,lev-table[i].level,table[i].adr);
}
}
}
else
{
memcpy(nxtlev,fsys,sizeof(bool)*symnum);
expressiondo(nxtlev,ptx,lev);
if(i!
=0)
{
if(sym2==becomes)
gendo(sto,lev-table[i].level,table[i].adr);
if(sym2==pluseq)
{
gendo(opr,0,2);
gendo(sto,lev-table[i].level,table[i].adr);
}
if(sym2==minuseq)
{
gendo(opr,0,3);
gendo(sto,lev-table[i].level,table[i].adr);
}
}
}//else
}
}
}
4、修改单词:
不等号#改为<>
●PL0.cpp
init();
移除:
ssym['#']=neq;
在getsym()里增加对<>的识别(在<或<=根底上修改)。
下面为在<根底上修改,注意在if(ch==’<’)中修改,不包括elseif(ch=='>')那局部:
if(ch=='<')/*检测小于或小于等于符号*/
{
getchdo;
if(ch=='=')
{
sym=leq;
getchdo;
}
/*在之间添加*/
elseif(ch=='>')//addneq
{
sym=neq;
getchdo;
}
/*在之间添加*/
else
{
sym=lss;
}
}
5、增加条件语句的ELSE子句
●PL0.cpp
在statement()里的“if...then〞语句处理的根底上添加对else子句的处理,使之能处理if……then……else……的语句。
else
{
if(sym==ifsym)/*准备按照if语句处理*/
{
getsymdo;
memcpy(nxtlev,fsys,sizeof(bool)*symnum);
nxtlev[thensym]=true;
nxtlev[dosym]=true;/*后跟符号为then
或do*/
conditiondo(nxtlev,ptx,lev);/*调用条
件处理〔逻辑运算〕函数*/
if(sym==thensym)
{
getsymdo;
}
else
{
error(16);/*缺少then*/
}
cx1=cx;/*保存当前指令地
址*/
gendo(jpc,0,0);/*生成条件跳转指
令,跳转地址暂写0*/
/*这里之间开场添加*/memcpy(nxtlev,fsys,sizeof(bool)*symnum);
//添加后跟符号
nxtlev[elsesym]=true;
statementdo(nxtlev,ptx,lev);/*处理
then后的语句*/
code[cx1].a=cx;/*经statement处理后
,cx为then后语句执行完的位置,它正是前面未定的跳转地址*/
if(sym==elsesym)
{
cx2=cx;
getsymdo;
gendo(jmp,0,0);
code[cx1].a=cx;
statementdo(fsys,ptx,lev);
code[cx2].a=cx;
}/*这里之间开场添加*/
}
else
{
if(sym==beginsym)/*准备按照复合语句处理*/
{
getsymdo;
memcpy(nxtlev,fsys,sizeof(bool)*symnum);
nxtlev[semicolon]=true;
nxtlev[endsym]=true;/*后跟符号为分号或
end*/
/*循环调用语句处理函数,直到下一个符号不
是语句开场符号或收到end*/
statementdo(nxtlev,ptx,lev);
while(inset(sym,statbegsys)||
sym==semicolon)
{
if(sym==semicolon)
{
getsymdo;
}
else
{
error(10);/*缺少分号*/
}
statementdo(nxtlev,ptx,lev);
}
if(sym==endsym)
{
getsymdo;
}
else
{
error(17);/*缺少end或分
号*/
}
}
写出相关文法:
G(S):
S→ifSelseS|ifS|a
Else语法图:
三、概述
源语言:
PL/0语言
目的语言:
假想栈式计算机的汇编语言
实现工具:
VC++6.0运行平台:
Windows7
四、构造设计说明
1、PL/0编译程序的构造图如下:
由于PL/0编译程序采用一趟扫描方法,所以语法语义分析过程block是整个编译程序的核心。
下面给出编译程序的总体流程图,以弄清block过程在整个编译程序中的作用。
在流程图中可以看出,主程序置初值后先调整用读单词过程getsym取一个单词,然后再调用语法分析过程block,直到遇源程序的完毕符“.〞为止。
五、各功能模块描绘
GetSym():
词法分析,从源文件中读出假设干有效字符,组成token串,识别它的类型为保存字/标识符/数字或其它符号。
GEN():
目的代码生成过程,本过程用于把生成的目的代码写入目的代码数组,供后面的解释器解释执行.
TEST():
测试当前单词是否合法
ENTER():
在名字表中参加一项
POSITION():
查找名字的位置,找到那么返回名字表中的位置,否那么返回0
ConstDeclaration():
常量声明处理
VarDeclaration():
变量声明处理
ListCode():
输出目的代码清单
FACTOR():
因子处理过程
TERM():
项处理过程
EXPRESSION():
表达式处理过程
CONDITION():
条件处理过程
STATEMENT():
语句处理过程
Block():
编译程序主体,参数:
lev:
这一次语法分析所在的层次,tx:
符号表指针,
fsys:
用于出错恢复的单词集合
BASE():
通过过程基址求上一层过程的基址
Interpret():
解释程序,PL/0编译器产生的类PCODE目的代码解释运行过程
六、主要成份描绘
1.符号表
在编译程序中符号表用来存放语言程序中出现的有关标识符的属性信息,这些信息集中反映了标识符的语义特征属性.符号表的主要功能如下:
、搜集符号属性
、上下文语义合法性检查的根据
、作为目的代码生成阶段地址分配的根据.
、符号表的数据构造:
structtablestruct
{
charname[al];/*名字*/
enumobjectkind;/*类型:
const,var,arrayorprocedure*/
intval;/*数值,仅const使用*/
intlevel;/*所处层,仅const不使用*/
intadr;/*地址,仅const不使用*/
intsize;/*需要分配的数据区空间,仅procedure使用*/
};
structtablestructtable[txmax];/*名字表*/
2.运行时的存储组织和管理
当源程序经过语法分析,假设未发现错误时,由编译程序调用解释程序,对存放在CODE中的代码CODE[0]开场进展解释执行.当废弃完毕后,记录源程序中标识符的TABLE表已没有作用.因此存储区只需以数组CODE存主的只读目的程序和运行机制时的数据区S,S是由解释程序定义的一维整数型数组.解释执行时的数据空间S为栈式计算机的在座空间,遵循后进先出规那么,对每个过程(包括主程序)当调用时,才分配数据空间,退出过程进,那么所分配原那么的数据空间被释放.
解释程序还定义了4个存放器:
1、指令存放器.存放当前正在解释的一条目的指令2、程序地址存放器.指向下一条要执行的目的程序的地址3、栈顶存放器.4、基址存放器.指向每个过程被调用时,在数据区S中给它分配原那么的数据段起始地址,也称基地址.
为了实现过程被调用时给它分配数据段,过程运行完毕后释放数据段以及嵌套过程之间结标志符引用的问题,当过程被调用时,在栈顶分配三个联络单元,这三个联络单元存放的内容分别为:
SL静态链,动态链DL,RA返回地址。
3.语法分析方法
语法分析的任务是识别由词法分析给出的单词符号序列在构造上是否符合给定的文法规那么.PL/0编译程序的语法分析采用了自顶向下的递归子程序法.粗略地说:
就是对应每个非终结符语法单元,编一个独立的处理过程(或子程序).语法分析研究从读入第一个单词开场由非终结符’程序即开场符出发,沿语法描绘图箭头所指出的方向进展分析.当遇到非终结符时,那么调用相应的处理过程,从语法描绘图看也就进入了一个语法单元,再沿当前所进入的语法描绘图的箭头方向进展分析,当遇到描绘图中是终结符时,那么判断当前读入的单词是否与图中的终结符相匹配,假设匹配,那么执行相应的语义程序(就是翻译程序).再读取下一个单词继续分析.遇到分支点时将当前的单词与分支点上的多个终结符逐个相比拟,假设都不匹配时可能是进入下一非终结符语法单位或是出错.
假设一个PL/0语言程序的单词序列在整修语法分析中,都能逐个得到匹配,直到程序完毕’.’,这时就说所输入的程序是正确的.对于正确的语法分析做相应的语义翻译,最终得出目的程序.
4.中间代码表示
PL/0编译程序所产生的目的代码是一个假想栈式计算机的汇编语言,可称为类pcode指令代码,它不依赖任何实际计算机,其指令集极为简单,指令格式如下:
f
L
a
lit0a
将常数值取到栈顶,a为常数值
Lodla
将变量值取到栈顶,a为偏移量,l为层差
Stola
将栈顶内容送入某变量单元中,a为偏移量,l为层差
Cala
调用过程,a为过程地址,l为层差
Int0a
在运行栈中为被调用的过程开拓a个单元的数据区
jmp0a
无条件跳转至a地址
Jpc0a
条件跳转,当栈顶布尔值非真那么跳转至a地址,否那么顺序执行
opr00
过程调用完毕后,返回调用点并退栈