编译原理课设报告最终版.docx

上传人:b****3 文档编号:3755249 上传时间:2022-11-25 格式:DOCX 页数:22 大小:219.65KB
下载 相关 举报
编译原理课设报告最终版.docx_第1页
第1页 / 共22页
编译原理课设报告最终版.docx_第2页
第2页 / 共22页
编译原理课设报告最终版.docx_第3页
第3页 / 共22页
编译原理课设报告最终版.docx_第4页
第4页 / 共22页
编译原理课设报告最终版.docx_第5页
第5页 / 共22页
点击查看更多>>
下载资源
资源描述

编译原理课设报告最终版.docx

《编译原理课设报告最终版.docx》由会员分享,可在线阅读,更多相关《编译原理课设报告最终版.docx(22页珍藏版)》请在冰豆网上搜索。

编译原理课设报告最终版.docx

编译原理课设报告最终版

编译原理课程设计

PL0ExperimentReport

(燕山大学信息科学与工程学院)

姓名班级:

计算机科学与技术

学生学号:

课程名称:

编译原理

指导教师:

2015年12月24日

一、设计目的

研究、改进或自行设计、开发一个简单的编译程序或其部分功能,加深对编译理论和编译过程的理解。

编程语言不限。

二、设计任务

扩展PL/0编译程序功能

目的:

扩充PL/0编译程序功能

要求:

(1)阅读、研究PL/0编译程序源文件。

(2)在上述工作基础上,可有选择地补充、完善其中词法分析、语法分析、语义分析、目标代码生成、目标代码解释执行等部分的功能。

如以语法分析部分为例,则可以增加处理更多语法成分的功能,如可处理一维数组、++、--、+=、-=、*=、/=、%(取余)、!

(取反)、repeat、for、else、开方、处理注释、错误提示、标示符或变量中可以有下划线等。

还可以增加类型,如增加字符类型、实数类型;扩充函数如有返回值和返回语句的,有参数函数等;

(3)设计编制典型的运行实例,以便能反映出自己所作的改进。

 

三、设计思想:

PL/0语言可以看成PASCAL语言的子集,它的编译程序是一个编译解释执行系统。

PL/0的目标程序为假想栈式计算机的汇编语言,与具体计算机无关。

PL/0的编译程序和目标程序的解释执行程序都是用PASCAL语言书写的,因此PL/0语言可在配备PASCAL语言的任何机器上实现。

其编译过程采用一趟扫描方式,以语法分析程序为核心,词法分析和代码生成程序都作为一个独立的过程,当语法分析需要读单词时就调用词法分析程序,而当语法分析正确需要生成相应的目标代码时,则调用代码生成程序。

用表格管理程序建立变量、常量和过程表示符的说明与引用之间的信息联系。

当源程序编译正确时,PL/0编译程序自动调用解释执行程序,对目标代码进行解释执行,并按用户程序的要求输入数据和输出运行结果。

四、设计内容:

1扩充语句for(<语句>;<条件>;<语句>)<语句>;

2扩充语句if<条件>then<语句>else<语句>;

3扩充语句repeat<语句>;until<条件>;

4增加自增自减运算++和—和+=,-=运算;

5修改不等号#,为!

=;

6增加一维数组

声明格式:

[/:

/];

赋值格式:

[]:

=<表达式>;

调用格式:

[]

五、程序结构:

PL/0源程序

图1编译程序结构图2功能模块调用

1.功能模块作用如下:

Pl0.c:

主程序

Error:

出错处理,打印出错位置和错误编码

Getsym:

词法分析,读取一个单词

Getch:

漏掉空格,读取一个字符

Gen:

生成目标代码,并送入目标程序区

Test:

测试当前符号是否合法

Block:

分程序分析处理过程,词法语法分析

Enter:

登陆名字表

Position:

查找标识符在名字表中的位置

Constdeclaration:

常量定义处理

Vardeclaraction:

变量说明处理

Listcode:

列出目标代码清单

Statement:

语句处理

Expression:

表达式处理

Term:

项处理

Factor:

因子处理

Condition:

条件处理

Interpret:

对目标代码的解释执行程序

Base:

通过静态链求出数据取得基地址

增加两个功能:

Arraydeclaration:

数组声明处理

Arraycoef:

数组索引计算和“虚拟机”动作生成

2.保留字:

enumsymbol{nul,ident,number,plus,minus,

times,slash,oddsym,eql,neq,

lss,leq,gtr,geq,lparen,

rparen,comma,semicolon,period,becomes,

beginsym,endsym,ifsym,thensym,elsesym,

forsym,inc,dec,whilesym,writesym,

readsym,dosym,callsym,constsym,varsym,

procsym,repeatsym,untilsym,plusbk,minusbk,

lbrack,rbrack,colon,}

共43个,其中补充保留字为:

else,for,repeat,until,plusbk,minusbk,

Lbrack,rbrack,colon

3.名字表中的类型

enumobject{constant,variable,procedure,arrays,}

共4个,扩充arrays,以便实现数组

4.虚拟机代码

enumfct{lit,opr,lod,sto,cal,inte,jmp,jpc,

lda,sta,}

共10个,补充的lda,sta用于数组操作

6.错误信息

(1)const,var,procedure后应为标识符

(2)常数说明中的=后应是数字

(3)常数说明中的标识符后应是=

(4)常数说明中的=写成了:

=

(5)漏掉了,或;

(6)过程说明后的符号不正确(应是语句开始符,或过程定义符)

(7)应是语句开始符

(8)标识符未说明

(9)程序结尾丢了句号。

(10)语句之间漏了;

(11)call后应为标识符

(12)赋值语句中,赋值号左部标识符属性应是变量

(13)赋值号左部标识符属性应是赋值号

(14)程序体内语句部分的后跟符不正确

(15)call后标识符属性应为过程

(16)条件语句中丢了then

(17)丢了end或;

(18)while循环语句中丢了do

(19)语句后的符号不正确

6.名字表结构

structtablestruct{

charname[al];enumobjectkind;intval;intlevel;intadr;intsize;

//扩充名字表结构,增加一个data域保存数组的下界

intdata;/*其他数据,对arrays来说是下界*/}

7.语法描述图:

图3程序语法描述图

图4分程序语法描述图

图5语句语法描述图

图6条件语法描述图

图7表达式语法描述图

图8项语法描述图

图9因子语法描述图

四、功能扩充

1.语句处理中加入for循环语句

if(sym==forsym){

getsymdo;

if(sym!

=lparen)error(34);//没有左括号出错

else{

getsymdo;

statementdo(nxtlev,ptx,lev);//S1代码

if(sym!

=semicolon)error(10);//语句缺少分号出错

else{

cx1=cx;

getsymdo;

conditiondo(nxtlev,ptx,lev);//E代码

if(sym!

=semicolon)error(10);//语句缺少分号出错

else{

cx2=cx;

gendo(jpc,0,0);

cx3=cx;

gendo(jmp,0,0);

getsymdo;

cx4=cx;

statementdo(nxtlev,ptx,lev);//S2代码

if(sym!

=rparen)error(22);//缺少右括号出错

else{

gendo(jmp,0,cx1);

getsymdo;

cx5=cx;

statementdo(nxtlev,ptx,lev);//S3代码

code[cx3].a=cx5;

gendo(jmp,0,cx4);

code[cx2].a=cx;

}}}}}

2.在语句处理中增加repeat-until语句

if(sym==repeatsym){

cx1=cx;

getsymdo;

statementdo(nxtlev,ptx,lev);

if(sym==untilsym){

getsymdo;

conditiondo(nxtlev,ptx,lev);

cx2=cx;

gendo(jpc,0,0);

code[cx2].a=cx1;}

elseerror(33);//没有写until出错

}}

3.扩充++和—运算符

对于++和--运算符,扩充时要注意存在两个情况:

1)作为语句的时候;2)作为表达式中的因子的时候。

注意:

扩充时增加因子开始符facbegsys[incs]=true和facbegsys[decs]=true。

扩充的语法描述见结构设计中的PL/0分程序和主要语句的语法描述中的描述图,详细代码见程序。

1)作为语句的时候,有四种情况:

a++;a--;++a;--a;文法的EBNF表示形式为:

<自增自减语句>:

:

=<标识符>[++|--]|[++|--]<标识符>

文法分析过程大体如下图:

++a和—aa++和a—

生成中间代码对于a++;++a;和a--;--a;语句的处理如下:

先将变量的值取出放在栈顶,后将1入栈,后执行加法或减法运算oprv指令的2(加法)、3(减法),后将运算后的栈顶值存回变量。

a++;和++a;语句的中间代码:

lod03;lit01;opr02;sto03;

a--;和--a;语句的中间代码:

lod03;lit01;opr03;sto03;

2)作为因子的时候,有两种情况:

a++和a--作为因子,比如:

b:

=a++*a--;语句++a和--a作为因子,比如:

b:

=--a+2*++a;语句

文法的EBNF表示形式为:

<表达式>:

:

=...[++|--]<标识符>|<标识符>[++|--]...其中的...表示前后都可以有其他的项或因子生成中间代码

A对于因子++a和--a的中间代码生成处理和a++;等语句处理一样;B对于因子a++和a—的中间代码生成处理如下:

a++:

lod03;lit01;opr02;sto03;lod03;lit01;opr03;

a--:

lod03;lit01;opr03;sto03;lod03;lit01;opr02;先将变量的值取出放在栈顶,后将1入栈,后执行加法或减法运算opr指令的2(加法)、3(减法),后将运算后的栈顶值存回变量,后将变量的值又取出来放入栈顶,后将1入栈,如果是a++就执行减法,如果是a—就执行加法,以实现先用a的值后再加1。

4.语句处理中加入if-then-else语句

在原有程序if(sym==then){...}后加入下列代码:

cx1=cx;

gendo(jpc,0,0);

statementdo(fsys,ptx,lev);

if(sym==elsesym){

getsymdo;

cx2=cx;

gendo(jmp,0,0);

code[cx1].a=cx;

statementdo(fsys,ptx,lev);

code[cx2].a=cx;}

else

code[cx1].a=cx;

5.修改不等号#为!

=

注释源程序中的ssym['#']=neq语句,在getsym中加入下列代码:

//修改不等号为!

=

elseif(ch=='!

'){

getchdo;

if(ch=='='){

sym=neq;

getchdo;}

elsesym=nul;}

6.加入对一维数组的支持

本程序将数组看做变量的一种,由var声明函数调用array声明函数完成数组声明,这样就处加入文件输出的相关语句外,可以完全保留block函数和enter函数;通过改写factor函数使数组因子包括了后缀的索引号,这样就可以调用通用的表达式函数赋值数组了。

为了方便完成数组相关功能,扩充了虚拟机处理代码。

数组的越界及非法调用错误处理没有完善,仅给出了错误代码。

在头文件pl0.h中:

/*定义两个全局变量,用来保存数组定义的下界和容量*/

staticintg_arrBase=0;

staticintg_arrSize=0;

/*虚拟机代码*///增加lda,sta专门由于数组的处理

//增加两个虚拟机指令lda,sta,分别用来从数组中取数和存到数组中

//数组元素的访问和存储,是将()后的当成表达式,先处理,得到元素的索引,放在栈顶

//最后根据数组的首地址,得到某个元素的地址

enumfct{...lda,sta}

//扩充名字表结构,增加一个data域保存数组的下界

structtablestruct{...intdata;/*其他数据,对arrays来说是下界*/}

/*名字表中的类型*/

enumobject{...arrays//添加数组类型}

//数组声明处理,下界和上界允许已经定义过的常量标识符

intarraydeclaration(int*ptx,intlev,int*pdx);

//数组元素索引计算与“虚拟机”生成

intarraycoef(bool*fsys,int*ptx,intlev);

在源程序文件pl0.c中:

编写相关的arraydeclaration,arraycoef两个功能函数:

/*数组声明处理,下界和上界允许已经定义过的常量标识符*/

intarraydeclaration(int*ptx,intlev,int*pdx){

chararrId[al];/*暂存数组标识名,避免被覆盖*/

intcstId;/*常量标识符的位置*/

intarrBase=-1,arrTop=-1;/*数组下界、上界的数值*/

getsymdo;

if(sym==lbrack){/*标识符之后是'[',则识别为数组*/

strcpy(arrId,id);

/*检查下界*/

getsymdo;

if(sym==ident){

if((cstId=position(id,(*ptx)))!

=0)

arrBase=(constant==table[cstId].kind)?

table[cstId].val:

-1;}

elsearrBase=(sym==number)?

num:

-1;

if(-1==arrBase){

error(50);

return-1;}

/*检查冒号*/

getsymdo;

if(sym!

=colon){

error(50);

return-1;}

/*检查上界*/

getsymdo;

if(sym==ident){

if((cstId=position(id,(*ptx)))!

=0)

arrTop=(constant==table[cstId].kind)?

table[cstId].val:

-1;}

elsearrTop=(number==sym)?

num:

-1;

if(arrTop==-1){

error(50);//随意指定,因为原程序对错误号的规划极差!

return-1;}

/*检查']'*/

getsymdo;

if(sym!

=rbrack){

error(50);

return-1;}

/*上下界是否符合条件检查*/

g_arrSize=arrTop-arrBase+1;

g_arrBase=arrBase;

if(g_arrSize<=0){

error(50);

return-1;}

/*恢复数组的标识符*/

strcpy(id,arrId);

return1;}

return0;}

/*数组元素索引计算与“虚拟机”生成*/

intarraycoef(bool*fsys,int*ptx,intlev){

boolnxtlev[symnum];

inti=position(id,*ptx);

getsymdo;

if(sym==lbrack){/*索引是括号内的表达式*/

getsymdo;

memcpy(nxtlev,fsys,sizeof(bool)*symnum);

nxtlev[rbrack]=true;

expressiondo(nxtlev,ptx,lev);

if(sym==rbrack){

gendo(lit,0,table[i].data);

gendo(opr,0,3);/*系数修正,减去下界的值*/

return0;}

elseerror(22);/*缺少右括号*/

}

elseerror(51);/*数组访问错误*/

return-1;}

修改函数enter,block,vardeclaration,factor及statement,使其具备处理数组的功能:

//将数组变量登陆名字表

voidenter(enumobjectk,int*ptx,intlev,int*pdx){

...

casearrays:

/*数组名,进行记录下界等*/

table[(*ptx)].level=lev;

table[(*ptx)].adr=(*pdx);

table[(*ptx)].data=g_arrBase;

table[(*ptx)].size=g_arrSize;

*pdx=(*pdx)+g_arrSize;

break;

...}

//输出数组名字表到控制台和文件fas.tmp

intblock(intlev,inttx,bool*fsys){

...

casearrays:

printf("%darray%s",i,table[i].name);

printf("lev=%daddr=%dsize=%d\n",table[i].level,table[i].adr,table[i].size);

fprintf(fas,"%darray%s",i,table[i].name);

fprintf(fas,"lev=%daddr=%dsize=%d\n",table[i].level,table[i].adr,table[i].size);

//加入数组声明

intvardeclaration(int*ptx,intlev,int*pdx){

intarrayRet=-1;

if(sym==ident){

arrayRet=arraydeclaration(ptx,lev,pdx);/*先判断数组*/

switch(arrayRet){

case1:

enter(arrays,ptx,lev,pdx);//填写数组名

getsymdo;

break;

case0:

enter(variable,ptx,lev,pdx);//填写名字表

//getsymdo;

break;

default:

return-1;/*数组定义解析出错*/}

}

elseerror(4);/*var后应是标识*/

return0;}

/*当因子是数组型变量时,调用arraycodefdo将数组的索引入栈顶,之后按vatiabler变量操作*/

intfactor(bool*fsys,int*ptx,intlev){

...

switch(table[i].kind){

...

casearrays:

/*名字为数组名*/

arraycoefdo(fsys,ptx,lev);

gendo(lda,lev-table[i].level,table[i].adr);/*找到变量地址并将其值入栈*/

...}...}

intstatement(bool*fsys,int*ptx,intlev){

...

if(sym==ident){

...

if((table[i].kind!

=variable)&&(table[i].kind!

=arrays)){

error(12);i=0;}

else{

enumfctfct1=sto;switch(table[i].kind){

casearrays:

arraycoefdo(fsys,ptx,lev);

fct1=sta;/*数组保存,要多读一个栈*/

casevariable:

{...}

}}}

//增加的两个虚拟机代码的处理:

lda,sta

voidinterpret(){

...

caselda:

/*数组元素访问,当前栈顶为元素索引,执行后,栈顶变成元素的值*/

s[t-1]=s[base(i.l,s,b)+i.a+s[t-1]];

break;

casesta:

/*栈顶的值存到数组中,索引为次栈顶*/

t-=2;

s[base(i.l,s,b)+i.a+s[t]]=s[t+1];

break;...}

五、调试及运行结果

1.测试repeat...until...语句功能测试文件:

4.txt测试结果:

vara,b,n;

begin

b:

=4;

a:

=1;

read(n);

repeat

a:

=a+1;

b:

=b+1;

untila>n;

write(a);

write(b);

end.

当输入的n为3时,repeat...until...语句中的循环体执行3次,所以a=4,b=72.测试增加的++,--功能

测试文件:

2.txt测试结果:

vara,b;

begin

a:

=1;

b:

=3;

a++;

b--;

write(a);

write(b);

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1