编译原理综合指导书docx.docx
- 文档编号:24279987
- 上传时间:2023-05-26
- 格式:DOCX
- 页数:17
- 大小:240.84KB
编译原理综合指导书docx.docx
《编译原理综合指导书docx.docx》由会员分享,可在线阅读,更多相关《编译原理综合指导书docx.docx(17页珍藏版)》请在冰豆网上搜索。
编译原理综合指导书docx
编译原理综合实验指导书
序言
《编译原理综合实验》作为《编译原理》课程的延伸,其目的是让同学动手设计和实现某一规模适屮的语言的编译器,该编译器不仅涉及编译程序前端设计的各个阶段,而口也强调了编译的总体设计、各个阶段的接口安排等等。
通过上机实践,來设计这个相对完整的编译器设计,一方面可以使学生增加对编译程序的整体认识和了解——巩固《编译原理》课程所学知识,另一方面,通过上机练习,学生也可以学到很多程序调试技巧和设计大型程序一般的原则,如模块接口的协调,数据结构的合理选择等等。
一.上机实践要求
(1)本次实验要求单人独立完成,综合实验提交的截止日期是第18周周五;
(2)本次综合实验须经授课教师当面验收考核后才予评分,否则以缺交处理;
实验验收地点:
1号学院楼121
实验验收时间:
笫14周-笫18周,每周一、三、四、五下午12:
00-2:
00
(3)综合实验分为四个部分,即:
词法分析器设计、语法分析器设计、语义分析器设计、屮间代码生成。
每实现一个部分可得到不同的分数,满分为100分。
(4)
具体评分标准如下:
成绩占总成绩的15%o
(5)实验结束后提交:
源代码和实验报告。
(6)实验报告内容:
1冇关编译器设计的说明。
详细说明你的编译器的实现原理,包括软件结构设计说明、功能模块说明、关键数据结构说明、关键算法实现等、
2指出你的程序屮最精彩的部分,以及你的设计小述存在的问题不足。
3总结开发经验收获;
4如果重做一遍,你会有哪些新的改进?
5测试源代码。
注:
实验报告中不要贴代码,否则一律视作灌水贴。
(6)附加题:
(1)语法分析能支持错误处理;
说明:
根据实现难度相应加分,最高20分。
(2)扩展文法屮的“条件表达式”文法,使其能如C语言那样支持带括号的布尔运算和逻辑运算,而且生成的布尔表达式采用短路计算。
说明:
实现本功能则平时成绩(上课出勤+平时作业+综合实验)按满分计算。
设计一、词法分析程序的设计与实现
设计要求:
(1)设计一个词法分析程序,每调用一次就从源程序文件屮顺序识别出一个单词符号,并返回该单词符号的内部编码、单词符号口身、行列位置信息。
(2)要能处理单行注释。
单词种类与识别规则
(1)标识符:
首字符为字母或卜•划线,其后由字母、数字或卜•划线组成、
长度不超过255个字符;
(2)整数:
由1到8个数字组成。
(3)小数:
数字串1・数字串2,其中:
数字串1曲1-8个数字符组成;
数字串2由0-8个数字符组成,即:
数字串2可以为空。
(4)字符串:
由一对“”描起来的符号串,长度不超过255个字符;
(5)保留字:
if>else>while>do>integer>float>string、input>output>
and、or>function>end>def\as、begin
(8)逻辑运算符:
and、or
(9)分隔符:
{、}、(、)、;、,
词法分析程序的设计思想
为了实现的编译程序实用,这里规定源程序可采用自由书写格式,即一行内可以书写多个语句,一个语句也可以占领多行书写。
这样词法分析程序的主要工作为:
(1)从源程序文件中读入字符。
(2)统计行数和列数用于错误单词的定位。
(3)删除空格类字符,包括回车、制表符空格。
(4)按拼写单词,并用(内码,属性)二元式表示。
这里采用的编译程序的实现方法是一遍扫描,即从左到右只扫描一次源程序,也就是词法分析作为语法分析的一个子程序。
故在编写词法分析程序时,用重复调用词法分析子程序取一单词的方法得到整个源程序的内码流。
扫描程序流程图,如图l-lo取单词子程序流程图;如图l-2o取字符和统计字符行列位置子程序;如图1。
ETSYM
GETCH
・・.
n<20?
V
ch为
关7-符?
N
6吧数
Y
fr衣输出单'齐符
str[n]:
=chn:
=n・1
ch;r-
或数了
将保留7输岀
Str是:
V侃7-
将标识符输出
Val:
=O
1
I
Val:
=valA10*ch-0・
1
|GETOH
6足数’F?
GETCH
GETSYM
图1・2収单•词程序流程图
str[o]:
=ch
1
1
GETCH
汕成双i?
str[i]:
=ch
H灰输用双了符
介灰输:
l;f符
GETCH
设计二、语法分析程序的设计与实现
设计要求:
构造下列文法的语法分析程序,应能指出源程序小出现的错误。
1.〈程序>—>〈函数块〉
2.〈函数块〉一>〈函数〉[〈函数〉]
3.〈函数〉一>functionid()〈语句块〉endfunction
4.〈语旬块〉一>begin语句[语句]end
5.〈语句>—>〈分支语句>|〈赋值语句>|〈循环语句〉|
〈输入语句〉〈输出语句〉〈变量定义语句〉
说明:
1id就是标识符;
2con是常量,即整数;deci是小数;
3<表达式〉中不允许出现字符串类型;
4<>:
用左右尖括号括起来的是非终结符;
5[]表示其中内容重复0次或N多次,相当于正则表达式中的*;
6{}表示其中内容出现0次或1次(if语句中的else),相当于可选项;
7文法中用蓝色标注的都是终结符(即单词),用红色标注的是文法描述语言中的运算符,不是终结符(单词),也不是非终结符。
8文法中关于〈布尔表达式〉的定义是不严格的,按其定义与实际语言存在两个差异:
其一,and与or不存在优先级,两者优先级别一样,按左结合处理;其二,不能用括号将<布尔表达式〉括起来(注:
表达式中的括号不受此限制)。
这样设计是为了降低实验的难度,同学若能修改<布尔表达式〉的文法定义,使其如正式语言的语法描述则最佳。
递归子程序的设计思想
为每个非终结符设计一个识别的了程序。
由于单词在语法分析中作为一个整体,故在语法识别中仅使用其内码。
在这里将词法分析作为语法分析的一个子程序,当语法分析需耍单词时,就调用相应的词法分析程序获得一个单词。
语法分析的作用是识别输入符号吊是否是文法上定义的句了,即判断输入符号串是否是满足“程序”定义的要求。
也就是当语法识别程序从正常退出表示输入符号串是止确的“程序”;若从出错退出,则输入符号串不是正确的“程序”。
出错时,可以根据读字符的位置判断出错的位置。
错误处理
一个编译程序,在多数情况下,所接受的源程序正文都是有错误的。
发现错误,并给出合适的诊断信息且继续编译下去从而发现更多的错误,对于编译程序而言是完全必要的。
一个好的编译器,其特征在于:
♦任何输入序列都不会引起编译程序的崩溃。
♦一切按语言定义为非法的结构,都能被发现和标志出来。
♦经常出现的错误,程序员的粗心或误解造成的错误能被正确地诊断出来,而不致引起进一步的株连错误。
根据这样的要求,一般采用以下两条规则:
(1)关键字规则;程序员在写程序吋,可能会因为粗心而漏掉语句的分隔符一一但他决不会漏掉算术运算符“+”,对于编译程序而言,不论是分隔符号类的符号还是关键字符号类的符号,它们都具有同等重要的地位。
基于这样的特点,我们可以采用不易出错的部分来作为恢复止常步调的标记。
每当遇到错误时,分析程序跳过后面的某些部分,直到出现所期望的符号为止。
对于程序设计语言来说,这种符号
(称为同步符号)的最好选择就是关键字。
PL/0的每-•种构造语句以begin>if或while开头;每种说明则以var、const或procedure开头。
每遇到错误吋,编译程序便可跳过一段程序,直到遇到这类符号为止,而继续编译。
(2)镇定规则;口顶向下分析的特点在于目标对分成一些了目标,分程序则用别的分析程序来处理其子口标。
镇定规则是说一个分析程序发现了错误,它不应该消极地停止前进,仅仅向调用它的程序报告发生的错误;而应该自己继续向前扫描,找到似乎可以使正常的分析得以恢复的地方。
这i规则在程序设计上的含义就是任-•分析程序除了正常终止外,没冇其它岀口。
对于镇定规则,一个可能的严格解释为:
一旦发现非法结构,即跳过后面的输入止文,直到下一个可以止确地跟随当前止在分析的句了结构的符号为止。
这意味着每一分析程序需知道其当前活动结点的后继符号集合。
为了找到这个后继符号集合,我们给对应语法图的每一个分析过程捉供一个显式参数,set,它指明可能的后继集合。
不过在任何条件下,如果都跳到输入正文屮下一个这种后继符号出现的地方,未免太短视了。
程序屮所含的错误可能只不过是漏掉了一个符号(如“;”)而己,由此而忽略去源程序的符号集合中,再凑加一些关键字,它们用于标记那些不容忽略的结构的开始符,因此,作为参数传递给分析过程的那些符号就不仅是后继符号了。
对于这样的符号集,我们釆用这样的计算策略:
先用一些明显的关键符号给它赋初值,然后随着分析子口标的层次深入,逐步补充别的合法符号。
为了灵活起见,我们引入test子程序来实现所说的验证工作。
test过程冇三个参数:
(1)可允许的下一个符号集合S1,如果当前符号不在此集合小,当即得到一个错误号;
(2)另加的停止符号集合S2,冇些符号的岀现,虽然无疑是错的,但它们绝对不应被忽略而跳过;
(3)整数n,表示冇关错误的诊断号:
voidtest(symsetsi,symsets2,intn)
{
symsets;
if(!
inset(sym,si))
{
error(n);
s=uniteset(sl,s2);
while(!
inset(sym,s))
getsym();
destroyset(s);
}
}
我们前面提出的方案,具冇这样的性质:
试图通过略过输入正文中的一个或多个符号来恢复分析的正常步调。
在错误仅为漏掉一个符号所引起的情况下,它都是不适宜的策略。
经验表明,这类错误基本上限于那种仅冇语法作用,而不代表动作的符号(如“厂)。
把一些关键字加到后继符号集合小去可使分析程序不再盲目地跳过后而的符号,好彖漏掉的已经补上去一样。
下而程序段就是PL/O分析程序中复合语句分析的一小段。
它的效果等于关键字前插入漏掉的分号。
statbegsys集合是“语句”这个结构的首符号集。
if(sym==SYM_BEGIN)
{getsym();
sell=createset(SYM_SEMICOLON,SYM_END,SYM_NULL);set=uniteset(setl,fsys);
statement(set);
while(sym==SYM_SEMICOLONIIinset(sym,statbegsys))
{
if(sym=SYM_SEMICOLON)
{
getsym();
}
else
{
error(lO);
}
statement(set);
}//while
destroyset(setl);
destroyset(set);
if(sym==SYM_END)
{
getsym();
}
else
error(17);//or'end'expected.
设计三、语义分析程序的设计与实现
设计要求:
为语法分析程序中添加类型检查功能,包括:
1变量重复定义;
2变量未定义就使用;
3变量未赋值就引用;
如何进行语义检查
符号表在编译的过程中起着非常重耍的作用,它一方面可以帮助语义分析程序进行语义检査,一方而可以辅助代码生成程序生成正确的代码(请参见书本相关章节)。
符号表是记录符号属性的表,它的每一表项表示一个标识符的属性信息。
这些属性信息通常包括种类(常数、变量、数组、标号等),类型(整型、实型、逻辑型、字符型等),给名字分配的存储单元地址等。
符号表屮的各类信息将在编译程序工作过程屮的适当时候填入。
对在语义分析阶段建造符号表的编译程序,当遇到标识符声明部分时,每当遇到一个名字声明,就以此名字查符号表;若表屮无此登记项,则将该名字填入表中;若该表屮已有此登记项,则说明该名字是重复声明,报告语义错谋。
至于与该名字相关的一些信息,可视工作的方便,分别在编译程序工作过程中的适当时候填入:
种类,类型等信息可在语义分析阶段完成;名字的存储地址等信息则要在代码生成阶段完成。
几乎在编译程序工作的全部过程中,都需要对符号表进行频繁的访问,查表和填表等操作,是编译程序的一笔很人的开销。
因此,合理地纽织符号表,并相应地选择好查表和填表的方法,是提鬲编译程序工作效率的重耍一环。
设计讲解
(1)创建符号表
对于如下的产生式
〈说明语句〉一var标识符〈变量列表〉:
v数据类型〉
我们在语法分析的基础上将标识符添加到符号表tokTable
voiddeclarate()
{
if(token==VAR)//token是保存下一个要分析单词的全局变量
{
token=getToken();〃取下一个单词到token
if(token.!
=ID)error(token;'缺少变量名或变量名错误!
”);
/*以下是语义动作:
将变量名保存到符号表;
注意:
此时变量类型还不知道,因为后续的语法分析还没有执行,所以此时先将变量名保存到符号表,在后续语法分析执行后,再根据分析结果回查符号表,将所有未指明数据类型的变量补上分析得到的数据类型信息*/
if(lookupToken(token,tokTable)>=0)
{
error(token,”变量重复定义!
”);
}
else
createToken(token,tokTable);
//语义处理结束,以下是语法分析代码
token=getToken();
varList();
if(token!
=COLON)error(token/1缺少:
");
token=getToken();
varType();
}
}
对于如下的产生式:
〈变量列表〉一,标识符〈变量列表〉I£
我们在语法分析的基础上创建符号表
voidvarList()
{
if(token==COMMA)
{
token=getToken();
if(token!
=ID)error(token,n缺少变量名或变量名错误!
”);
/*以下是语义动作:
将变量名保存到符号表;
注意:
此时变量类型还不知道,因为后续的语法分析还没有执行,所以此时先将变量名保存到符号表,在后续语法分析执行后,再根据分析结果回查符号表,将所有未指明数据类型的变量补上分析得到的数据类型信息
*/
if(lookupToken(token,tokTable)>=0)
{
error(token,H变量重复定义”);
}
else
createToken(token,tokTable);
//语义处理结束,以下是语法分析代码
token=getToken();
varList();
}
}
对于如下的产生式:
〈数据类型〉一INTEGERIFLOAT
我们回填已声明的变量的数据类型
voidvarType()
{
if(token!
=INTEGER&&token!
=FLOAT)error(token,"数据类型错误!
");
〃以下语义动作根据token的值修改刚定义的变量的数据类型
updateTokenType(token,tokTable);
token=getToken();
}
(2)查找符号表
在表达式语法分析中检查变量是否已定义
设计四、中间代码生成的设计与实现
设计要求:
将语法止确的源程序翻译成四元式。
设计参考:
在〈IF语句>语法分析程序的基础上添加语义翻译了程序和中间代码生成部分代码的方法。
<if语句〉:
:
=ifv布尔表达式〉thenv执行句〉elsev执行句>
语诙今祈程唇
ifs(){
token=getnexttoken();
If(token!
="if")error;token=gctnexttokenO;bexp();
token=getnexttoken();
Tf(token!
="then")error;token=getnexttoken();ST_SORT();/*调用函数处理then后的可执行语句*/
token=getnexttoken();If(token!
="else")error;token=getnexttoken();
ST_SORT();/*处理else后的可执行语句*/
}
token=getnexttoken();
If(token!
=uif,)error;
token=getnexttoken();
(e.tc,c.fc)二bexp();
token二getnexttoken();
if(token!
=,,thenH)error(H缺then”);backpatch(e・tc,nxq);/*回填真出口e.tc*/token=getnexttoken();
sl.chain二st_sort(token);/*处理si,返回si链
token=getnexttoken();
iftoken=,'else*'/*处理else部分*/
{a二nxa;
父encoded,—,—,0);
backpatch(e・fc,nx(i);/*回填假出口e.fc*/
t.chain二mer^sl.chaima);getnexttoken(token);
s2・chain=st_sort(tokeii);
s.chain=mcr£(t・chaims2・chain);retum(s・chaiii)
}
else/*if—then结构时*/
retuni(merg(sl.chain,e.fc))
ST_SORT(token);/*处理各种可执行语句*/
ST_SORT(chart*token)/*可执行语句分类处理模块主程序*/
{
if(token=〃if〃)ifs();/*调用IF语句分析模块*/
elseif(token="wh订e〃)wh订es();/*调用while语句分析模块*/elseif(token="repeat")repeats();/*调用repeat语句分析模块*/elseif(token="for")fors();/*调用for语句分析模块*/elseassign()余情况表示是赋值语句,直接调用赋值语句的分析*/
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 编译 原理 综合 指导书 docx