编译原理课程设计LR(1)语法分析构造器的设计Word下载.doc
《编译原理课程设计LR(1)语法分析构造器的设计Word下载.doc》由会员分享,可在线阅读,更多相关《编译原理课程设计LR(1)语法分析构造器的设计Word下载.doc(55页珍藏版)》请在冰点文库上搜索。
2.巩固学生学习的编译原理、程序设计语言、数据结构等课程的基础知识,训练学生分析和解决编译系统的相关问题的能力,提高学生的综合素质。
3.从软件工程的角度来看,《编译原理》课程设计是一个很好的实例,可以训练学生软件设计的能力以及编码调试能力。
2、本课题任务的主要内容
本课程设计主要内容包括以下几点:
1、根据选定的题目,查阅资料,熟悉相关理论、方法;
(1)掌握文献检索方法,以获得编译系统开发技术等相关资料;
(2)学习并熟练使用一种4GL开发平台(如VC++、Java、Dephi、PB、VB等);
2、分析问题,确定系统逻辑结构;
3、确定系统所需模块及模块结构,并用流程图描述各模块;
4、编码及调试程序;
5、撰写课程设计说明书。
3、提交的成果
1、一份符合课程设计说明书撰写规范的课程设计说明书。
2、一套系统原型。
附录一:
课程设计说明书撰写要求
1、基本要求:
(1)能反映完成了设计内容要求;
(2)要求撰写不少于5000个文字(20页)的文档;
(3)文档中至少要包括:
数据流图、逻辑结构图、系统功能图、算法流程图。
(4)用户界面设计:
附界面的抓图或手工绘图,及其主要核心部分代码。
2、文档格式要求(参考课程设计参考模板)
(1)封面
(2)前言
(3)目录
(4)课程设计任务书
(5)正文(分章、层次等,每一章从新一页开始)
.概述
包括项目背景、编写目的、软件定义、开发环境等内容。
.需求分析
问题陈述、需完成的功能。
以数据流图和数据字典表达。
.逻辑设计
描述系统组织和基本工作流程。
以总体逻辑结构图表达。
.总体设计
画出软件功能图,描述每一个功能所完成的任务情况。
.界面设计
界面设计要合理,给出主要界面和主要代码并有适当的说明。
(6)小结
(7)参考文献
对于引用的参考文献,列出主要参考文献(至少10篇)的题录及摘要或参考文献原文。
(8)其他图表原始资料或参考资料附录
第1章概述
1.1背景
全面、深入地探讨了编译器设计方面的重要主题,包括词法分析、语法分析、语法制导定义和语法制导翻译、运行时刻环境、目标代码生成、代码优化技术、目标代码生成。
语法分析是整个编译程序的核心部分,而LR分析方法对文法要求比起其他分析方法能力较强
LR(k)分析方法是1965年Knuth提出的,括号中的k表示向右查看输入串符号的个数。
这种方法比起自顶向下的LL
(1)分析方法和自低向上的优先分析方法对文法的限制要少的多,也就是说对于大多数用无二义性上下文无关文法描述的语言都可以用相应的LR分析器进行识别,而且这种方法还具有分析速度快,能准确、即时地指出出错位置。
自低向上分析的关键问题是在分析过程中如何确定句柄。
LR分析法正是给出一种能根据当前分析栈中的符号串(通常以状态表示)和向右顺序查看输入串的k个(k≥0)符号就可惟一地确定分析器的动作是移进还是归约和用哪个产生式归约,因而也就能惟一地确定句柄。
LR分析法的归约过程是规范推到的逆过程,所以LR分析过程是一种规范归约的过程。
1.2目的
因为LR(0)分析过程中不需要向右查看输入符号,因而它可以对文法的限制较大,对绝大多数的高级语言的语法分析器是不能适用的,所以,要分析绝大多数的高级语言编译程序的需要,采用向后查看一个输入符号的方法,即LR
(1)的方法。
(1)掌握并深刻理解有穷自动机在LR分析法中的应用(即LR分析器)。
(2)掌握LR分析法的思想,学会特定分析表的构造方法,利用给出的分析表进行LR分析。
1.3软件定义
对任意给定的上下文无关文法G,构造其LR
(1)项目集规范族、预测分析表,并且在此基础上进一步构造其LR
(1)分析表。
1.4开发环境
软件:
Windows7操作系统,MicrosoftVisualC++6.0;
编程语言:
C++
第2章需求分析
2.1问题陈述
设计的题目是要对LR
(1)类文法判定及其分析器进行构造。
如果一个文法的LR
(1)分析表不含多重入口时,(即任何一个LR
(1)项目集中无移进—归约冲突或归约—归约冲突),则称该文法为LR
(1)文法,所构造的相应分析表称为LR
(1)分析,使用LR
(1)分析表的分析器称为LR
(1)分析器或称规范的LR分析器。
所以,要判断一个文法是否是LR
(1)类文法,则主要是看是否存在两个冲突。
2.2需完成的功能
一个LR分析器由3个部分组成:
(1)总控程序:
也可以成为驱动程序。
对所有的LR分析器总控程序都是相同。
(2)分析表或分析函数:
不同的文法分析表将不同,同一个文法采用的LR分析器不同时,分析表也不同,分析表又可分为动作(ACTION)表和状态转换(GOTO)表两个部分,它们都可用二维数组表示。
(3)分析栈:
包括文法符号栈和相应的状态栈。
它们均是先进后出栈。
分析器的动作由栈顶状态和当前输入符号所决定(LR(0)分析器不需要向前查看输入符号)。
综上所述,要实现本次设计所需工作主要有以下方面:
1、构造项目集规范族;
2、构造预测分析表;
3、设计总控程序,完成分析过程。
第3章逻辑设计
3.1模块设计
LR分析器工作过程示意图如图1.1所示。
其中SP为栈指针,S[i]为状态栈,X[i]为文法符号栈。
状态转换表内容按关系GOTO[Si,X]=Si确定,该关系式是指当栈顶状态为Si,遇到当前文法符号为X时应转向状态Sj。
X为终结符或非终结符。
ACTION[Si,a]规定了栈顶状态Si时遇到输入符号a应执行的动作。
动作有4种可能:
(1)移进:
当Si=GOTO[Si,a]成立,则把Si移入到状态栈,把a移入到文法符号栈。
其中i,j表示状态号。
SP
Sn
S1
Xnn
X1
X0
S0
.
总控程序
ACTION
表
GOTO
输入串XXXXXXXX…#
输出
图1-1LR分析器工作过程示意图
(2)归约:
当在栈顶形成句柄为β时,则用β归约为相应的非终结符A,即当文法
中有A→β的产生式,而β的长度为r(即|β|=r),则从状态和文法符号栈中自顶向下去掉r个符号,即栈指针SP减去r。
并把A移入文法符号栈内,再把满足Sj=GOTIO[Si,A]的状态移入状态栈,其中Si为修改指针后的栈顶状态。
(3)接受acc:
当归约到文法符号栈中只剩文法的开始符号S时,并且输入符号串已结
束即当输入是‘#’,则为分析成功。
(4)报错:
当遇到状态栈顶为某一状态下出现不该遇到的文法符号时,则报错,说
明输入串不是该文法所能接受的句子。
LR分析器的关键部分是分析表的构造。
构造LR分析表,那么先解决LR项目集规范族的构造。
LR项目集规范族的项目类型分为如下四种:
1、移进项目
圆点后为终结符的项目,形如A→α·
aβ,其中α、β∈V*,a∈VT,相
应状态为移进状态。
2、规约项目
圆点在产生式右部的最后的项目,形如A→β·
其中β∈V*,对于β=ε的
项目为A→·
(对应的产生式为A→ε),相应状态为归约状态。
3、待约项目
圆点后为非终结符的项目,形如A→α·
Bβ,其中α、β∈V*,B∈VN,
这表明用产生式A的右部归约时,首先要将B的产生式右部归约为B,对A的右部才能继续进行分析。
也就是期待着继续分析过程中首先能进行归约得到B。
4、接受项目
当归约项目为S’→S·
时则表明已分析成功,即输入串为该文法的句子,
相应状态为接受状态。
3.1.1LR
(1)项目集规范族的构造算法
以S’→S·
,#属于初始项目集中,把“#”号作为向前搜索符,表示活前缀为γ(若γ是有关S产生式的某一右部)要归约成S时,必须面临输入符为“#”号才行。
我们对初始项目S’→S·
,#求闭包后再用转换函数逐步求出整个文法的LR
(1)项目集族。
具体构造步骤如下:
(1)构造LR
(1)项目集的闭包函数。
①假定I是一个项目集,I的任何项目都属于CLOSURE(I)。
②A→α·
Bβ,a属于CLOSURE(I),B→γ是文法中的产生式,若有项
目β∈V*,b∈FIRST(βa),则B→·
γ,b也属于CLOSURE(I)中。
③重复②直到CLOSURE(I)不再增大为止。
(2)构造转换函数。
LR
(1)转换函数的构造与LR(0)的相似,GO(I,X)=CLOSURE(J)
其中I是LR
(1)的项目集,X是文法符号:
J={任何形如LR
(1)分析表的构造[A→αX·
β,a]的项目|[A→α·
Xβ,a]∈I}
对文法G’的LR
(1)项目集族的构造仍以[S’→S·
,#]为初态的初始项目,然后对其求闭包和转换函数,直到项目集不再增大。
3.1.2LR
(1)分析表的构造算法
由于一个LR
(1)项目可以看成两个部分组成,一部分和LR(0)项相同,这部分称为心,另一部分为向前搜索符合集。
因而LR
(1)分析表的构造与LR(0)分析表的构造在形式上基本相同,只是归约项目的归约动作取决于归约项目的向前搜索符集,即只有当面临的输入符属于向前搜索符的集合,才做归约动作,其他情况均出错。
具体构造过程如下:
若已构造出某文法的LR
(1)项目集族C。
C={I0,I1,...,In}其中的Ik的k为分析器的状态,则动作ACTION表和状态转换GOTO表构造方法如下:
(1)若项目[A→α·
aβ,b]属于Ik,且GO(Ik,a)=Ij,其中a
∈VT,,则置ACTION[k,a]=Sj。
其Sj的含义是把输入符号a和状态j分别移入文法符号栈和状态栈。
(2)若项目[A→α·
,a]属于Ik,则置ACTION[k,a]=rj,其中a
∈VT,rj的含义为把当前栈顶符号串α归约为A(即用产生式A→α归约)。
j为在文法中对产生式A→α的编号。
(3)若项目[S’→S·
,#]属于Ik,则置ACTION[k,#]=“acc”,
表示接受。
(4)若GO(Ik,A)=Ij,其中A∈VN,则置GOTO[k,A]=j。
表示转入j状态,则置当前文法符号栈顶为A,状态栈顶为j。
(5)凡不能用规则
(1)~(4)填入分析表中的元素,均置“报错
标志”。
可以填入空白以表示。
3.2流程图
图1-2(a)构造LR
(1)项目集规范族流程图
图1-2(b)构造LR
(1)项目集规范族流程图
图1-2(c)构造LR
(1)项目集规范族流程图
图1-3构造LR
(1)分析表流程
图1-4LR
(1)串分析程序流程图
第4章总体设计
功能设计
为能实现本次课程设计的功能,同时减小工作量,根据需求,构造LR
(1)项目集规范族需要用到FIRST集,主要思路是采用替换策略以及删除重复的元素。
数据结构使用的是链表。
这不是核心部分,所以不再赘述。
将功能分为三个大模块,即构造项目集规范族、分析表、和分析串程序。
以下是对构造项目集规范族、分析表、和分析串程序的设计叙述:
4.1构造项目集规范族模块
项目集规范族是整个设计的基础。
数据结构:
实现其构造,在数据结构选用方面要求能够方便查找、而
且能够节省空间。
据此,主要采用了C++中STL中队列、集合、向量,作为主要数据存放的容器,并用结构体存放重要产生式的重要信息。
算法设计:
由课本上关于构造项目集规范族的叙述,联想到了图的广度遍历,
所以将扩展后的文法[S’→·
S,#]作为核放在队列中,由核求闭包,每求出一个就放入集合中(主要是可以去除重复的)如果插入集合成功,就把结构体插入向量中,插入队列中,直到队列空,说明由核产生的项目已经构造完,下一步只需从向量中取出项目进行闭包运算,直到把向量中所有元素取完,这样就可以把整个规范族求出。
程序流程主要是一个WHILE循环和switch语句控制。
4.2构造预测分析表模块
预测分析是分析过程的依据。
用的是结构体存放数据,数据信息主要包括:
ACTION表,当前输入符、是移进还是归约的控制符号,归约的产生式编号和移进的状态。
GOTO表,遇到的非终结符、转到的状态号。
这里主要是查找,利用上一步求出的项目集规范族,找出输入符号以后所转向的状态,或者是归约的产生式编号。
4.3分析串程序模块
分析串程序是最终功能的具体表示。
用的是两个先进先出栈,即状态栈和符号栈,另外还用一个string类型输入串。
根据预测分析表,开始时把‘0’压入状态栈,把‘#’压入符号栈中,根据状态栈的栈顶元素和输入串的串首字符两个线索,在分析表中查找,找到相应的动作,并实现。
如果找到,则继续分析,否则,说明该输入串是不能接受的。
根据题目需要,还要判断是否是LR
(1)文法,我们知道一个二义性文法绝不是LR类文法,对于这一功能也以与实现。
主要的实现方法是通过分析表,如果分析表里有两个入口,也就是存在两个冲突,那么就说明不是。
第5章界面设计
图1-5开始界面—输入产生式文件
代码如下:
voidmain()
{
voidFirst(structVnpv2[],inti,sqfirst[],intc,chars2[],charvn1[]);
intFind(pseqstackS,sqst[],intv);
voiddels(sqst[],inti);
voidproject_set(charvn1[],intc,structset2select[],stringft[]);
intForecast_analyse(charvn1[],intc);
//进行预测分析
inti,n,t,h=0,j=0,c=0,l;
charVN,T='
@'
;
pls1;
stringft[10];
pseqstackS;
sqfirst[20],p;
charvn1[20],string[20],scanout[20],s2[2]={'
-'
'
>
'
};
FILE*fp;
pv1=pv;
pv4=pv2;
S=Init_seqstack();
cout<
<
"
\t*说明*\n"
<
\t*本系统设计了一些产生式分别存放在*\n"
\t*以下文件中:
1.txt,f.txt,H.txt*\n"
\t*in.txt,L.txt,L2.txt,ll.txt,lr.txt*\n"
\t*wd.txt结果输出存放在文件result.txt*\n"
\t*中,请用户按照上述文件名书写规范进*\n"
\t*行操作使用*\n"
\t*制作人:
计算机071李亚龙*\n"
\t*~~~~~~~~~~~~欢迎使用~~~~~~~~~~~~~~*\n"
<
\t*==================================*\n"
printf("
请输入所用产生式的文件名:
);
scanf("
%s"
scanout);
if((fp=fopen(scanout,"
r"
))==NULL)
printf("
\n打开文件出错了!
\n"
exit(0);
}
getchar();
s2[2]='
\0'
所用的文法是:
endl;
while(fgets(string,20,fp)!
=NULL&
&
!
feof(fp))//从文件读入产生式
{cout<
string;
VN=string[0];
if(VN!
=T)
{pv[c].ch=VN;
pv2[c].ch=VN;
vn1[c]=pv[c].ch;
pv[c].tag=1;
pv2[c].tag=1;
select[h].n=c;
pv[c].info=-1;
pv2[c].info=-1;
for(i=0,l=0,n=3;
string[n]!
='
\n'
n++)
{
string[i++]=string[n];
select[h].NVT[l++]=string[n];
}
string[i]='
select[h].NVT[l]='
select[h].len=l;
pv[c].p=(pl)malloc(sizeof(l));
init(pv[c].p);
pv2[c].p=(pl)malloc(sizeof(l));
init(pv2[c].p);
strassign(pv1[c].p,string);
strassign(pv4[c].p,string);
T=pv[c].ch;
t=c;
c++;
h++;
}
else
{
s1=(pl)malloc(sizeof(l));
init(s1);
pv[t].tag++;
pv2[t].tag++;
select[h].n=t;
for(i=0,n=3;
select[h].