《编译原理》课程实践报告
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实验题目:简单C程序解释器设计
实验目的:通过简单C解释器的编写,了解高级语言程序的编译过程,深刻理解词法分析、语法分析及语义分析,理解符号表及符号表的作用以及出错处理在编译过程中的作用。
同时通过简C语言单解释器的编写,进一步提高学生的编程水平,锻炼学生独立编程能力。
实验要求:首先阅读实验指导书,理解实验要求及程序设计方法,然后将附录中的程序输入到计算机中调试成功,然后按照思考题的要求完成相应的程序设计。
实验内容及说明:
一、引言
编译器的编写涉及到程序设计语言、计算机体系结构、语言理论、算法和软件工程等学科。
是计算机科学技术的重要基础,在每一个计算机科学工作者的职业生涯中,这些原理和技术都是反复用到的。
一个编译程序从逻辑上分为词法分析、语法分析、语义分析、优化及代码生成五部份,但为编写好编译程序还需要有出错处理及符号表管理两个辅助模块。
《编译原理》课程由原理上(或者说从形式语言角度上)讨论编译程序的设计,而且按照编译程序的逻辑组成分块讨论,为了对编译程序的设计有一个总体认识,深入理解编译程序中使用的各种技术,理解编译程序的构造,学习《编译原理》课程的学生应该编写某个程序设计语言的编译器,或者分析某个语言的编译器。
然而由于一个程序设计语言的编译器是十分庞大的,在有限的课时条件下无法完成。
为了理解编译器的构造及结构,掌握编译器构造中的各种技术,我们设计一个以C语言为版本的简化了的简单程序设计语言,为了编译程序构造的简单,仅仅要求该程序设计语言具有顺序、分枝及循环结构,而无函数或过程调用,为了简单起见,仅仅处理整数数据类型,无数组、结构体等数据类型。
我们知道,一个编译程序是将某一高级语言编写的源程序翻译成机器语言目标程序或者翻译成汇编语言目标程序。
然而翻译成汇编语言目标程序或机器语言目标程序需要十分熟悉目标机的指令系统,为编译器的编写带来很多麻烦。
为了简化编译器的编写,我们仅仅完成编译器的前端设计,即实现解释执行简化后的C语言源程序。
二、简化C语言文法
简化C语言文法定义(LL(1)文法)
C程序::=void main(){函数体}
函数体::=变量定义部份语句列
变量定义部份::=变量定义变量定义部份|ε
变量定义::=int 变量表;
变量表::=标识符|标识符,变量表
语句列::=语句语句列|ε
语句::=条件语句|循环语句|读语句|写语句|复合语句|表达式语句|空语句
条件语句::=if(表达式)语句
循环语句::=while(表达式)语句
读语句::=read(变量表);
写语句::=write(表达式表);
复合语句::={语句列}
表达式语句::=表达式;
空语句::=;
表达式定义(算符优先文法)
表达式::=变量=表达式|变量+=表达式|变量-=表达式|变量*=表达式|
变量/=表达式|变量%=表达式|表达式1
表达式1::=表达式1||表达式2|表达式2
表达式2::=表达式2&&表达式3|表达式3
表达式3::=表达式3==表达式4|表达式3!=表达式4|
表达式3>=表达式4|表达式3>表达式4|
表达式3<=表达式4|表达式3<表达式4|表达式4
表达式4::=表达式4+表达式5|表达式4-表达式5|表达式5
表达式5::=表达式5*表达式6|表达式5/表达式6|表达式5%表达式6|表达式6
表达式6::=!表达式7
表达式7::=(表达式)|变量|常量
标识符及常量定义(词法部份)
标识符::=字母(字母|数字)*
字母::=a|……|z|A|……|Z
常数::=数字(数字)*
常数::=0|……|9
说明:
1、语义与C语言中的语义基本相同,为了简便,引入的read语句及write语句
实现数据的输入与输出,取代系统函数scanf及printf。
2、为简单,不允许使用任何函数包括系统函
三、词法分析(取单词)程序设计
本模块的功能是扫描源程序识别保留字、标识符、运算符、分隔符及常数,然后将识别出的符号填写到符号表中。
扫描源程序的方法较多,可以将源程序作为一个文件流处理、亦可利用缓冲区暂存文件、亦可将源程序一次性地读入到一个字符串中,将其视为字符流处理。
本例中将源程序一次性地读入到字符串中,将其视为字符流处理。
单词分类:
保留字:void、int、main、if、while、read、write
运算符分隔符:+、-、*、/、%、==、!=、>、>=、<、<=、&&、||、!
=、+=、-=、*=、/=、%=、(、)、,、{、}、;
标识符:由字母开始的字母数字串
常数:整常,由十进制数组成的数字串
单词的处理:
将标识符作为一类(ID),将常数作为一类(NUM),保留字、运算符分隔符中的符号每一符号为一类,统一视为保留字。
单词结构由三项组成:指向单词字符
串的指针、单词类型及单词的值。
其结构如下:
struct entry
{
char *lexptr; /*指向单词的指针*/
int token; /*单词类型*/
int ival; /*常量值或存储的变量的值*/
};
为了简便处理,使用顺序表存储符号,使用一个字符数组存储源程序中的符号。
首先将保留字及运算符分隔符表填写到符号表数组中,识别一个单词后,返回单词的类型,
如果是常数,则将常数值存放在一个全局变量(tokenval)中(注:仅仅处理整数),如果是标识符,首先查找符号表,如果符号表中不存在,将该标识符填写到符号表中,将单词在符号表中的位置存放在全局变量(tokenval)中。
在处理标识符时,需要区分是处理变量定义语句阶段,还是在处理执行语句阶段,如果是处理变量定义阶段,则每识别出的标识符应该是新标识符,否则出现“变量重定义”错误,如果在执行阶段,则每识别的标识符必须是存在的,否则出现“变量未定义”
错误。
四、表达式计算(算法优先分析)程序设计
首先根据表达式的文法确定表达式的优先矩阵,为了编程简单化,以一个二维数组存储优先矩阵。
其次为了进行算符优先分析,使用一个分析栈,栈的结构如下:
struct ExpStruct
{
int token; /*单词类型,<0时为非终结符号,>=0为输入符号*/
union{
int iVal; /*如果单词为整数时,存储整数值*/
int nPos; /*如果单词为标识符时,存储单词在符号表中的位置*/
};
};
最后算符优先分析返回表达式的值。
说明:由于计算表达式的值是语法(或语义)分析的一部份,因此在分析过程中,不可能(也无必要)先得到表达式,然后进行表达式的计算,而是将表达式的
识别与计算同时进行,因此当处理表达式过程可能会因为表达式“出错”而完成表
达式的识别,因此将表达式的出错处理稍加修改,修改如下:
如果分析栈中仅有一个输入符号(事实上是开始终止符号)时,则认为表达式计算成功,完成表达式的计算,多于一个输入符号,则认定表达式出错。
五、递归下降分析(解释器)程序设计
此简单C语言的文法是LL(1)文法,可以使用递归下降分析处理语法分析及语义分析,由于分支结构及循环结构的存在,对某语句的分析时,有时需要进行语义处理(解释执行语句)有时需要仅仅对语句进行分析而不处理语义。
因此将对每一文法规则定义两套分析程序,一为语义处理(解释执行语句),另一为语法处理(不执行语句)。
六、思考练习题
1、分析“简单C解释程序”设计,指出解释器设计与编译器设计之区别,修改解释
器,将简单C源程序翻译成中间代码并输出,然后再编写中间代码解释执行环境。
(注:
1、自己定义中间代码形式;
2、java语言及VB使用的P-code是一种中间代码。
)
2、引入实型及整型变量,如何定义符号表。
修改本程序使之能够处理两种数据类型(整
型、实型)的简单C语言解释程序。
3、增加数组类型,如何定义符号表。
修改本程序使之能够处理数组。
4、允许定义函数,定义全局变量,如何定义符号表。
修改本程序使之成为较完美的C
语言解释器。