Skip to content

第八章 自下而上的语法分析

Giovanna

About 2332 wordsAbout 8 min

2024-07-02

一、自下而上的语法分析

自下而上的语法分析:从ω\omega开始,看能否找到一个规范归约,逐步向上归约,直到开始符号S。

设有文法

S→SASS→bA→ccAA→a \begin{aligned} S\to &SAS\\ S\to &b\\ A\to &ccA\\ A\to &a \end{aligned}

输入串bccabbccab的归约过程:

栈输入串归约
bccab
bccabS->b
Sccab
SccabA->a
SccAbA->ccA
SAb
SAbS->b
SASS->SAS
S

分析树的生成(裁剪)过程:

image.png

二、几个核心概念

短语

设有文法GG,SS是开始符号,设αβδ\alpha\beta\delta是GG的一个句型,若有S⇒+αAδS{\overset{+}{\Rightarrow}}\alpha A\delta且A⇒+βA{\overset{+}{\Rightarrow}}\beta,则称β\beta是句型αβδ\alpha\beta\delta关于AA的短语。

直接短语

在上面的定义中,如果AA直接推出β\beta,即A⇒βA\Rightarrow \beta,则称β\beta是句型αβδ\alpha\beta\delta关于A→βA\to\beta的直接短语。

句柄

一个句型的最左直接短语称为句柄。

例

image.png

  • 短语:T*F、E+T*F
  • 直接短语:T*F
  • 句柄:T*F

三、算符优先文法

Note

适合于表达式的分析。

不严格按照最左归约进行,不是规范归约。

以文法GG为例:

E→E+T∣TT→T∗F∣FF→(E)∣i \begin{aligned} E\to &E+T|T\\ T\to &T*F|F\\ F\to &(E)|i \end{aligned}

1. 基本概念

算符文法

如果一个文法的任何产生式都不含两个或两个以上的相连的非终结符,且不含ε\varepsilon产生式,则称该文法为算符文法。

算符之间的优先关系

对算符文法GG,a,b∈VTa,b\in V_T,P,Q∈VNP,Q\in V_N,定义:

  • a=ba=b:GG中有P→…ab…P\to\ldots ab\ldots或P→…aQb…P\to\ldots aQb\ldots
  • a<ba<b:GG中有P→…aQ…P\to\ldots aQ\ldots且Q⇒+b…Q\overset{+}{\Rightarrow}b\ldots或Q⇒+Rb…Q\overset{+}{\Rightarrow}Rb\ldots
  • a>ba>b:GG中有P→…Qb…P\to\ldots Qb\ldots且Q⇒+…aQ\overset{+}{\Rightarrow}\ldots a或Q⇒+…aRQ\overset{+}{\Rightarrow}\ldots aR

算符优先文法

如果算符优先文法GG的任何两个终结符之间的关系至多只有<,=,>中的一个优先关系,则称该文法为算符优先文法。

Warning

优先关系表中没有多重入口,即为算符优先文法。

优先关系表

记录一个文法GG中所有非终结符之间的优先关系。

Note

优先关系不是数学关系:a<b未必有b>a,终结符之间未必有优先关系,优先关系不能传递。

素短语

素短语:文法GG某句型的一个短语α\alpha是素短语,当且仅当它至少含有一个终结符,且除它自身外不再含更小的素短语。

最左素短语:在具有多个素短语的句型中处于最左边的那个素短语。

image.png

  • 素短语:T*F
  • 最左素短语:T*F

2. 构造FIRSTVT集和LASTVT集

FIRSTVT集:非终结符能推出的第一个终结符的集合

  • P→a…P\to a\ldots或P→Qa…P\to Qa\ldots,则a∈FIRSTVT(P)a\in FIRSTVT(P)
  • P→Q…P\to Q\ldots,则FIRSTVT(Q)⊂FIRSTVT(P)FIRSTVT(Q)\subset FIRSTVT(P)

LASTVT集:非终结符能推出的最后一个终结符的集合

  • P→…aP\to\ldots a或P→…aQP\to\ldots aQ,则a∈LASTVT(P)a\in LASTVT(P)
  • P→…QP\to\ldots Q,则LASTVT(Q)⊂LASTVT(P)LASTVT(Q)\subset LASTVT(P)
FIRSTVTLASTVT
E+ * ( i+ * ) i
T* ( i* ) i
F( i) i

3. 构造优先关系表

  • 横竖都是VTV_T与#\#
  • 增加产生式S′→#S#S'\to\#S\#
  • P→…aQ…P\to\ldots aQ\ldots,则a<FIRSTVT(Q)a<FIRSTVT(Q)(即aa行横填<)
  • P→…Qa…P\to\ldots Qa\ldots,则a>LASTVT(Q)a>LASTVT(Q)(即aa列纵填>)
i+*()#
i>>>>
+<><<>>
*<>><>>
(<<<<=
)>>>>
#<<<<=

4. 算符优先分析过程

  • 栈、输入串、动作
  • 栈放#\#
  • [最靠近栈顶的VT,当前字符][\text{最靠近栈顶的}V_T,\text{当前字符}]查表
  • #=#\#=\#,acc
栈输入串动作
#i+i*i##<i,移进
#i+i*i#i>+,归约,F->i
#F+i*i##<+,移进
#F+i*i#+<i,移进
#F+i*i#i>*,归约,F->i
#F+F*i#+<*,移进
#F+F*i#*<i,移进
#F+F*i#i>#,归约,F->i
#F+F*F#*>#,归约,T->T*F
#F+T#+>#,归约,E->E+T
#E##=#,acc

四、LR分析法

1. LR分析法的种类

  • LR(0)分析法
  • SLR分析法
  • 规范LR分析法
  • LALR分析法

2. 几个概念

活前缀

规范句型中不含句柄之后任何一个符号的一个前缀。

如:S⇒∗δAω⇒∗δαβωS\overset{*}{\Rightarrow}\delta A\omega\overset{*}{\Rightarrow}\delta\alpha\beta\omega,δαβ\delta\alpha\beta及其任何前缀都是δαβω\delta\alpha\beta\omega的活前缀

Note

αβ\alpha\beta是句柄,αβ\alpha\beta及前任何前缀均不包含句柄之后的符号,而αβ\alpha\beta是δαβ\delta\alpha\beta的后缀,是分析栈栈顶符号串。

规范归约的特点

  • 归约符号串总是在栈顶
  • 句柄之后的待入栈符号串总是终结符
  • 规范句型(由规范推导推出的句型)在符号栈中的符号串是规范句型的前缀

活前缀与句柄之间的关系

  • 归约符号串总是在栈顶
  • 句柄之后的待入栈符号串总是终结符
  • 规范句型(由规范推导推出的句型)在符号栈中的符号串是规范句型的前缀

活前缀与句柄之间的关系

  • 活前缀不含句柄的任何符号,A→⋅αβA\to\cdot\alpha\beta
  • 活前缀只含句柄的真前缀,A→α⋅βA\to\alpha\cdot\beta
  • 活前缀包含句柄的全部符号,A→αβ⋅A\to\alpha\beta\cdot

LR(0)项目

在一个产生式右部添加一个圆点,称为一个LR(0)项目。

Note

圆点左边已识别,右边期待识别。

LR(0)项目的分类

  • 归约项目:形如A→α⋅A\to\alpha\cdot
  • 移进项目:形如A→α⋅aβA\to\alpha\cdot a\beta,a∈VTa\in V_T
  • 待约项目:形如A→α⋅BβA\to\alpha\cdot B\beta,B∈VNB\in V_N
  • 接受项目:形如S→α⋅S\to\alpha\cdot,SS为开始符号

文法的拓广

在文法GG中增加产生式S′→SS'\to S,从而使S′→S⋅S'\to S\cdot成为唯一的接受项目。

3. 构造识别所有活前缀的转换图

  • 每个状态是一个LR(0)项目
  • S′→⋅SS'\to\cdot S是唯一初态
  • X→…Xi−1⋅Xi…⇒XiX→…Xi−1Xi⋅…X\to\ldots X_{i-1}\cdot X_i\ldots\overset{X_i}{\Rightarrow}X\to\ldots X_{i-1}X_i\cdot\ldots
  • 如果上一条规则的XiX_i是VNV_N,X→Xi−1⋅Xi⇒εXi→α1∣α2∣…∣αmX\to X_{i-1}\cdot X_i\overset{\varepsilon}{\Rightarrow}X_i\to\alpha_1|\alpha_2|\ldots|\alpha_m

例:G={S→BB,B→aB∣b}G=\{S\to BB,B\to aB|b\}

  1. S′→⋅SS'\to\cdot S
  2. S′→S⋅S'\to S\cdot
  3. S→⋅BBS\to\cdot BB
  4. S→B⋅BS\to B\cdot B
  5. S→BB⋅S\to BB\cdot
  6. B→⋅aBB\to\cdot aB
  7. B→a⋅BB\to a\cdot B
  8. B→aB⋅B\to aB\cdot
  9. B→⋅bB\to\cdot b
  10. B→b⋅B\to b\cdot

image.png

4. 构造LR(0)项目集规范族

  • 有效项目:对于项目A→α⋅βA\to\alpha\cdot\beta,如果有S⇒∗δAω⇒δαβωS\overset{*}{\Rightarrow}\delta A\omega\Rightarrow\delta\alpha\beta\omega,则称A→α⋅βA\to\alpha\cdot\beta对活前缀δα\delta\alpha有效
  • 有效项目集:对于一个活前缀有效的项目可能不止一个,对活前缀δα\delta\alpha有效的项目集合称为δα\delta\alpha的有效项目集
  • LR(0)项目集规范族:文法GG的所有有效项目集组成的集合

写出上例的LR(0)项目集规范族

I0=closure{S'->.S}={S'->.S,S->.BB,B->.aB,B->.b}
I1=go(I0,S)={S'->S.}
I2=go(I0,B)={S->B.B,B->.aB,B->.b}
I3=go(I0,a)=go(I2,a)=go(I3,a)={B->a.B,B->.aB,B->.b}
I4=go(I0,b)=go(I2,b)=go(I3,b)={B->b.}
I5=go(I2,B)={S->BB.}
I6=go(I3,B)={B->aB.}

5. 构造LR(0)分析表

有多少状态就有多少行,action表头VTV_T与#\#,goto表头VNV_N

  • 接收项:action[k,#]=accaction[k,\#]=acc
  • 移进项:go(Ik,a)=Ijgo(I_k,a)=I_j,action[k,a]=sjaction[k,a]=s_j
  • IkI_k中的归约项,A→αA\to\alpha为第jj个产生式,action[k,所有非终结符或#]=rjaction[k,\text{所有非终结符或}\#]=r_j
  • go(Ik,A)=Ijgo(I_k,A)=I_j,goto[k,A]=jgoto[k,A]=j
  • 空白:错误

接上例:

ab#SB
0s3s4
1acc12
2s3s4
3s3s45
4r3r3r36
5r1r1r1
6r2r2r2

Warning

LR(0)分析表中没有多重入口说明是LL(1)文法

6. 构造SLR(1)分表

构造VNV_N的FOLLOW集,产生式左部VNV_N的FOLLOW集中的VTV_T才填rjr_j

接上例:

FIRSTFOLLOW
Sa ba b #
Ba ba b #
ab#SB
0s3s412
1acc
2s3s45
3s3s46
4r3r3r3
5r1r1r1
6r2r2r2

Warning

SLR(1)分析表中没有多重入口说明是SLR(1)文法

7. 分析过程

action表与goto表

action表:action[S,a]action[S,a]定义了在状态S下,当输入字符为a时应采取的动作

  • sjs_j:j入状态栈,输入指针所指符号入符号栈
  • rjr_j:按第j个产生式归约,状态栈出栈产生式右部符号个数,左部入符号栈,goto后的状态入状态栈
  • acc:成功
  • 空白:出错

goto表:goto[S,x]goto[S,x]定义了在状态S下,面对文法符号x时状态的转换

分析步骤

  • 状态栈、符号栈、输入串、动作
  • 状态栈放0,符号栈放#
  • action[状态栈顶,当前字符]action[\text{状态栈顶},\text{当前字符}]
    • sjs_j:j入状态栈,当前字符入符号栈
    • rjr_j:根据第j个产生式归约,两个栈都出栈右部个数。左部入符号栈,goto[状态栈顶,产生式左部]goto[\text{状态栈顶},\text{产生式左部}]入状态栈

接上例,输入串aabb的分析过程如下:

状态符号输入串动作
0#aabb#s3
03#aaabb#s3
033#aabb#s4
0334#aabb#r3,B->b,goto[3,B]
0336#aaBb#r2,B->aB,goto[3,B]
036#aBb#r2,B->aB,goto[0,B]
02#Bb#s4
024#Bb#r3,B->b,goto[2,B]
025#BB#r1,S->BB,goto[0,S]
01#S#acc