编译原理实验全集.docx
- 文档编号:10059410
- 上传时间:2023-02-08
- 格式:DOCX
- 页数:24
- 大小:66.99KB
编译原理实验全集.docx
《编译原理实验全集.docx》由会员分享,可在线阅读,更多相关《编译原理实验全集.docx(24页珍藏版)》请在冰豆网上搜索。
编译原理实验全集
实验5中间代码生成
实验6代码优化与代码生成
相关说明
实验1、2要求独立完成。
实验3、实验4的实现代码(VC++工程)均已调试成功,打包在目录中(lab3,lab4)。
希望认真学习的同学多看看。
由于时间关系,许多地方还没来得及做好,本手册的内容可能比较繁琐。
欢迎大家提出问题和意见。
时间比较紧的同学也请关注每个实验末尾的实验报告要求。
期末实验占10分。
实验1词法分析
1.1.目的
构造词法分析器,熟悉编译程序词法分析过程。
掌握LEX自动生成工具的使用。
1.2.内容
从本实验开始,用C语言实现一个编译系统。
词法分析是其第一步。
采用Lex工具自动生成大大简化了其中的内容。
因此本实验的重心并不在如何操作,而是在于怎样编写Lex源程序。
而要编写Lex源程序,首先要定义源语言,即该编译系统所实现的语言。
这里作为例子,我们以C++为基础,采用其部分单词,因此不妨将我们定义的这种语言称之为MiniC++。
1.3.知识
Lex是一个词法分析器的自动构造工具。
相关资料较多。
1.4.步骤
1、在编写LEX源程序前,首先要定义一种高级语言。
找出其中所有单词。
并进行编码。
高级语言可以是已经存在的一种语言,如,C,Pascal,Basic。
也可以是自己构造的一种语言。
当然,考虑到后续实现的难度,可以简化许多内容。
如不考虑数组,不考虑For循环等。
将定义好的单词编码用一个表表达出来。
如我们这里的MiniC++的单词定义如下:
保留字
内部编码
运算符号
内部编码
其他符号
内部编码
if
1
+
8
(
19
else
2
-
9
)
20
while
3
*
10
;
21
cout
4
/
11
{
22
cin
5
**
12
}
23
>>(输入)
6
==
13
=
24
<<(输出)
7
<
14
数字
25
>
15
标识符
26
<=
16
27
>=
17
var
28
<>
18
说明:
(1)由于只允许数字,所以不作变量类型。
所有类型都是var。
(2)单词编码无任何约束,只要方便可行。
要知道,我们构造的是一个独立的自包含的系统。
所有的东西都是我们自己定义的。
而编码只是便于机器内部识别。
只要后面的语法分析,语义分析都按这个编码即可。
2、将flex.rar展开在一个目录中,如E:
\flex。
3、根据上述单词编码编写LEX源程序。
LEX源程序是纯文本文件,任何文本编辑器均具备这一功能(如用记事本打开即可)。
保存时文件名任取。
这里假设命名为LexDemo.txt。
为后续操作简便,将该文件保存在E:
\flex子目录中。
4、编译LEX源程序。
生成lexyy.c。
5、使用C语言环境打开lexyy.c。
编译运行,最终生成lexyy.exe。
根据我们现有的情况,可以使用三种方案,任选其一。
(建议使用命令行工具,实际上更简单。
)
(1)使用VC建立一个工程(Project),将lexyy.c加入到该工程。
实际上,最好拷贝lexyy.c到相应的目录。
然后编译,产生的lexyy.exe文件在该工程目录下的Debug子目录中。
(2)使用VC的命令行工具。
格式为:
如:
VC安装在D:
\”ProgramFiles”下。
则命令为:
D:
\"ProgramFiles"\"MicrosoftVisualStudio"\VC98\Bin\CLlexyy.c
(3)使用TC命令行工具。
D:
\TC20\bin\TCClexyy.c
6、写一段该语言测试代码进行测试。
如:
While(i<2)
{Cout<<10;}
则输出序列为:
3,0
19,0
15,0
……
7、根据单词编码序列对照上面的表进行验证。
(1)每个单词对应一对数,第一个数为其编码。
第二个数为该单词的附加属性。
如无该属性则置0。
(2)如单词为标识符,其属性应该为符号表的入口地址。
但考虑到学词法分析时,还不熟悉符号表的机制,因而暂时也置0
1.5.问题及解答
1、涉及到命令行操作,考虑到仍有许多同学对DOS命令不熟悉。
希望自行加强。
2、如采用VisualC++编译,则先要建一个工程,将lexyy.c加入。
3、如果采用TC编译,则建议用TCC命令行工具。
4、由于LEX只能处理单字节的字符,请在LEX源程序中千万不要用中文注释。
保存时一定要看清楚类型,必须是ANSI类型。
1.6.实验报告要求
1、目的
2、内容
(1)单词及编码定义(设计一种高级语言(或子集)的单词其编码,见前面的表)
(2)部分LEX代码
(3)测试(写出两个测试句子:
所有单词正确,含有错误单词的句子)
3、总结(心得)
实验2自顶向下的语法分析
2.1.目的
在词法分析的基础上,熟悉自顶向下的语法分析方法。
体验递归程序的特点。
实现界面和业务逻辑代码分离。
实现异种平台的访问。
培养初步的软件架构的观念。
培养软件设计及编程能力。
2.2.知识
2.2.1终结符、非终结符及产生式
所有的组成最终句子的单词即为终结符。
非终结符代表的是我们观念上的语法范畴。
如主语。
谓语,名词等。
在高级语言中,语法范畴包括程序、说明语句、可执行语句、IF结构、关系表达式、算术表达式等等。
2.2.2语法及语义分析
语法分析只要求验证某个句子是否符合某个文法。
关注的是结构。
而语义关注的是句子的含义。
计算机本身无生命,无所谓真正地理解句子的含义。
我们所期望的计算机执行相应的语义是指:
当计算机程序扫描到某个句子后,所执行的操作恰好符合这个句子所表达的意思。
如扫描到一个“3+2”这样一个字符串。
自动地把结果5算出来。
而在语法分析中,所作的仅仅是判别这个句子的结构是否合法。
并不执行计算。
2.2.3FIRST集与FOLLOW集
所谓FIRST集,本质上就是第一个可能出现的符号构成的集合。
这个是可以由产生式本身得到的。
因为任何句子都是由该文法的产生式推出来的。
2.2.4关于递归下降子程序的编写
规则(请注意规则和下面的颜色对应关系):
(1)文法中的每个非终结符对应一个过程(函数)。
(2)若某个非终结符有多个产生式候选,则根据当前输入符是否在某个候选的Select集选择。
每个候选对应一个if分支。
(3)选择好候选之后,对产生式候选的右部的符号由左到右依次处理。
1若为终结符。
则将该符号和当前输入符号进行判等操作。
若相等,继续。
否则转③
2若为非终结符,则调用该非终结符所对应的过程。
3若当前扫描的符号为当前候选的第一个符号,且当前产生式有ε候选.则不作出错处理。
否则均出错处理。
例1:
Sà(S)|a
charstr[];…//待检查的串
intip;//扫描指针
voidS(){//只有一个非终结符S
if(str[ip]==’(‘){//处理Sà(S)候选
ip++;S();
if(str[ip]==’)’)ip++;
elseerror();
}
elseif(str[ip]==’a’){//处理Sàa候选
ip++;
}else//对应第1符号
error();//但无ε候选项,必须出错。
例2:
Sà(S)|ε
charstr[];…//待检查的串
intip;//扫描指针
voidS(){//只有一个非终结符S
if(str[ip]==’(‘){
ip++;S();
if(str[ip]==’)’)ip++;
elseerror();//不是第1个符号,出错。
}
elseif(str[ip]==’a’){
ip++;
}//不作出错处理。
需要注意的是,本例比较的是字符。
而本实验中用的是符号(单词)编码,是经过词法分析后的编码。
也就是后面定义的token数组相当于本例的str。
这点变通能力希望大家有。
关于这三条规则的具体运用,可看模板代码。
实现了两个非终结符,其余的需要同学们自己去实现。
2.3.内容
2.3.1.文法的设计
本实验的目标是实现一个文本计算器的语法检验。
要实现实数的加、减、乘、除、乘方几种运算。
这几种运算构成了三种优先级。
因此文法的表达和课堂上不一样。
根据需要设计所有的非终结符。
可以以产生式为核心。
在设计的过程中逐步引入非终结符。
->*
->/
→ε
非终结符定义
符号
定义
符号
定义
算术表达式
消除左递归的算术子表达式
优先级为3的子项
消除左递归且优先级为3的子项
优先级为2的子项
消除左递归且优先级为2的子项
优先级为1的子项
说明:
优先级的编号越小,其运算(归约)的次序先。
2.3.2.基于LEX自动构造器地词法分析程序的重构(任选)
对于大型编译系统而言,往往存在多遍扫描的可能性。
将词法分析程序构造成独立的过程有很大的好处。
但对于我们这个小型的文本计算器而言。
将词法分析构造成依附于语法分析的子过程可能更加合理。
因此我们需要对词法分析程序进行重构。
本实验只要求实现表达式的求值。
因此,只需要使用原来集合的部分单词。
对词法分析程序进行重构,使之成为语法分析程序的子程序。
通过全局变量的方式加工数据,输入表达式串,输出编码及属性序列,分别存放在两个全局的整型数组中。
因此需要作如下变更:
(1)单词的编码在语法分析和词法分析中都需要,因而同一集中在一个头文件token.h中。
以后,无论是词法、语法、语义分析,均用这套编码。
以下是部分语句:
#ifndefTOKEN
/*终结符定义*/
#define$add20/*+*/
#define$sub21/*-*/
…
#define$assign37/*=*/
#define$num38/*常数*/
#define$eps40/*ε*/
……
/*非终结符定义*//*逻辑表达说明*/
#define_E113/*
(2)定义两全局整型数tokens[]和attribs[]分别用于表示词法分析后的单词编码和属性对集。
该定义在token.h中,其定义格式如下:
#defineMAXTOKEN100
inttokens[MAXTOKEN];/*单词编码序列,可容纳100个*/
intattribs[MAXTOKEN];/*单词属性编码序列*/
intip;/*当前符号(单词编码)指针*/
#endif
将上述token.h加入到Lex文件的第1部分,替换掉原来的定义。
%{
#defineYYSTYPEdouble
#include
#include
#include"types.h"
#include"hash.h"
#include"token.h"
floatval=0;
%}
(3)第2部分为正规式单词的定义。
实际这里只用到数字,也可以不变。
(4)每扫描到一个单词,对应的处理动作为:
将该单词的编码和属性分别写入到tokens和attribs数组中。
具体在Lex第3部分如下编程:
"+"{addToken($add,0);}
"-"{addToken($sub,0);}
…
{digits}{sscanf(yytext,"%f",&val);//数字。
将数字的值拷贝进整型变量val。
其中addToken(inttoken,intattrib)函数定义在第4部分:
voidaddToken(inttoken,intattr){
if(ip tokens[ip]=token; attribs[ip]=attr; ip++; }} (建议大家统一再看看expToken.txt源文件。 ) (5)主程序无需打开输出文件,每扫描到一个单词,系统会自动用相应的代码块,即,调用addToken函数执行相应的工作。 这都在Lex自动生成的函数lexyy()内部处理。 因此语法分析主程序只需要调用lexyy()函数即可。 2.3.3.自编代码的词法分析器构造(任选) 该程序相对而言较为简单,以下列举词法分析主程序。 详细内容见参考代码。 //从流中读取一个单词 intlexyy(){ charch=''; if(bufChar=='') while(isBlank(ch))ch=fgetc(stream);//跳过所有的空格回车 else{//使用了超前搜索的情况 ch=bufChar; bufChar='';//用空格清掉超期搜索字符。 } if(ch=='+') return$add; elseif(ch=='-') return$sub; elseif(ch=='*') return$mul; elseif(ch=='/') return$div; elseif(ch=='(') return$llbr; elseif(ch==')') return$lrbr; elseif(ch=='#') return$sharp; elseif(isDigit(ch)){ bufChar=ch;//超期搜索缓冲 wip=0;//单词指针清0 returnreadNum();//读取整个数字,返回数字的单词编码,而数字的值暂存于curValue变量。 } else error("词法错误"); return0; } 2.3.4.主程序的编写 voidmain(){ /*执行词法分析*/ lexyy(); /*执行语法分析*/ E();/*调用开始符号所对应的过程进行分析。 */ printf("语法正确.");/*如能执行到这里,证明中途没出错*/ } 若上述代码放在LEX文件中,请把中文注释去掉,同时保存类型设为ANSI。 2.4.步骤 1、重构词法分析程序 设计文法 定义不回溯且考虑到三种优先级的表达式计算的文法,前面是一参考实现。 最好能根据要求独立设计文法。 2、建立C语言工程 建立语法分析主程序文件,假定命名为: express.c。 同时将该token.h加入。 3、编写递归下降程序 根据文法,按要求在express中编写递归下降子程序。 如感觉比较困难,请参照递归下降法。 4、编写测试主程序(基本要求) 构造一命令行的输入程序,即输入表达式串。 输出语法分析成功与否。 输出结果为“语法正确”“语法错误”。 5、程序系统调试验证结果 2.5.问题及解答 请大家提出意见和建议,以便改进本手册。 2.6.提高 1、实现一个完整的高级语言子集。 2、建立图形界面,对本实验内容进行包装。 3、选取针对某些结构化文档,如XML建立语法分析器。 2.7.实验报告要求 考虑到大家的实际情况,实验报告只要求实现基于字符的分析过程。 其提纲如下: 1、目的 2、内容 先介绍下该程序主要功能 (1)文法 EàTE’E’=+TE’|εTàFT’T’=*FT’|εFà(E)|i (2)递归下降过程(附上代码) (3)测试用例 3、总结(心得) 实验3自底向上的语法分析 3.1.目的 1、掌握SLR法进行语法分析的原理。 2、设计相应得数据结构及编码,实施算法的相关要素的机内表示。 3、掌握语法分析器的设计与调试; 3.2.内容 1、算法的数据结构 (1)分析表 分析表采用二维持数组,其中第0行存放表头。 intat[MAXROW][MAXCOL];//分析表 (2)堆栈 由于符号栈与状态栈通常同步操作,可将二栈合二为一。 堆栈元素可用结构体包装相应的数据。 //代表非终结符和终结符(由于符号栈和状态栈均为同步操作。 通过结构体的方式实现堆栈合并) typedefstructT{ charname;//名称(便于外部输出) intcode;//编码(便于内部处理) intstate;//状态 intvalue;//该非终结符的值 }Token; //堆栈类型 typedefstructS1{ Tokenbody[MAXSIZE];//堆栈体 intip;//栈顶指针 }TokenStack; (3)产生式的表示 //产生式候选记录表 typedefstructProduction{ charleftName;//左部名称 intleftCode;//左部编码 intrightLen;//右部长度,决定出栈数。 }Prod; 2、程序模块的划分 (1)堆栈操作的相关函数 (2)分析表操作 (3)主分析过程 //对一个输入符号执行分析 //说明: 由于堆栈集成为一个,所以堆栈操作可以不指出堆栈变量。 intanalysis(TokenStack*s,intcode){ intcurState,curOp,pi;//当前状态,操作,产生式编号 curState=getState(s);//获取当前状态 curOp=getOperate(curState,code);//获取当前操作 if(curOp==-1)error("分析表中未定义"); if(curOp==ACC){ printf("\n输入串正确"); exit(0);//输入串正确出口 } elseif(curOp>R){//归约代码 pi=curOp-R;//去掉R标识,得到产生式编号 for(inti=0;i pop(s);//出栈。 } curState=getState(s);//获取当前栈顶状态。 curOp=getOperate(curState,p[pi].leftCode);//查Goto表获取跳转的状态。 //符号栈入栈. //状态入栈(即查Goto表对应的值,所以G与S必须相等) //(语法分析阶段: 不考虑语义,故value参数为0) push(s,p[pi].leftName,p[pi].leftCode,curOp-S,0); outputStacks(s); opState=0;//当前为归约状态 } elseif(curOp>S){//Si入栈 push(s,getName(code),code,curOp-S,0);//当前符号、状态入栈 outputStacks(s); opState=1;//当前为移进状态 } else error("语法错误! ");// return0; } (4)主程序及输入输出 (5)错误处理 3、测试要求 (1)成功的输入串 (2)词法错误串 (3)语法错误串 3.3.步骤 1、建立C语言工程 2、加入单词编码头文件。 3、编写相关代码 4、在命令行下运行,进行测试。 3.4.问题 时间关系,许多地方还没来得及作好,但欢迎大家提出问题。 3.5.实验报告要求 1、目的 2、内容 (1)数据结构设计: 文字描述,不要求写代码 (2)主要模块: 1主分析过程(代码或流程图) 2堆栈操作: push、pop…(将函数名写出) 3分析表操作: (将函数名写出) 4词法分析辅助过程: (将函数名写出) 5输入输出及错误处理: (将函数名写出) (期末考试实验占10分。 其中本实验将根据内容记分,并直接记入期末成绩。 可参考所附代码,如自己有另外实现也可,需提交代码) (3)测试: 1语法正确的表达式。 2语法错误的表达式。 3词法错误的表达式。 3、总结(心得) 实验4语义分析 4.1.目的 1、属性文法及语法制导翻译和递归下降的语法分析结合,构造语义分析程序。 2、以表达式文法为例,构造文本计算器演示程序。 4.2.知识 1、设计属性文法。 以一个例子加以说明: 考察文法G(E): ETL L+TL|ε T$num 我们可以从以下几个方面去理解。 (1)下标的含义 首先考虑到产生式中不止一个E,在语法分析中由于在不同的时间不同的地方处理,因而不会产生歧义。 但在语义分析中,任何一个符号的属性都可在上下文中出现,因而有必要加以区分。 通常通过设置下标的方式来区分。 L+TL1 有了下标L和L1就能加以区分。 可以这么说,L、L1不是“同个”非终结符,但是是“同类”非终结符。 因此,属性文法设计的第一步就是把同一产生式候选中的相同符号加以下标。 (2)属性的设计 属性是为每个符号配置的。 终结符得属性通常通过词法分析得到。 非终结符的属性通常为上下文的属性计算得到。 首先考虑全局的,即整个文法最终要求什么。 将其数据化作为开始符号得属性。 在上例中,表达式最终是求若干个数之和的结果值。 因此为非终结符E配备一个属性s,表示E所展开的子表达式的值。 再考虑叶子结点能够获取到什么信息。 叶子都是终结符,信息实际上是通过词法分析获取的单词语义。 如“2”,分析程序扫描到文本字符“2”,将其转换成二进制的值2。 但在语法分析中,只是将其解释为“数字”,用$num表示。 而二进制的值2只是作为$num的一个属性,不妨记为$num.v。 最后考虑语法树中的支干结点需要传递些什么数据,根据相应的数据定义相应的属性。 设计属性时,必须明确两点: 3、支干(非叶子,非根)结点的属性主要功能是传递数据和中间计算。 要按树的遍历顺序来考虑。 因为无论是自顶向下还是自底向上,本质上都是对树的一次遍历(这点在上课实讲过)。 4、一个
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 编译 原理 实验 全集
![提示](https://static.bdocx.com/images/bang_tan.gif)