编译原理大作业设计.docx

上传人:b****8 文档编号:10337043 上传时间:2023-02-10 格式:DOCX 页数:18 大小:44.32KB
下载 相关 举报
编译原理大作业设计.docx_第1页
第1页 / 共18页
编译原理大作业设计.docx_第2页
第2页 / 共18页
编译原理大作业设计.docx_第3页
第3页 / 共18页
编译原理大作业设计.docx_第4页
第4页 / 共18页
编译原理大作业设计.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

编译原理大作业设计.docx

《编译原理大作业设计.docx》由会员分享,可在线阅读,更多相关《编译原理大作业设计.docx(18页珍藏版)》请在冰豆网上搜索。

编译原理大作业设计.docx

编译原理大作业设计

 

编译原理大作业

题目C语言编译器的设计与

实现

 

专业计算机科学与技术系

班  号0103111班

学生姓名刘岩

指导教师姓名

答辩日期2004年7月

 

不要删除行尾的分节符,此行不会被打印

目录

第1章编译器概述2

1.1编译器概述2

第2章编译器设计4

2.1词法分析设计4

2.1语法分析设计6

2.1中间代码生成9

2.1语法翻译实现方法11

第3章数据结构说明12

3.1数据结构说明12

第4章编译程序运行测试15

4.1编译程序运行测试15

参考文献18

第1章编译器概述黄金分割的哈工大回归热大【】个阿訇妇孺诶航天拖后腿航天皇太后

1.1编译器概述

编译器的任务是:

输入*.asm源程序文件,输出*.obj目标代码文件和*.lst列表文件——对输入的源程序进行扫描,找出所有词法和语法的错误,然后生成有待重定位的目标代码(中间文件)和提供查看信息的列表文件。

中间文件后缀名为obj,是连接器的输入文件。

一个编译器的整个工作流程是划分成一个一个阶段进行的,每个阶段都将源程序的一种表示形式转换成另一种表示形式,各个阶段进行的操作在逻辑上是紧密联结在一起的。

这几个阶段是:

词法分析、语法分析、语义分析、中间代码生成。

另外两个重要的工作:

符号表格的管理和出错处理贯穿以上的所有阶段。

编译过程中源程序的各种信息被保留在种种不同的表格里。

编译时自始至终涉及到表格的构造、查找和更新。

如果编译过程中发现源程序有错误,编译程序应报告错误的性质和错误发生的地点,并且将错误所造成的影响限制在尽可能小的范围内,使得源程序的其余部分能继续被编译下去,有些地方编译器还能自动校正错误,这些工作称之为出错处理。

词法分析

自左至右扫描源程序的字符串,并识别出一个个的单词(也称单词符号)。

这里所谓的单词是指逻辑上紧密相连的按词的组成规则结合起来的一组字符,它们具有一定的意义。

把每个单词的ASCII码序列替换成所谓的机内表示——Token形式。

这时还需要检查词法错误。

词法分析阶段不依靠语法关系。

语法分析

扫描对象可能是源程序的ASCII码序列,也可能是词法分析后的Token序列。

前者情形,词法分析程序作为语法分析程序的子程序。

语法分析的主要任务是检查源程序的形式语法错误。

每当发现错误时将输出有关信息。

很多编译程序在进行语法分析时同时完成其他工作,但要注意,如果语法有错误,那么其他工作也就白做。

语义分析

扫描的对象通常是语法分析后的结果。

这时候,源程序的Token序列已经变换成没有错误的符合语法的Token生成树。

对于编译器来说,它的任务是收集符号信息,登记在符号表格里,对伪指令的语义进行解释,完成相应的动作。

伪指令的功能是改变编译器的状态并将一些必要的信息(如段定义,变量声明)加入到目标文件中去。

另外,表达式的求值,段的选择,地址计数器的计数,指令长度的计算等都在这个阶段中完成。

语义分析阶段同样进行着对错误的处理。

目标代码生成

这时候的Token流在形式上已经比较一致,符号信息都已经登记在各种表格里。

这个阶段的任务是根据助记符的各种寻址方式决定它的目标代码。

这一部分的工作与目标机的指令系统紧密相关。

目标代码生成之后,还要根据obj文件的格式把目标代码写入文件。

最后还要产生列表文件,为用户提供源码与目标码的对照。

第2章编译器设计

2.1词法分析设计

由于我们规定的程序语句中涉及单词较少,故在词法分析阶段忽略了单词输入错误的检查,而将编译程序的重点放在中间代码生成阶段。

词法分析器的功能是输入源程序,输出单词符号。

我们规定输出的单词符号格式为如下的二元式:

(单词种别,单词自身的值)

#defineACC-2

#definesyl_if0

#definesyl_else1

#definesyl_while2

#definesyl_begin3

#definesyl_end4

#definea5

#definesemicolon6

#definee7

#definejinghao8

#defines9

#defineL10

#definetempsy11

#defineEA12

#defineEO13

#defineplus14

#definetimes15

#definebecomes16

#defineop_and17

#defineop_or18

#defineop_not19

#definerop20

#definelparent21

#definerparent22

#defineident23

#defineintconst24

函数说明

1.读取函数readline()、readch()

词法分析包含从源文件读取字符的操作,但频繁的读文件操作会影响程序执行效率,故实际上是从源程序文件”in.txt”中读取一行到输入缓冲区,而词法分析过程中每次读取一个字符时则是通过执行readch()从输入缓冲区获得的;若缓冲区已被读空,则再执行readline()从in.txt中读取下一行至输入缓冲区。

2.扫描函数scan()

扫描函数scan()的功能是滤除多余空格并对主要单词进行分析处理,将分析得到的二元式存入二元式结果缓冲区。

3.变量处理find()

变量处理中首先把以字母开头的字母数字串存到spelling[]数组中,然后进行识别。

识别过程是先让它与保留关键字表中的所有关键字进行匹配,若获得成功则说明它为保留关键字,即将其内码值写入二元式结果缓冲区;否则说明其为变量,这时让它与变量名表中的变量进行匹配(变量匹配函数find()),如果成功,则说明该变量已存在并在二元式结果缓冲区中标记为此变量(值填为该变量在变量名表中的位置),否则将该变量登记到变量名表中,再将这个新变量存入二元式缓存数组中。

4.数字识别number()

数字识别将识别出的数字填入二元式结果缓存数组。

5.显示函数disp()

显示函数的功能在屏幕上输出词法分析的结果(即二元式序列程序),同时给出二元式个数及源程序行数统计。

2.2语法分析设计

语法分析器的核心是三张SLR分析表以及针对这三张SLR分析表进行语义加工的语义动作。

编译程序中语法分析处理及四元式生成部分主要是以二元式作为输入,并通过SLR分析表对语法分析处理过程进行控制,使四元式翻译的工作有条不紊的进行,同时识别语法分析中的语法错误。

在处理if和while语句时,需要进行真值或假值的拉链和返填工作,以便转移目标的正确填入。

1.控制语句的SLR分析表1设计如下:

将扩展文法G’

0)S’S

1)SifeSelseS

2)SwhileeS

3)S{L}

4)Sa;

5)LS

6)LSL

staticintaction[20][11]=

/*0*/{{2,-1,3,4,-1,5,-1,-1,-1,1,-1},

/*1*/{-1,-1,-1,-1,-1,-1,-1,-1,ACC,-1,-1},

/*2*/{-1,-1,-1,-1,-1,-1,-1,6,-1,-1,-1},

/*3*/{-1,-1,-1,-1,-1,-1,-1,7,-1,-1,-1},

/*4*/{2,-1,3,4,-1,5,-1,-1,-1,9,8},

/*5*/{-1,-1,-1,-1,-1,-1,10,-1,-1,-1,-1},

/*6*/{2,-1,3,4,-1,5,-1,-1,-1,11,-1},

/*7*/{2,-1,3,4,-1,5,-1,-1,-1,12,-1},

/*8*/{-1,-1,-1,-1,13,-1,-1,-1,-1,-1,-1},

/*9*/{2,-1,3,4,105,5,-1,-1,-1,9,14},

/*10*/{-1,104,-1,-1,104,-1,-1,-1,104,-1,-1},

/*11*/{-1,15,-1,-1,-1,-1,-1,-1,-1,-1,-1},

/*12*/{-1,102,-1,-1,102,-1,-1,-1,102,-1,-1},

/*13*/{-1,103,-1,-1,103,-1,-1,-1,103,-1,-1},

/*14*/{-1,-1,-1,-1,106,-1,-1,-1,-1,-1,-1},

/*15*/{2,-1,3,4,-1,5,-1,-1,-1,16,-1},

/*16*/{-1,101,-1,-1,101,-1,-1,-1,101,-1,-1}};

其中,前9列为action值,后2列为goto值;0~16表示17个移进状态(即Si);-1表示出错;ACC表示分析成功;而100~106对应7个归约产生式:

100S’S

101SifeSelseS

102SwhileeS

103S{L}

104Sa;

105LS

106LSL

2.算术表达式的LR分析表2设计如下:

0)S’E

1)EE+E

2)EE*E

3)E(E)

4)Ei

staticintaction1[10][7]=

/*0*/{{3,-1,-1,2,-1,-1,1},

/*1*/{-1,4,5,-1,-1,ACC,-1},

/*2*/{3,-1,-1,2,-1,-1,6},

/*3*/{-1,104,104,-1,104,104,-1},

/*4*/{3,-1,-1,2,-1,-1,7},

/*5*/{3,-1,-1,2,-1,-1,8},

/*6*/{-1,4,5,-1,9,-1,-1},

/*7*/{-1,101,5,-1,101,101,-1},

/*8*/{-1,102,102,-1,102,102,-1},

/*9*/{-1,103,103,-1,103,103,-1}};

3.布尔表达式的SLR分析表3设计如下:

0)S’B

1)Bi

2)Biropi

3)B(B)

4)B!

B

5)AB&&

6)BAB

7)OB||

8)BOB

staticintaction2[16][11]=

/*0*/{{1,-1,4,-1,5,-1,-1,-1,13,7,8},

/*1*/{1,2,-1,101,-1,101,101,101,-1,-1,-1},

/*2*/{3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},

/*3*/{-1,-1,-1,102,-1,102,102,102,-1,-1,-1},

/*4*/{1,-1,4,-1,5,-1,-1,-1,11,7,8},

/*5*/{1,-1,4,-1,5,-1,-1,-1,6,7,8},

/*6*/{-1,-1,-1,104,-1,9,10,104,-1,-1,-1},

/*7*/{1,-1,4,-1,5,-1,-1,-1,14,7,8},

/*8*/{1,-1,4,-1,5,-1,-1,-1,15,7,8},

/*9*/{105,-1,105,-1,105,-1,-1,-1,-1,-1,-1},

/*10*/{107,-1,107,-1,107,-1,-1,-1,-1,-1,-1},

/*11*/{-1,-1,-1,12,-1,9,10,-1,-1,-1,-1},

/*12*/{-1,-1,-1,103,-1,103,103,103,-1,-1,-1},

/*13*/{-1,-1,-1,-1,-1,9,10,ACC,-1,-1,-1},

/*14*/{-1,-1,-1,106,-1,9,10,106,-1,-1,-1},

/*15*/{-1,-1,-1,108,-1,9,10,108,-1,-1,-1}};

 

2.3中间代码生成

1.布尔表达式

布尔表达式在程序语言中有两个基本作用:

一是用作控制语句(如if-else或while语句)的条件式;二是用于逻辑演算,计算逻辑值。

布尔表达式是由布尔算符(&&、||、!

)作用于布尔变量(或常数)或关系表达式而形成的。

关系表达式的形式是E1ropE2,其中rop是关系符(如<、≤、=、≠、>或≥),E1和E2是算术式。

在这里,我们只考虑前面给定文法所产生的布尔表达式:

B→B&&B|B||B|!

B|(B)|iropi|i

遵照我们的约定,布尔算符的优先顺序(从高到低)为:

!

、&&、||,并假定&&和||都服从左结合规则。

所有关系符的优先级都是相同的,而且高于任何布尔算符,低于任何算术算符,关系算符不得结合。

表达式的真、假出口的确定:

考虑表达式B1||B2,若B1为真,则立即知道B也为真;因此,B1的真出口也就是整个B的真出口。

若B1?

为假,则B2必须被计值,B2的第一个四元式就是B1的假出口。

当然,B2的真、假出口也就是整个B的真、假出口。

类似的考虑适用于对B1&&B2的翻译,我们将B1||B2和B1&&B2的翻译用下图表示,

在自下而上的分析过程中,一个布尔式的真假出口往往不能在产生四元式的同时就填上。

我们只好把这种未完成的四元式的地址(编号)作为B的语义值暂存起来,待到整个表达式的四元式产生完毕之后再来回填这个未填入的转移目标。

2.条件语句 

对条件语句ifeS1elseS2中的布尔表达式e,其作用仅在于控制对S1和S2的选择。

因此,作为转移条件的布尔式e,我们可以赋予它两种“出口”:

一是“真”出T口,出向S1;一是“假”出口,出向S2。

于是,e的代码F条件语句可以翻译成如图的一般形式。

非终结符e具有两项语义值e_TC和e_FC,它们分别指出了尚待回填真、S2的代码假出口的四元式串。

e的“真”出口只有在往回扫描到if时才能知道,而它图3-2条件语句的代码结构的“假”出口则需到处理过S1并且到达else才能明确。

这就是说,必须把e_FC的值传下去,以便到达相应的else时才进行回填。

另外,当S1语句执行完时意味着整个if-else语句也已执行完毕;因此,在S1的编码之后应产生一条无条件转移指令。

这条转移指令将导致程序控制离开整个if-else语句。

但是,在完成S2的翻译之前,这条无条件转移指令的转移目标是不知道的。

甚至,在翻译完S2之后,这条转移指令的转移目标仍无法确定。

这种情形是由于语句的嵌套性所引起的。

例如下面的_语句:

ife1ife2S1elseS2elseS3在S1的代码之后的那条无条件转移指令不仅应跨越S2而且应跨越S3。

这也就是说,转移目标的确定和语句所处的环境密切相关。

3.条件循环语句

条件循环语句whileeS通常被翻译成图的代码结构。

布尔式e的“真”出口出向S代码段的第一个四元式。

紧接S代码段之后应产生一条转向测试e的无条件转移指令。

e的“假”出口将导致程序控制离开整个while语句。

e的“假”出口目标即使在整个while语句翻译完之后也未必明确。

例如:

ife1whilee2S1elseS2这种情况仍是由于语句的嵌套性引起的。

所以,我们只好把它作为语句的语义值S·CHAIN暂留下来,以便在处理外层语句时再伺机回填。

2.4语法翻译实现方法

将上述语法翻译付诸实现过程中,我们仅保留了算术表达式和布尔表达式翻译的文法和语义动作;面对程序语句的翻译,由于改造后含有较多的非终结符且语义动作又相对简单,故仍恢复为改造之前的程序语句文法。

由于总体上构造一个SLR分析表来实现语法分析及语义加工将使得所构造的SLR分析表过大,所以将其分为下面三部分处理:

(1)对算术表达式单独处理,即为算术表达式构造一个SLR分析表,并将赋值语句A=E与算术表达式归为一类处理,处理之后的赋值语句仅看作为程序语句文法中的一个终结符a。

(2)对布尔表达式也单独处理,并为其构造一个SLR分析表,经SLR分析表处理后的布尔表达式看作为程序语句文法中的一个终结符e。

(3)程序语句文法此时变为:

SifeSelseS|whileeS|{L}|a;

LSL|S

此时为程序语句构造相应的SLR分析表就简单多了。

前面的程序语句文法中所添加的非终结符是为了能及时回填有关四元式转移目标而引入的,在取消了这些非终结符后又如何解决及时回填转移目标的问题呢?

我们采取的解决方法是增加两个数组labelmark和labeltemp来分别记录语句嵌套中每一层布尔表达式(如果有的话)e的首地址以及每一层else(如果有的话)之前的四元式地址(即无条件转出此层if语句的四元式)。

也即,对程序语句的翻译来说:

(1)在处理完布尔表达式e后,回填if或while语句的真值链;

(2)在归约完每一个语句S之后检查符号栈,看在S之前的文法符号是否if或while,若是则回填假值链(假值入口为语句S所对应的四元式序列之后;对if语句,此时已在该序列之后加入了一条无条件转移的四元式);

(3)在if语句中,else前面要加入一个无条件转移的四元式转向if语句末尾;在while语句尾要有一个无条件转移四元式转向while语句开头。

第3章数据结构说明

3.1数据结构说明

编译程序中涉及到的数据结构说明如下:

charch='\0';/*从字符缓冲区中读取当前字符*/

intcount=0;/*词法分析结果缓冲区计数器*/

staticcharspelling[10]={""};/*存放识别的字*/

staticcharline[81]={""};/*一行字符缓冲区(最多80个字符)*/

char*pline;/*字符缓冲区指针*/

staticcharntab1[100][10];/*变量名表:

共100项,每项长度为10*/

structntab

{

inttc;/*真值*/

intfc;/*假值*/

}ntab2[200];/*在布尔表达式)中保存有关布尔变量的真、假值*/

intlabel=0;/*指向ntab2的指针*/

structrewords

{

charsp[10];

intsy;

};/*匹配表的结构,用来与输入缓冲区中的单词进行匹配*/

structrewordsrewords[8]=

{{"if",syl_if},

{"else",syl_else},

{"while",syl_while},

{"{",syl_begin},

{"}",syl_end},

{"&&",op_and},

{"||",op_or},

{"!

",op_not}};/*匹配表初始化,大小为8*/

structaa{

intsyl;/*存放名字*/

intpos;/*存放名字所对应的值*/

}buf[100],/*词法分析结果缓冲区*/

n,/*读取二元式的当前字符*/

n1,/*当前表达式中的字符*/

E,/*非终结符*/

sstack[100],/*算术或布尔表达式加工处理使用的符号栈*/

ibuf[100],/*算术或布尔表达式使用的缓冲区*/

stack[1000];/*语法分析加工处理使用的符号栈*/

structaaoth;/*四元式中空白位置*/

structfourexp

{

charop[10];

structaaarg1;

structaaarg2;

intresult;

}fexp[200];/*四元式的结构定义*/

intssp=0;/*指向sstack栈指针*/

structaa*pbuf=buf;/*指向词法分析缓冲区的指针*/

intnlength=0;/*词法分析中记录单词的长度*/

inttt1=0;/*变量名表指针*/

FILE*cfile;/*源程序文件,~为结束符*/

intlnum=0;/*源程序行数记数*/

intsign=0;/*sign=0为赋值语句;sign=1为while语句;sign=3为if语句*/

intnewt=0;/*临时变量计数器*/

intnxq=100;/*nxq总是指向下一个将要形成的四元式地址,每次执行gen()时,地址自动增1*/

intlr;/*扫描LR分析表1过程中保存的当前状态值*/

intlr1;/*扫描LR分析表2或表3所保存的当前状态值*/

intsp=0;/*查找LR分析表时状态栈的栈顶指针*/

intstack1[100];/*状态栈1定义*/

intsp1=0;/*状态栈1的栈顶指针*/

intnum=0;/*算术或布尔表达式缓冲区指针*/

structll{

intnxq1;/*记录下一条四元式的地址*/

inttc1;/*真值链*/

intfc1;/*假值链*/

}labelmark[10];/*记录语句嵌套层次的数组,即记录嵌套中每层的布尔表达式e的首地址*/

intlabeltemp[10];/*记录语句嵌套层次的数组,即记录每一层else之前的四元式地址*/

intpointmark=-1;/*labelmark数组指针*/

intpointtemp=-1;/*labeltemp数组指针*/

千万不要删除行尾的分节符,此行不会被打印。

“结论”以前的所有正文内容都要编写在

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

当前位置:首页 > 法律文书 > 判决书

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

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