编译原理课设报告2.docx

上传人:b****3 文档编号:24719141 上传时间:2023-05-31 格式:DOCX 页数:24 大小:164.04KB
下载 相关 举报
编译原理课设报告2.docx_第1页
第1页 / 共24页
编译原理课设报告2.docx_第2页
第2页 / 共24页
编译原理课设报告2.docx_第3页
第3页 / 共24页
编译原理课设报告2.docx_第4页
第4页 / 共24页
编译原理课设报告2.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

编译原理课设报告2.docx

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

编译原理课设报告2.docx

编译原理课设报告2

 

编译原理课程设计

题目:

pl/0编译程序的改进与完善

 

学生所在学院:

信息科学与工程学院

学生所在班级:

06级计算机软件1班

学生姓名:

学生学号:

指导教师:

张世辉

一、课设目的:

1.阅读、研究、改进、设计和调试一个简单的编译程序;

2.加深对编译程序理论和编译过程的理解。

二、课设内容:

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.虚拟机代码

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

lda,sta,}

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

4.名字表中的类型

enumobject{constant,variable,procedure,arrays,}

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

5.名字表结构

structtablestruct{

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

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

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

6.错误信息表

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

=

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

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

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

(5)漏掉了,或;

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

(7)应是语句开始符

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

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

(10)语句之间漏了;

(11)标识符未说明

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

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

(14)call后应为标识符

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

(16)条件语句中丢了then

(17)丢了end或;

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

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

(20)应为关系运算符

(21)表达式内标识符属性不能是过程

(22)表达式中漏掉右括号)

(23)因子后的非法符号

(24)表达式的开始符不能使此符号

(31)数越界

(32)read语句括号中的标识符不是变量

补充错误处理:

(50)数组格式错误

(51)数组访问错误

7.语法描述图:

图3程序语法描述图

图4分程序语法描述图

图5语句语法描述图

图6条件语法描述图

图7表达式语法描述图

图8项语法描述图

图9因子语法描述图

四、功能扩充

1.在语句处理中增加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出错

}}

注意:

repeat是可以作为语句的开始符,所有要在初始化函数中的语句开始符中添加statbegsys[repeatsym]=true;语句。

2.语句处理中加入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;

}}}}}

3.语句处理中加入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;

4.扩充++和—运算符

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

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++和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。

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);

end.

3.测试if-then-else功能测试文件:

3.txt测试结果:

vars,i,n;

begin

i:

=4;

s:

=0;

n:

=7;

ifi>nthens:

=i

elses:

=n;

write(s);

end.

4.测试for功能

测试文件:

5.txt测试结果:

vars,i,n;

begin

s:

=0;

n:

=1;

for(i:

=1;i<=5;i:

=i+1)

begin

s:

=s+2;

n:

=n*i;

end;

write(s);

write(n);

end.

5.测试数组:

测试文件6.txt:

测试结果:

varb,a[1:

2];

begin

b:

=2;

a[1]:

=2;

a[2]:

=a[1]+b;

write(b);

write(a[1]);

write(a[2]);

end.

6.综合测试:

测试文件7.txt:

测试结果:

vara[1:

2],i;

begin

a[1]:

=1;

i:

=0;

a[2]:

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

当前位置:首页 > IT计算机

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

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