编译原理实验指导书.docx
《编译原理实验指导书.docx》由会员分享,可在线阅读,更多相关《编译原理实验指导书.docx(28页珍藏版)》请在冰点文库上搜索。
编译原理实验指导书
LIAOCHENGUNIVERSITY
编译原理
实验指导书
聊城大学计算机学院
2011年3月
目 录
《编译原理》课程实验教学大纲
课程名称:
编译原理
英文名称:
Compileprinciples
设置形式:
非独立设课
课程模块:
专业方向课
实验课性质:
专业实验
课程编号:
509615
课程负责人:
姜华
大纲主撰人:
姜华
大纲审核人:
左风朝
一、学时、学分
课程总学时:
72
实验学时:
8
课程学分:
4
二、适用专业及年级
计算机科学与技术、软件工程三年级
三、课程目标与基本要求
《编译原理》课程是计算机专业的核心课程,是培养计算机技术高级人才的必修课程。
该课程通过程序设计语言和语言处理软件的理论与技术的教学,培养学生利用计算机语言处理技术进行系统分析和软件设计的能力。
<<编译原理>>是理论与实践并重的课程,这门实验课要综合运用一、二、三年级所学的多门课程的内容。
实验目标与要求;1.学会用高级程序设计语言设计词法分析器。
2.学会用高级程序设计语言设计语法分析器。
四、主要仪器设备
Windows操作系统,编程语言采用C、C++,集成调试环境采用TC或MicrosoftVisualStudio6
五、实验项目及教学安排
序
号
实验项目
名称
实验内容
学时
要求
性质
类别
所用主要仪
器及台套数
所在实验室
1
用C或者C++语言设计一个词法分析器
1. 确定编译中使用的表格、词法分析器的输出形式、标识符与关键字的区分方法。
2. 把词法分析器设计成一个独立的过程。
4
必做
设计
综合型
微机,每人一台。
计算机学院实验中心
2
用C或者C++语言设计一个语法分析器。
1. 词法分析和语法分析在一起实现。
2.把语法分析器设计成一个独的过程。
4
必做
设计
综合型
微机,每人一台。
计算机学院实验中心
六、考核方式及成绩评定
根据学生实验出勤情况、实验态度、实验报告成绩等方面评定实验成绩。
实验报告平均成绩(含实验理论)占实验成绩的50%,实验技能平均成绩(含实验态度)占实验成绩的50%。
实验成绩占该课程考试总成绩的10%—20%。
在机器上将程序及运行结果上传至服务器,由实习教师给出优、良、中、及格、不及格。
七、实验教科书、参考书
1.实验教科书
自编实验指导书。
2.实验参考书
实验一 词法分析器的设计
基本信息
实验课程:
编译原理设课形式:
非独立
课程学分:
4实验项目:
词法分析器的设计
项目类型:
设计项目学时:
4
实验目的
1.掌握词法分析的原理;
2.熟悉符号表的建立与单词的分类方法;
3.掌握词法分析器的设计与调试;
实验内容
1.分析如表1所定义的PASCAL语言子集的语法,找出所有单词的组成及类别;
2.完成单词的分类及其编码;
3.完成保留字表、变量名表和常数表的结构设计;
4.建立识别单词符号集合的DFA;
5.由DFA设计词法分析程序;
6.调试并运行词法分析程序;
7.实验结果分析。
分析结果含义并写出自己的心得体会。
表1.PASCAL语言子集的语法定义
〈程序〉→〈变量说明〉BEGIN〈语句表〉END.
〈变量说明〉→VAR〈变量表〉:
〈类型〉;|〈空〉
〈变量表〉→〈变量表〉,〈变量〉|〈变量〉
〈类型〉→INTEGER
〈语句表〉→〈语句表〉;〈语句〉|〈语句〉
〈语句〉→〈赋值语句〉|〈条件语句〉|〈WHILE语句〉|〈复合语句〉|〈过程定义〉
〈赋值语句〉→〈变量〉∶=〈算术表达式〉
〈条件语句〉→IF〈关系表达式〉THEN〈语句〉ELSE〈语句〉
〈WHILE语句〉→WHILE〈关系表达式〉DO〈语句〉
〈复合语句〉→BEGIN〈语句表〉END
〈过程定义〉→PROCEDURE〈标识符〉〈参数表〉;BEGIN〈语句表〉END
〈参数表〉→(〈标识符表〉)|〈空〉
〈标识符表〉→〈标识符表〉,〈标识符〉|〈标识符〉
〈算术表达式〉→〈算术表达式〉+〈项〉|〈项〉
〈项〉→〈项〉*〈初等量〉|〈初等量〉
〈初等量〉→(〈算术表达式〉)|〈变量〉|〈无符号数〉
〈关系表达式〉→〈算术表达式〉〈关系符〉〈算术表达式〉
〈变量〉→〈标识符〉
〈标识符〉→〈标识符〉〈字母〉|〈标识符〉〈数学〉|〈字母〉
〈无符号数〉→〈无符号数〉〈数字〉|〈数字〉
〈关系符〉→=|<|<=|>|>=|<>
〈字母〉→A|B|C|…|X|Y|Z
〈数字〉→0|1|2|…|8|9
〈空〉→
提示:
(1)单词的分类。
可将所有标识符归为一类;将常数归为另一类;保留字和分隔符则可采取一词一类。
(2)符号表的建立。
可事先建立一保留字表,以备在识别保留字时进行查询。
变量名表及常数表则在词法分析过程中建立。
(3)单词串的输出形式。
所输出的每一单词,均按形如(CLASS,VALUE)的二元式编码。
对于变量标识符和常数,CLASS字段为相应的类别码,VALUE字段则是该标识符、常数在其符号表中登记项的序号(要求在变量名表登记项中存放该标识符的字符串,其最大长度为四个字符;常数表登记项中则存放该整数的二进制形式)。
对于保留字和分隔号,由于采用一词一类的编码方式,所以仅需在二元式的CLASS字段上放置相应的单词的类别码,VALUE字段则为“空”。
不过,为便于查看由词法分析程序所输出的单词串,也可以在CLASS字段上直接放置单词符号串本身。
测试用输入:
测试用输入程序为。
Procedureprogram1(a,b);
Begin
Varxyz=50;
Whilea>bdo
begin
Ifxyz=0then
xyz=50;
xyz:
=xyz-a;
a:
=a-1;
end
End
实验扩充
构造语言的词法分析程序,要求识别出变量类型并记录相关信息。
实验说明
实验环境:
WINDOWS下,工具为TurboC2.0或VisualC6.0。
实验考核方式
1.提交实验报告
2.演示程序和答辩(抽查)
实验辅导
1.词法分析程序的功能
词法分析程序又称为扫描器,其功能在于依次扫视字符串形式源程序中的各个字符,逐个识别出其中的单词,并将其转换为内部编码形式的单词符号串作确为输出。
通常,可采用二元式
(class,value)
来表示一个单词符号的内部编码,其中:
class为一整数码,用于表示该单词的类别;value则是该单词之值(如变量名在符号表中序号,常数的二进制表示,以及运算符和分隔符的编码等等)。
概括地说,扫描器在其工作过程中,一般应完成下列的任务:
(1)识别出源程序中的各个单词符号,并将其转换为内部编码形式;
(2)删除无用的空白字符、回车字符以及其它非实质性字符;
(3)删除注释;
(4)进行词法检查,报告所发现的错误。
此外,视编译工作流程的组织,一些编译程序在进行词法分析时,还要完成将所识别出的标识符登录到符号表的工作。
2.实例分析
对于表2所列的各类单词符号,词法分析程序可按图1所示的状态转换图来构造。
表2一个语言的单词符号及分类码表
图1识别表2所列语言单词的DFA及相关的语义过程
相关变量和子程序说明如下:
◆函数GETCHAR每调用一次,就把扫描指示器当前所指示的源程序字符送入字符变量ch,然后把扫描指示器前推一个字符位置。
◆字符数组TOKEN用来依次存放一个单词词文中的各个字符。
◆函数CAT每调用一次,就把当前ch中的字符拼接于TOKEN中所存字符串的右边。
◆函数LOOKUP每调用一次,就以TOKEN中的字符串查保留字表,若查到,就将相应关键字的类别码赋给整型变量c;否则将c置为零。
◆函数RETRACT每调用一次,就把扫描指示器回退一个字符位置(即退回多读的那个字符)。
◆函数OUT一般仅在进入终态时调用此函数,调用的形式为OUT(c,VAL)。
其中,实参c为相应单词的类别码或其助记符;当所识别的单词为标识符和整数时,实参VAL为TOKEN(即词文分别为字母数字串和数字串),对于其余种类的单词,VAL均为空串。
函数OUT的功能是,在送出一个单词的内部表示之后,返回到调用该词法分析程序的那个程序。
可参考的C语言源程序如下:
#include
#include
#include
#defineID6
#defineINT7
#defineLT8
#defineLE9
#defineEQ10
#defineNE11
#defineGT12
#defineGE13
charTOKEN[20];
externintlookup(char*);
externvoidout(int,char*);
externreporterror(void);
voidscannerexample(FILE*fp)
{
charch;
inti,c;
ch=fgetc(fp);
if(isalpha(ch))/*itmustbeaidentifer!
*/
{
TOKEN[0]=ch;
ch=fgetc(fp);i=1;
while(isalnum(ch))
{
TOKEN[i]=ch;i++;
ch=fgetc(fp);
}
TOKEN[i]=′\0′
fseek(fp,-1,1);/*retract*/
c=lookup(TOKEN);
if(c==0)out(ID,TOKEN);
elseout(c,"");
}
Else
if(isdigit(ch))
{
TOKEN[0]=ch;
ch=fgetc(fp);i=1;
while(isdigit(ch))
{
TOKEN[i]=ch;i++;
ch=fgetc(fp);
}
TOKEN[i]=′\0′;
fseek(fp,-1,1);
out(INT,TOKEN);
}
Else
switch(ch)
{
case′<′:
ch=fgetc(fp);
if(ch==′=′)out(LE,"");
elseif(ch==′>′)out(NE,"");
else
{
fseek(fp,-1,1);
out(LT,"");
}
break;
case′=′:
out(EQ,"");break;
case′>′:
ch=fgetc(fp);
if(ch==′=′)out(GE,"");
else
{
fseek(fp,-1,1);
out(GT,"");
}
break;
default:
reporterror();
break;
}
return;
}
实验二 语法分析器的设计
基本信息
实验课程:
编译原理设课形式:
非独立
课程学分:
4实验项目:
语法分析器的设计
项目类型:
设计项目学时:
4
实验目的
1.掌握自上而下语法分析的基本思想;
2.掌握利用预测分析法进行语法分析的原理和过程;
3.熟悉文法的机内表示;
4.掌握语法分析器的设计与调试,提高编程能力、动手能力以及独立分析问题、解决问题的能力和综合运用所学知识的能力。
实验内容
1.输入任意文法,改写文法使其成为LL
(1)文法。
2.构造文法的预测分析表;
3.设计堆栈和预测分析表的机内表示;
4.设计并书写语法分析程序;
5.调试并运行语法分析程序;
6.实验结果分析
●分析程序中文法存储所采用的数据结构
●分析结果并写出自己的心得体会
提示:
对于所选定的分析方法,如有需要,应选择一种合适的数据结构,以构造所给文法的机内表示。
实验说明:
实验环境:
WINDOWS下,工具为TurboC2.0或VisualC6.0。
实验考核方式:
1.提交实验报告
2.演示程序和答辩(抽查)实验预习
实验辅导
1.设计原理及算法描述
所谓LL
(1)分析法,就是指从左到右扫描输入串(源程序),同时采用最左推导,且对每次直接推导只需向前看一个输入符号,便可确定当前所应当选择的规则。
实现LL
(1)分析的程序又称为LL
(1)分析程序或LL1
(1)分析器。
一个文法要能进行LL
(1)分析,那么这个文法应该满足:
无二义性,无左递归,无左公因子。
当文法满足条件后,再分别构造文法每个非终结符的FIRST和FOLLOW集合,然后根据FIRST和FOLLOW集合构造LL
(1)分析表,最后利用分析表,根据LL
(1)语法分析构造一个分析器。
2.分析过程
LL
(1)的语法分析程序包含了三个部分,总控程序,预测分析表函数,先进先出的语法分析栈,本程序也是采用了同样的方法进行语法分析,该程序是采用了C语言来编写,其逻辑结构图如图2所示。
图2LL
(1)语法分析逻辑结构图
LL
(1)预测分析程序的总控程序在任何时候都是按STACK栈顶符号X和当前的输入符号a做哪种过程的。
对于任何(X,a)总控程序每次都执行下述三种可能的动作之一:
(1)若X=a=‘#’,则宣布分析成功,停止分析过程。
(2)若X=a‘#’,则把X从STACK栈顶弹出,让a指向下一个输入符号。
(3)若X是一个非终结符,则查看预测分析表M。
若M[A,a]中存放着关于X的一个产生式,那么,首先把X弹出STACK栈顶,然后,把产生式的右部符号串按反序一一弹出STACK栈(若右部符号为ε,则不推什么东西进STACK栈)。
若M[A,a]中存放着“出错标志”,则调用出错诊断程序ERROR。
事实上,LL
(1)的分析是根据文法构造的,它反映了相应文法所定义的语言的固定特征,因此在LL
(1)分析器中,实际上是以LL
(1)分析表代替相应方法来进行分析的。
3.构造LL
(1)分析表
例如考查文法G[E]:
E→E+T|T
T→T*F|F
F→(E)|i|
我们容易看出此文法没有左公因子也没有二义性,但却存在两个直接左递归,这里我们利用引入新非终结符的方法来消除它使方法满足要求,即:
(1)改写文法。
对形如:
U→Ux|y的产生式(其中x,yV+,y不以U开头),引入一个新的非终结符U’后,可以等价地改写成为:
U→yU’
U’→xU’|ε
显然改写后,U和U’都不是左递归的非终结符。
因此文法G[E]按上述方法消去左递归后可等价地写成:
E→TP
P→+TP|ε
T→FQ
Q→*FQ|ε
F→(E)|i
(2)FIRST和FOLLOW集合构造。
在构造LL
(1)预测分析表之前,首先要构造该文法的每个非终结符的FIRST和FOLLOW集合,按照下面描述的算法来构造这两个集合。
①FIRST集合的构造算法:
●若X∈VT,则FIRST(X)={X}。
●若X∈VN,且有产生式X→a……,则把a加入到FIRST(X)中;若X→ε也是一条产生式,则把ε也加到FIRST(X)中。
●若X→Y……是一个产生式且Y∈VN,则把FIRST(Y)中的所有非ε-元素都加到FIRST(X)中;若X→Y1Y2…Yk是一个产生式,Y1,…,Yi-1都是非终结符,而且,对于任何j,1≤j≤i-1,FIRST(Yj)都含有ε(即Y1…Yi-1*ε),则把FIRST(Yj)中的所有非ε-元素都加到FIRST(X)中;特别是,若所有的FIRST(Yj)均含有ε,j=1,2,…,k,则把ε加到FIRST(X)中。
连续使用上面的规则,直至每个集合FIRST不再增大为止。
②FOLLOW集合的构造算法:
●对于文法的开始符号S,置#于FOLLOW(S)中;
●若A→αBβ是一个产生式,则把FIRST(β)|{ε}加至FOLLOW(B)中;
●若A→αB是一个产生式,或A→αBβ是一个产生式而βε(即ε∈FIRST(β)),则把FOLLOW(A)加至FOLLOW(B)中。
连续使用上面的规则,直至每个集合FOLLOW不再增大为止。
算法中利用二维数组分别表示各产生式右部的FIRST和左部的FOLLOW集合
(3)预测分析表构造。
构造G[E]的LL
(1)预测分析表。
预测分析表M[A,a]是如下形式的一个矩阵。
A为非终结符,a是终结符或‘#’。
矩阵元素M[A,a]中存放这一条关于A的产生式,指出当A面临输入符号a是所应采用的规则。
M[A,a]也可能存放一条“出错标志”,指出当A根本不该面临输入符号a。
算法中利用二维数组存储分析表。
4.利用分析表进行预测分析
(1)总控程序的流程图
LL
(1)分析法总控程序流程图如图3所示。
图3LL
(1)分析法总控程序流程图
(2)总程序的算法描述如下:
BEGIN
首先把‘#’然后把文法开始符号推进STACK栈;
把第一个输入符号读进a;FLAG:
=TRUE;
WHILEFLAGDO
BEGIN
把栈顶符号出栈到X中;
IFXinVTTHEN
IFX=aTHEN把下一输入符号读进a
ELSEERROR
ELSEIFX=‘#’THENIFX=aTHENFLAG:
=FALSE
ELSEERROR
ELSEIFM[A,a]={X→x1x2…xk}THEN把xk,xk–1,…,x1依次进栈/*若x1,x2…xk=e,则不进栈*/
ELSEERROR
ENDOFWHILE;STOP/*分析成功,过程结束*/
END
(5)实例分析
①实例
考查文法G[E]:
E→E+T|T
T→T*F|F
F→(E)|i|
文法改造为LL
(1)文法为测试文法G[E]:
E→TG
G→+TG|^
T→FS
S→*FS|^
F→(E)|i
分析例句:
i*(i)#,i+i#
输入:
串w和文法G的分析表M。
输出:
如果W属于L(G),则输出W的最左推导,否则报告错误。
方法:
开始时,#S在分析栈中,其中S是文法的开始符号,在栈顶;令指针ip指向W#的第一个符号;
②主要数据结构描述
chartermin[50];/*终结符号*/
charnon_ter[50];/*非终结符号*/
charv[50];/*所有符号*/
charleft[50];/*左部*/
charright[50][50];/*右部*/
intM[20][20];/*二维数组存储分析表*/
栈T用来存放产生式的右边
Str数组存放要分析的句子串
③可参考的C语言源程序
V/*LL
(1)分析法源程序,只能在VC++中运行 */
#include
#include
#include
#include
charA[20];/*分析栈*/
charB[20];/*剩余串*/
charv1[20]={'i','+','*','(',')','#'};/*终结符 */
charv2[20]={'E','G','T','S','F'};/*非终结符 */
intj=0,b=0,top=0,l;/*L为输入串长度*/
typedefstructtype/*产生式类型定义 */
{ charorigin;/*大写字符 */
chararray[5];/*产生式右边字符*/
intlength;/*字符个数 */
}type;
typee,t,g,g1,s,s1,f,f1;/*结构体变量 */
typeC[10][10];/*预测分析表 */
voidprint()/*输出分析栈 */
{ inta;/*指针*/
for(a=0;a<=top+1;a++)
printf("%c",A[a]);
printf(""t"t");
}/*print*/
voidprint1()/*输出剩余串*/
{ intj;
for(j=0;j
printf("");
for(j=b;j<=l;j++)
printf("%c",B[j]);
printf(""t"t"t");
}/*print1*/
voidmain()
{ intm,n,k=0,flag=0,finish=0;
charch,x;
typecha;/*用来接受C[m][n]*/
/*把文法产生式赋值结构体*/
e.origin='E';
strcpy(e.array,"TG");
e.length=2;
t.origin='T';
strcpy(t.array,"FS");
t.length=2;
g.origin='G';
strcpy(g.array,"+TG");
g.length=3;
g1.origin='G';
g1.array[0]='^';
g1.length=1;
s.origin='S';
strcpy(s.array,"*FS");
s.length=3;
s1.origin='S';
s1.array[0]='^';
s1.length=1;
f.origin='F';
strcpy(f.array,"(E)");
f.length=3;
f1.origin='F';
f1.array[0]='i';
f1.length=1;
for(m=0;m<=4;m++)/*初始化分析表*/
for(n=0;n<=5;n++)
C[m][n].origin='N';/*全部赋为空*/
/*填充分析表*/
C[0][0]=e;C[0][3]=e;
C[1][1]=g;C[1][4]=g1;C[1][5]=g1;
C[2][0]=t