一、引言
编译程序能够将软件语言书写的各种程序处理成可在计算机上执行的程序,是重要的系统软件。在编译系统中,语法分析阶段是整个编译过程中继词法分析后的第二个阶段。语法分析的任务是在词法分析识别出单词符号串的基础上,分析并判定程序的语法结构是否符合语法规则。按照建立语法分析树的方法,语法分析分为两类:自上而下分析和自下而上分析。本文讨论的LR分析法属于自下而上的方法。
二、语法规则的表示
语法规则是由上下文无关文法进行定义的。例如一个语言可用一系列的产生式定义:
<程序>-> begin <语句串> end <语句串> -> <语句><;<语句串>> 等等,为了方便问题描述,把实际含义用符号进行抽象,本文讨论的例子就是抽象后的符号表示的,且LR(0)法进行语法分析要求文法是非二义性的。本文的文法如下:E->aA|bB A->cA|d B->cB|d
三、LR分析法的基本思想
本文所讨论的LR(0)分析法是从句子出发,从左到右扫描符号串,根据当前分析栈中的符号串(通常以状态表示)和向右顺序查看输人串的k 个( ≥0)符号,根据LR分析表,可唯一地确定分析器的动作是移进还是归约以及用哪个产生式对句柄进行归约,直至归约为初始符号为止。此过程就是模拟规范推导的逆过程。
四、LR(0)分析表的构造
整个分析过程中,关键部分是分析表的构造,本文主要讨论LR(0)分析表的构造,即在分析过程中不需要向右查看输入符号。
(一) 拓广文法。由于最后的归约状态必须归约为开始符号,因此为了归约的唯一性,开始符号的产生式必须智能有一个候选式,因此对于开始符号有两个或以上候选式的情况(例如本例),需要对文法进行拓广,加入一条产生式S->E,且把S作为开始符号。
(二)计算LR项目集族。由于LR分析法是模拟规范归约,因此每次归约后的符号串都应称为一个规范句型,那么,已扫描过的字符串就是规范句型的一个前缀,且不含句柄之后的任何符号,称之为活前缀。造表的基本思想就是构造一个识别所有活前缀的有限自动机,再转换成分析表。此有限自动机的状态就是每一个产生式后面加一个圆点后形成的项目。识别文法活前缀的DFA的项目集的全体称为LR项目集规范族。计算方法为:
1、从S一>开始符号产生式开始,求产生式的CLOSURE闭包。设I为任意项目集:
①I的任何项目集都属于CLOSURE (I);②如 A->α·Bβ 在CLOSURE(I)中,那么,对任何关于B的产生式B->γ,项目B->·γ也属于CLOSURE (I) 。③重复执行上述两步骤直至CLOSURE (I)不再增大为止。
2、计算项目集中的所有项目的GO函数:
GO(I,X)=CLOSURE(J)其中,J={任何形如A->αX·β的项目|A->α·Xβ属于I}。
按上述规则,计算出本例的项目集族为I0-I11,分别为:
I0={S’->·E, E->·aA, E->·bB}
I1=GO(I0,E)={S’->E·}
I2=GO(I0,a)={E->a·A , A->·cA , A->·d}
I3=GO(I0,B)={E->b·B, B->·cB, B->·d}
I4=GO(I2,c)={A->c·A, A->·cA, A->·d }
=Go(I4,c)
I5=GO(I3,c)={E->c·B, B->·cB, B->·d}
I6=GO(I2,A)={E->aA·}
I7=GO(I3,B)={E->bB·}
I8=GO(I4,A)={A->cA·}
I9=GO(I5,B)={B->cB·}
I10=GO(I4,d)=GO(I2,d)={A->d·}
I11=GO(I3,d)=GO(I5,d)={B->d·}
(三)构造LR分析表。对于LR(0)文法,可以直接从它的项目集规范族c和识别活前缀的自动机的状态转换函数GO构造出LR分析表。LR分析器的分析表可以抽象为分析动作表Action和状态转移表GOTO。构造方法如下:
1、若项目A->α·aβ属于Ik且GO(Ik,a)=Ij,a为终结符,则置ACTION[k,a]为“把(j,a)移近栈”,简记为Sj。
2、若项目A->α·属于Ik,那么,对任何终结符a(或终结符#),置ACTION[k,a]为用产生式A->α(设为第j个产生式)进行归约,简记为rj。
3、若项目S’->S·属于Ik,则置ACTION[k,#]为接受,简介为Acc。
4、若GO(Ik,A)=Ij,A为非终结符号,则置GOTO[k,A]=j。
5、分析表中凡不能用以上规则添入信息的空白均置“报错”。
综上,按照以上规则,本例的LR(0)分析表为
状态 | ACTION表 | GOTO表 | ||||||
a | b | c | d | # | E | A | B | |
0 | S2 | S3 |
|
|
| 1 |
|
|
1 |
|
|
|
| ACC |
|
|
|
2 |
|
| S4 | S10 |
|
| 6 |
|
3 |
|
| S5 | S11 |
|
|
| 7 |
4 |
|
| S4 | S10 |
|
| 8 |
|
5 |
|
| S5 | S11 |
|
|
| 9 |
6 | r1 | r1 | r1 | r1 | r1 |
|
|
|
7 | r2 | r2 | r2 | r2 | r2 |
|
|
|
8 | r3 | r3 | r3 | r3 | r3 |
|
|
|
9 | r5 | r5 | r5 | r5 | r5 |
|
|
|
10 | r4 | r4 | r4 | r4 | r4 |
|
|
|
11 | r6 | r6 | r6 | r6 | r6 |
|
|
|
五、 LR(0)语法分析器的构造
(一)逻辑结构。
输出 |
Action |
Goto |
LR总控程序 |
分析表 |
Sm Xm · · · · · · S1 X1 S0 # |
状态栈 |
输入串a0 a1······an #
|
图1:LR分析器的逻辑结构
(二)分析器的工作流程。LR分析器的分析表可以抽象为分析动作表Action和状态转移表GOTO。对于Action表中的动作Action[S,a]有如下四种情况(其中a为终结符号∪#):
1、Action[S,a]=“移入”,表示为 S
2、 Action[S,a]=“归约”,表示为rj,即用第j个表达式进行归约;
3、 Action[S,a]=“接受” 表示为Acc,即为接受状态;
4、Action[S,a]=“出错”。
Goto[S,X]=Si表示S移入X后进入的状态为Si,在表中填数字i即可。
LR分析器的工作流程为:
(1)初始格局
S0 a1 a2……an # # |
(2)一般格局
S0 S1……Sm ai ai+1……an # # X1……Xm |
对于一般格局情况,应查分析表[Sm, ai],按表中的规定分为3种情况:
①Action[Sm, ai]=S,表示移入,即符号ai进入符号栈,再查Goto[Sm, ai]= Sm+1,形成新的格局:
S0 S1……Sm Sm+1 ai+1……an # # X1……Xm ai |
② Action[Sm, ai]=rj,且第j个产生式为A->Xm-r+1……Xm,表示归约,即符号栈中的n项符号Xm-r+1……Xm归约为A,形成格局
S0 S1……Sm-r ai+1……an # # X1……Xm ai |
查Goto表,Goto[Sm-r,A]= Sk,把Sk状态添入状态栈,形成格局
S0 S1……Sm-r Sk ai+1……an # # X1……Xm-r ai |
(3)接受格局
S0 Sz # # Z |
对于一般格局情况,应查分析表Action[Sz,#]=Acc,表示接受状态,分析结束。
(三) LR总控程序算法。将初始状态S0置于语法分析器的栈顶,输入串ω#放入缓冲区,S是栈顶状态,a为指针p指向的符号。总控算法如下:
指针p指向第一个符号
While(1)
{
If(Action[s,a]= S移入)
{
查goto表goto[s,a]=s’
把a入符号栈,s’入状态栈
P指向下一个输入符号
}
else if(action[s,a]=rj归约)//rj为A->β
{
栈顶退掉β个字符,
符号栈A入栈
查Goto表Goto[Sm,A]=Sm+1;
Sm+1入栈;
}
else if(action[S,a]=accept)
return
else
error();
}
六、小结
本文本文以上下文无关文法作为语法分析的基础,配合实例,探讨了编译程序构造中的自下而上语法分析法,并初步完成了LR(0)语法分析器的实现。