SLR分析器的分析与构造.docx
《SLR分析器的分析与构造.docx》由会员分享,可在线阅读,更多相关《SLR分析器的分析与构造.docx(18页珍藏版)》请在冰豆网上搜索。
SLR分析器的分析与构造
SLR分析器的分析与构造
作者姓名:
张锐指导老师:
张玉州
摘要:
语法分析是编译程序的重要组成部分,LR分析法是一种自下而上的语法分析方法,其适用范围大,在编译程序中大都采用此法进行语法分析,SLR分析则是LR中一种较简单有效的分析法;语法分析作为编译过程中一个不可缺少的步骤,对其进行研究有着非常重要的意义。
本文阐述了语法分析的方法、地位及其意义,并对SLR分析器的分析与构造进行了具体的分析和探讨。
关键词:
语法分析,自下而上分析,LR分析,SLR分析
1引言
编译程序是把用高级语言(如PASCAL语言、C语言)编写的源程序翻译为与之等价的目标程序(如汇编语言程序、机器语言程序)的一种翻译程序,其工作过程非常类似于自然语言之间的翻译。
为了把源程序翻译成与之等价的目标程序,编译程序一般要做词法分析、语法分析、语义分析、代码优化和代码生成等五个方面的工作。
从而编译程序常由词法分析程序、语法分析程序、语义分析程序、代码优化程序和目标代码生成程序等五个主要部分组成,这五个部分相辅相成,互相联系,紧密相关。
其中词法分析程序主要是依据词法规则从左到右扫描输入的源程序,进行词法分析,输出单词符号;语法分析程序主要是依据语法规则对单词符号进行语法分析,输出由语法单位构成的语法树,判断输入串是否构成语法上正确的“程序”;语义分析程序依据语义规则把语法分析程序归约出(或推导出)的语法单位翻译成一定形式的中间代码;代码优化程序依据程序等价变换原则对中间代码进行优化处理;目标代码生成程序把优化后的中间代码翻译成目标程序。
[1]
语法分析方法不仅在编译程序及相关语言翻译程序构造中具有非常重要的作用,而且在其它领域也具有很多重要的应用。
本文所讨论的问题是如何用C语言实现SLR分析器的分析与构造。
一个LR分析器主要由总控程序、分析表与分析栈三部分组成,分析表是LR分析器的核心部分,有四种不同的LR分析表,SLR分析表是一种比较容易实现又有使用价值的,能够处理一些移进-归约冲突的分析法的基础。
不同的LR分析法、不同的文法,其LR分析总控程序是相同的,差别仅在于LR分析表的不同,故SLR语法分析器的构造实质就是SLR分析表的构造。
2编译器的构造及编译程序的工作过程[1][2]
2.1什么是编译器
通常情况下,人们将能够完成一种语言到另一种语言变换的软件称为翻译器,而我们要谈的编译器就是其中的一类。
所以编译器是一种计算机程序,是将便于人编写、阅读、维护的高级计算机语言翻译为计算机能解读、运行的低阶机器语言的程序,即编译程序。
2.2编译器的构造
通常编译器被分为前端和后端,前端的工作主要依赖于源语言而与目标机无关,后端工作依赖于目标机而一般不依赖源语言。
通常前端包括词法分析、语法分析、语义分析和中间代码生成这些阶段,某些优化工作,即中间代码优化也可在前端做,也包括与前端每个阶段相关的出错处理工作和符号表管理等工作。
后端工作包括目标代码生成和目标代码优化,以及相关出错处理和符号表操作。
2.3什么是编译程序
从功能上看,一个编译程序就是一个语言翻译程序。
它把一种语言(称作源语言)书写的程序翻译成另一种语言(称作目标语言)的等价的程序。
源语言通常是一个高级语言,如Pascal、C++、Java等,而目标语言通常是一个低级语言,如汇编或机器语言。
编译程序的功能如图1所示:
图1编译程序的功能
2.4编译程序的工作过程
编译过程是指从输入源程序开始到输出目标程序为止的整个过程。
为了把源程序翻译成与之等价的目标程序,编译程序一般要做词法分析、语法分析、语义分析、中间代码产生、代码优化和代码生成六个方面的工作,因此通常将编译过程划分成五个阶段(习惯上为了方便讨论,将语义分析与中间代码的产生看作一个阶段)。
如下便是编译过程框图:
图2编译过程框图
3语法分析
语法分析是编译程序的重要组成部分,自下而上分析是语法分析的一种常用方法。
语法分析器主要采用上下文无关文法的自上而下分析程序来进行构造。
3.1语法分析器的地位[3]
语法分析是编译过程的核心部分,它是在词法分析识别出单词符号串的基础上,分析并判定程序的语法结构是否符合语法规则。
语法分析器在编译程序中的地位如图所示:
图3语法分析器在编译程序中的地位
3.2语法分析的任务[4]
语法分析的任务是在词法分析的基础上,根据语言的语法规则,把单词符号串分解成各类语法单位(语法范畴)如“短语”、“子句”、“句子”(语句)、“程序段”和“程序”等。
(1)依循规则:
语言的语法规则(文法)
(2)描述工具:
上下文无关文法
<1>文法:
描述语言语法结构的形式规则(语法规则)
<2>文法的基本组成
终结符号:
组成语言的基本符号(基本字、标识符、常数、算符、界符),用小写字母a,b,c。
非终结符号(语法变量):
代表语法范畴,表示一定符号串的集合,用大写字母A,B,C。
开始符号:
一种特殊的非终结符号
产生式(产生规则、规则):
形如A->α,A是一个非终结符,α是由终结符或非终结符号组成的一个符号串。
3.3语法分析方法种类(按语法分析树建立方法)
自上而下分析----------递归下降分析、预测分析
自下而上分析----------算符优先分析、LR分析
4LR分析
4.1什么是LR分析法[5]LR分析法是一种从左至右扫描、自下而上的语法分析方法,从输入流开始,根据文法的规则,从语
法树的下部通过无回溯的移进归约,最终通过能否推出开始符号来判断输入流是否合法。
由于其适用范围大,在编译程序中大都采用此法进行语法分析。
LR分析方法中L表示从左到右扫描输入串,R表示构造一个最右推导的逆过程,能用LR分析器分析的文法类,包含能用LL
(1)分析器分析的全部文法类。
LR分析法在自左至右扫描输入串时,能发现其中的任何错误,并能准确地指出出错位置。
4.2LR分析方法的基本思想
LR分析方法的基本思想是:
记住历史、展望未来、定夺现在。
在规范归约过程中,一方面记住已移进和归约的整个符号串,即记住“历史”,另一方面根据所用产生式推测未来可能碰到的输入符号,即对未来进行“展望”。
根据“历史”,“展望”以及现实输入符号三方面材料,来确定栈顶的符号是否构成相对某一产生式的句柄,若是一个句柄,则进行归约。
[6]
4.3LR分析的特点:
(1)采用最一般的无回溯移进—归约方法;
(2)可分析的文法是LL文法的真超集;
(3)能够及时发现错误;
(4)分析表较复杂,难以手工构造。
4.4LR分析法的分类
实际使用的LR分析法通常可分为:
LR(0)分析法、SLR
(1)分析法(简单LR分析)、LR
(1)分析法和LALR
(1)分析法(向前LR分析),相应的就有LR(0)表、简单LR表(SLR表)、规范LR表(LR
(1)表)、向前LR表(LALR表)。
LR分析器根据分析表构造的不同,可以有LR(0),SLR
(1),LALR
(1),LR
(1)分析器。
它们功能的强弱和构造的难度依次递增。
4.5LR分析器结构[3][6]
一个LR分析器实质上是一个带先进后出存储器(栈)的确定有限状态自动机。
一个LR分析器主要由总控程序、分析表与分析栈三部分组成:
图4LR分析器模型
(1)分析栈用来存放状态和移进归约的符号串:
假设(s0,#)为分析开始前预先放到栈里的初始状态和句子括号,sm表栈顶状态,X1X2…Xm是至今已移进归约出的部分。
(2)分析表是LR分析器的核心部分,其构造方法有四种,因此有4种不同的LR分析表:
LR(0)分析表是最简单的一种,它是其它一般LR分析法的基础;SLR分析表是一种比较容易实现又有使用价值的,能够处理一些移进-归约冲突的分析法的基础;LR
(1)分析表能力最强,能够适用一大类文法,但实现价值过高;LALR
(1)分析表的能力介于SLR和LR
(1)分析方法之间,稍加努力,就可以高效实现。
LR分析表主要有动作表和状态转换表两个部分组成,它们都是二维数组。
其中动作表ACTION[s,a]规定了当状态s面临输入符号a是应采取的动作。
状态转换表GOTO[s,X]规定了状态s面对文法符号X(终结符或非终结符)时下一个状态是什么。
显然,GOTO[s,X]定义了一个以文法符号为字母表的DFA.
(3)总控程序表示具体的分析过程:
一个LR分析器工作过程可看成是栈里的状态序列,已归约串和输入串所构成的三元式的变化过程。
分析开始时的初始三元式为(s0,#,a1a2…an#)其中,s0为分析器的初态;#为句子的左括号;a1a2…an为输入串;其后的#为结束符(句子右括号)。
分析过程每步的结果可表示为(s0s1s2…sm,#X1X2…Xm,aiai+1…an#)。
分析器的下一步动作是由栈顶状态sm和现行输入符号ai所唯一决定的。
即执行ACTION[sm,ai]所规定的动作。
经执行每种可能的动作之后,三元式的变化情形是:
(Ⅰ)若ACTION[sm,ai]为移进,且s=GOTO[sm,ai],则三元式变为(s0s1s2…sms,#X1X2…Xmai,ai+1…an#)。
(Ⅱ)若ACTION[sm,ai]={A→β},则按A→β进行归约,则三元式变为(s0s1s2…sm-rs,#X1X2…Xm-rA,aiai+1…an#),其中s=GOTO[sm-r,A],r为β的长度,β=Xm-r+1…Xm。
(Ⅲ)若ACTION[sm,ai]为“接受”,三元式不再变化,分析成功。
(Ⅳ)若ACTION[sm,ai]为“报错”,三元式变化过程终止,报告错误。
5SLR分析
实际使用的LR分析法通常可分为:
LR(0)分析法、SLR
(1)分析法(简单LR分析)、LR
(1)分析法和LALR
(1)分析法(向前LR分析)。
由于SLR
(1)分析法的实现代价远远低于LR
(1)分析法,算法又较LALR
(1)分析法简单,而且基本能够解决程序设计语言的词法/语法分析问题,因此SLR
(1)分析法更具有实用价值。
5.1SLR
(1)分析器
SLR
(1)分析器也称SLR分析器,其中S是简单的意思,在它工作时可以根据简单向前看一个终结符来确定下一步动作。
假定LR(0)规范族的一个项目集I中含有m个移进项目,则隐含的动作冲突可通过检查现行输入符号a属于某个集合而解决,这种解决冲突性动作的方法称SLR
(1)解决办法。
SLR
(1)分析器的基本组成:
LR()控制器+SLR
(1)分析表。
SLR
(1)分析器是LR(0)分析器的改进与扩充,其中的1是指:
在分析过程中,顶多只要向前看一个符号,即当句柄识别器发生了移进和归约冲突时,通过查看当前符号就可解决之意。
5.2SLR分析表的构造[3][7]
5.2.1引例
假定一个LR(0)规范族中含有如下一个项目集(状态)I:
I={X→α·bβ,A→α·,B→α·}其中,第一个项目是移进项目;第二、三项目是归约项目。
这三个项目告诉我们应做的动作各不相同,互相冲突。
第一个项目告诉我们应该把下一个输入符号b(如果是b)移进;第二个项目告诉我们应该把栈顶的α归约为A;第三个项目则说应把α归约为B。
解决冲突的一种简单办法是,分析所有含A或B的句型,考察句型中可能直接跟在A或B之后的终结符,也就是说,考察集合FOLLOW(A)和FOLLOW(B)。
如果这两个集合不相交,而且都不包含b,那么当状态I面临a时,可采取如下“移进-归约”决策:
(1)若a=b,则移进;
(2)若a∈FOLLOW(A),则用产生式A→α进行归约;
(3)若a∈FOLLOW(B),则用产生式B→α进行归约;
(4)此外,报错。
一般而言,若LR(0)项目集规范族的一个项目集I中含有m个移进项目;A1→α·a1β1,A2→α·a2β2,……,Am→α·amβm;同时含有n个归约项目:
B1→α·,B2→α·,……,Bn→α·,若集合{a1,a2,……,am},FOLLOW(B1),……,FOLLOW(Bn)两两不相交(包括不得有两个FOLLOW集合有#),则隐含在I中的动作冲突可通过检查现行输入符号a属于上述n+1个集合中的哪个集合而获得解决。
这就是:
(1)若a是某个ai,i=1,2,…,m,则移进;
(2)若a∈FOLLOW(Bi),i=1,2,…,n,则用产生式Bi→α进行归约;
(3)此外,报错。
5.2.2SLR分析表的构造
(1)文法G拓广成G’;
(2)构造G’的LR(0)项目集族C和活前缀识别自动机的状态转换函数GO;
(3)使用项目集族C和GO函数按下面的算法构造G’的SLR分析表
假定C={I0,I1,I2,…,In},令每个项目集Ik的下标k为分析器的一个状态,因此,G’的SLR分析表含有状态0,1,…,n。
令那个含有项目S’→·S的Ik的下标为初态,函数ACTION和GOTO子表可按如下方法构造:
(Ⅰ)若项目A→α·aβ属于Ik且GO(Ik,a)=Ij,a为终结符,则置ACTION[k,a]为“把状态j和符号a移进栈”,简记为“sj”;
(Ⅱ)若项目A→α·属于Ik,那么,对任何终结符a,a∈FOLLOW(A),置ACTION[k,a]为“用产生式A→α进行归约”,简记为“rj”其中,假定A→α是文法G’的第j个产生式;
(Ⅲ)若项目S’→S·属于Ik,则置ACTION[k,#]为“接受”,简记为“acc”;
(Ⅳ)若GO(Ik,A)=Ij,A为非终结符,则置GOTO[k,A]=j;
(Ⅵ)分析表中凡不能用规则1至4填入信息的空白格均置上“出错”标志。
5.3LR分析算法(总控程序)
输入:
一个输入串ω和一张LR分析表
输出:
若ω∈L(G),输出对于ω的一个自下而上的分析,否则出错
开始时,分析栈顶(s0,#)输入缓冲区ω#
ip指向输入串ω#的第一个输入符号
while(t=TRUE)do
begin
使s是栈顶状态,a是ip指向的符号
ifaction[s,a]=sjthen/*移进*/
begin
将a和j压入分析栈
修改ip使其指向下一个输入符号
end;
elseifaction[s,a]=rj(A→β)then/*归约*/
begin
从分析栈顶弹出2×|β|个符号(状态符号)
令s’是当前栈顶状态
将A和goto[s’,A]压入分析栈
{输出产生式A→β}
end
elseifaction[s,a]=accthenreturn/*成功*/
elseerror()/*出错*/
endif;
endif;
endif;
endofwhile;
end;
5.4实例
对于文法G:
E→E-T|TT→T*F|FF→-F|id它的全部LR(0)项目:
E→·E-TE→E·-TE→E-·TE→E-T·
E→·TE→T·
T→·T*FT→T·*FT→T*·FT→T*F·
T→·FT→F·
F→·-FF→-·FF→-F·
F→·idF→id·
FIRST(F)={-,id}FIRST(T)={-,id}FIRST(E)={-,id}FIRST(E')={-,id}
FOLLOW(E')={#}FOLLOW(E)={-,#}FOLLOW(T)={*,-,#}FOLLOW(F)={*,-,#}
I1、I2、I9,均有移进/归约冲突。
但是:
I1:
FIRST(-T)∩FOLLOW(E')=Φ
I2、I9:
FIRST(*F)∩FOLLOW(E)=Φ所以:
此文法是SLR
(1)文法。
6SLR分析器的数据结构
6.1语法分析的存储结构[8]
语法分析过程涉及的数据存储结构有许多种。
常用的如:
数组、栈、表结构、多维链表结构等。
这主要取决于语法分析技术本身用到的模型定义和语法范畴的文法和分析表的性质。
6.1.1文法的产生式存储
文法的产生式存储以数组加链表结构并用来实现:
产生式右部的所有候选项用自定义的数据结构结点来存储,左部非终结符用一维数组来存放,同该一维数组相对应定义一组与之同步的链头指针数组来指向各自的候选项链表。
对应的存储数据结构及示意图如下:
typedefstructHxxNode//定义存放候选项的结点
{intno;//表示该结点存放的候选项在所处产生式中为第‘no’个
intsize;//表示该结点存放的候选项的大小,即字符串的长度
charStr[10];//存放候选项字符串
structHxxNode*next;//指向紧接其后的下一个存放候选项的结点
}HxxNode,*PHN;
在结点的操作过程中,通过顺序数组找到产生式,每个产生式的多个后选(如果有的话)通过链表查找,这种设计在运行中有较高效率。
6.1.2拓广文法的存储
将文法进行拓广是为了使“接受”状态易于识别,假定文法G是一个以S为开始符号的文法,其拓广文法G’包含了整个G,但是它引进了一个不出现在G中的非终结符S’,并加进一个新产生式S’->S,而这个S’是G’的开始符号,这样,便会有一个仅含项目S’->S·的状态,即唯一的“接受”态。
因此可知,拓广文法的存储较文法产生式的存储不同之处,只是多加了一个新产生式S’->S,且每个产生式只有一个候选项,除此之外原理基本相同,而对于新增产生式S’->S一般被存放于HVn[0]中。
6.1.3文法的项目集规范族的存储
typedefstructIxNode//定义各状态中项目信息的结点
{intno;//项目在拓广文法中所对应产生式的序号
intmo;//标志当前项目是移进、待约、归约、接受中的哪种
intnextIx;//指向当前项目将要移进的下一状态
}IxNode;
typedefstructIxteam//定义一个包含Nom个项目的状态结构
{IxNodeIxxm[Nom];}Ixteam;
如上为定义的文法项目集规范族的数据结构及其示意图,其中Ix[]用来存储状态,即,状态i的所有信息都存储在Ix[i]之中,Ixxm[]用来存储某一状态中的所有项目信息。
6.1.4SLR分析表的存储
分析表顾名思义是一张二维表,所以可以定义一个二维数组来进行存放,行标志状态,列标志移入的终结或非终结符。
以文法G:
E->E+T|T
T->T*F|F
F->(E)|i为例,其对应的SLR分析表如下:
6.2语法分析算法实现的主要功能函数模块
ReadFire()函数的主要功能是:
读取指定路径文件中的文法,并将文法中各产生式存储在相应的数据结构中,以使后续操作可方便进行。
如,对于某个产生式的某个候选项信息存储的第一步操作过程大致为下:
r->no=m;r->size=b;m++;//m表示当前指针所在的产生式的第m个侯选项
fs.get(ch);r->Str[b]='\0';
q=newHxxNode;q->next=r->next;
r->next=q;r=r->next;
b=0;r->Str[b]=ch;b++;
VnFollow()函数的主要功能是:
求出文法中各非终结符的Follow集,如:
对非终结符A来说,Follow(A)是所有句型中出现在紧接A之后的终结符或“#”。
其作用是在分析表的构造中为为归约产生式的选择作参考。
SLR()函数的主要功能是:
构造文法的项目集规范族,然后依之得出SLR分析表。
while(j<=IxxmN[i]-1)//构造文法项目集闭包的大致算法,IxxmN[i]代表状态i中的项目个数
{
if(!
IsVn(HVn[Ix[i].Ixxm[j].no]->Str[Ix[i].Ixxm[j].mo])&&HVn[Ix[i].Ixxm[j].no]->Str[Ix[i].Ixxm[j].mo]!
='\0')j++;//IsVn(ch)函数用来判断ch是否为非终结符
elseif(IsVn(HVn[Ix[i].Ixxm[j].no]->Str[Ix[i].Ixxm[j].mo])&&HVn[Ix[i].Ixxm[j].no]->Str[Ix[i].Ixxm[j].mo]!
='\0')//Ix[i].Ixxm[j].mo表示状态i中项目j的mo值。
{
for(intk=1;k<=VnN;k++)//求闭包
{
if(Ixxmflag[k]==0&&Vn[k]==HVn[Ix[i].Ixxm[j].no]->Str[Ix[i].Ixxm[j].mo])
{IxxmN[i]++;Ixxmflag[k]=1;Ix[i].Ixxm[IxxmN[i]-1].no=k;Ix[i].Ixxm[IxxmN[i]-1].mo=0;}
}
}j++;
}
GYProcess()函数的主要功能是:
针对给定的输入串根据SLR分析表得出其归约具体过程,即实现LR分析器功能。
7运行结果
致谢
在此感谢张玉州老师,在我写论文期间,给了我很多指点和帮助,使我顺利的完成了论文的编写。
感谢和我一起做论文的同学,在我做论文的时候,遇到一些疑难的问题,大家同心协力,使我能够顺利地完成毕业设计。
参考文献
[1]Holub.CompilerDesigninC[M].prentice-Hall,1990.
[2]Lowden.CompilerConstructionPrinciplesandPractice[M].北京:
机械工业出版社,2000.
[3]陈火旺等.程序设计语言编译原理[M].北京:
国防工业出版社,2000.
[4]陈意云,张昱.编译原理[M].北京:
高等教育出版社,2003.
[5]任炎祥.编译原理[M].北京:
高等教育出版社,2004.
[6]周经野,等.编译原理[M].武汉:
武汉理工大学出版社,2003.
[7]高仲仪.编译原理及编译程序构造[M].北京:
北京航天航空大学出版社,1990.
[8]严蔚敏,吴伟民.数据结构(C语言版)[M].北京:
清华大学出版社,1996.
TheanalysisandgenerationofSLRsyntaxanalyzer
Author:
ZhangRuiDirector:
ZhangYu-zhou
Abstract:
Syntaxanalysisisanimportantcomponentofacompiler,LRanalysisisadown-topanalysistechniques,andasitswiderangeofapplication,ithasbeenusedformostofthecompiler.Butasaparsingtechnique,SLRisasimpleandeffectiveoneinLRanalysis.Infact,itisveryimportanttoresearchsyntaxanalysisforbeinganecessarysteptobuildacompiler.Inthispaper,thesyntaxanalysis’smethod,statusandsignificanceareexpounded,furthermoretheanalysisandgenerationofSLRsyntaxanalyzerisdescribedanddeeplyanalyzed