语法分析和语法分析程序
文章目录
自顶向下
特点:
- 无左递归
- 不能有回溯
- 指出错误位置
理论基础
消除左递归
情况一、 消除直接左递归
方法一
将A->Aα|β
等价改写为A->β{α}
方法二
将A->Aα|β
等价改写为A->βA'
和A'->αA'|ε
情况二、 消除间接左递归
将A->Bα|β
和B->Aγ
转化为A->Aγα|β
,变为情况一
消除回溯
将情况相同的都归为同一类
LL(1) 文法:
对每个产生式A->γ1|γ2|...|γm
有
FIRST(γi)
与FIRST(γj)
的交集为空集(1<=i,j<=m;i!=j)
- 若
γj=>...=>ε
,则FIRST(γi)
与FOLLOW(A)
的交集为空集(i=1,2,...,m;i!=j)
代码实现
递归下降分析法(对应消除左递归)
指对文法的每一非终结符号,都根据相应的产生式各候选式的结构,为其编写一个子程序(或函数),用来识别该非终结符号所表示的语法范畴。
[ 例1 ]
对于文法G[E]
:
E->T|E+T
T->F|T*F
F->(E)|i
[ 解 ] 消除左递归(方法二):
E->TE'
E'->+TE'|ε
T->FT'
T'->*FT'|ε
F->(E)|i
代码实现(伪代码):
def E():
if (match(T)):
T()
EE()
print("语句正确!")
else:
error()
def EE():
if(match(PLUS)):
PLUS()
T()
EE()
# 剩余的就不再赘述
def T():
none
def TT():
none
def F():
none
改进的递归下降分析法(对应消除左递归)
[ 同例1 ]
对于文法G[E]
:
E->T|E+T
T->F|T*F
F->(E)|i
[ 解 ] 消除左递归(方法一):
E->T{+T}
T->F{*F}
F->(E)|i
代码实现(伪代码):
def E():
if (match(T)):
T()
while (match(PLUS)):
T()
print("语句正确!")
else:
error()
# 剩余的就不再赘述
def T():
none
def F():
none
预测分析法(对应消除回溯)
关键是构造LL(1)分析表
(I) 前置知识
构建FIRST
集和FOLLOW
集:
构建FIRST
集算法:
- 若
X∈VT
,则FIRST(X)={X}
。 - 若
X∈VN
,且有X->aα∈P (a∈VT)
则令a∈FIRST(X)
;若有X->ε∈P
则令ε∈FIRST(X)
。 - 若
X->Y1Y2...Yk∈P
Y1∈VN
则令FIRST(Y1)-{ε}⊆FIRST(X)
- 对于所有
j(1<=j<=k-1),Yj∈VN
,且Yj=>...=>ε
,则令FIRST(Yj)-{ε}⊆FIRST(X) (1<=j<=k)
- 特别的当
ε∈FIRST(Yj) (1<=j<=k)
时,令ε∈FIRST(X)
。
构建FOLLOW
集算法:
- 对于文法开始符号
S
,令#∈FOLLOW(S)
。 - 对于每一
A->αBβ∈P
,令FIRST(β)-{ε}⊆FOLLOW(B)
。 - 对于每一
A->αB∈P
,或A->αBβ∈P
,且ε∈FIRST(β)
,则令FOLLOW(A)⊆FOLLOW(B)
。 - 反复使用本规则,直到集合不再增大为止。
(II) 构建预测分析表:
表元素M[A,a] (A ∈ VN,a ∈ VT ∪ {#})
按下述规则确定。
对于文法中的每一个产生式A->γ1|γ2|...|γm
:
- 若
a∈FIRST(γi)
,则置M[A,a]="A->γi"
; - 若
ε∈FIRST(γi)
,且a∈FOLLOW(A)
,则置M[A,a]="A->γi"
; - 其余都置出错。
(III) 分析器的工作流程:
一、初始化
分析栈 | 余留符号串 |
---|---|
#S | a1a2...an# |
二、在某一时刻,有以下格局:
分析栈 | 余留符号串 |
---|---|
#X1X2...Xm-1Xm | aiai+1...an# |
- 若
Xm∈VT∪{#}
,且Xm=ai
,则将Xm
出栈,并将输入串前进一位,否则报错; - 若
Xm∈VN
,则以符号对(Xm,ai)
查分析表,设表元素A[Xm,ai]
为产生式Xm->Y1Y2...Yk
,则将Xm
出栈,将Y1Y2...Yk
反序入栈,从而得到下面的格局。若A[Xm,ai]
为报错,则报错; - 若
Xm=ai=#
,则分析成功。
分析栈 | 余留符号串 |
---|---|
#X1X2...Xm-1YkYk-1...Y1 | aiai+1...an# |
[ 同例1 ]
对于文法G[E]
:
E->T|E+T
T->F|T*F
F->(E)|i
分析符号串i+i*i
[ 解 ] 消除左递归(方法二):
E->TE'
E'->+TE'|ε
T->FT'
T'->*FT'|ε
F->(E)|i
求FIRST
集和FOLLOW
集:
FIRST(E)={(,i}
FIRST(E')={+,ε}
FIRST(T)={(,i}
FIRST(T')={*,ε}
FIRST(F)={(,i}
FOLLOW(E)={),#}
FOLLOW(E')={),#}
FOLLOW(T)={+,),#}
FOLLOW(T')={+,),#}
FOLLOW(F)={+,*,),#}
构造LL(1)分析表:(注:终结符号用#
表示)
i | + | * | ( | ) | # | |
---|---|---|---|---|---|---|
E | E->TE' | E->TE' | ||||
E' | E'->+TE' | E'->ε | E'->ε | |||
T | T->FT' | T->FT' | ||||
T' | T'->ε | T'->*FT' | T'->ε | T'->ε | ||
F | F->i | F->(E) |
空白部分,报错。
编写程序:
# 略
进行预测分析(过程如下表):
步骤 | 分析栈 | 余留符号串 | 所用产生式 |
---|---|---|---|
1 | #E | i+i*i# | E->TE' |
2 | #E'T | i+i*i# | T->FT' |
3 | #E'T'F | i+i*i# | F->i |
4 | #E'T'i | i+i*i# | |
5 | #E'T' | +i*i# | T'->ε |
6 | #E' | +i*i# | E'->+TE' |
7 | #E'T+ | +i*i# | |
8 | #E'T | i*i# | T->FT' |
9 | #E'T'F | i*i# | F->i |
10 | #E'T'i | i*i# | |
11 | #E'T' | *i# | T'->*FT' |
12 | #E'T'F* | *i# | `` |
13 | #E'T'F | i# | F->i |
14 | #E'T'i | i# | |
15 | #E'T' | # | T'->ε |
16 | #E' | # | E'->ε |
17 | # | # | 分析成功 |
自底向上(移进-归约)
关键是求句柄
优先分析法
简单优先分析法
暂略
局限性:对文法要求太强
算符优先分析法(有二义性)
暂略
LR分析法
类似于LL(1),LR分析法也是需要一个输入符号串,一个下推分析栈,一个总控程序和分析表。
显然,关键就在于分析表的构建。(分析表本质上是为了寻找句柄)
以下仅展示分析表的构建方法和总控程序。
LR(0)分析法
(I) 前置知识:
规范句型的活前缀:规范句型(没有语法错误的句型)不含句柄之右的前缀
·:为刻画在分析过程中,文法的一个产生式的右边有多少被识别,可以在右部加一个圆点“·”来表示位置。如:A->β·
,A->·α
等。
LR(0)项目:
假设G[S]
,为方便描述,引入第0个产生式S'->S
。
- 归约项目:下一步要进行规约。如
A->α·
。 - 接受项目:表明整个分析已经成功完成。如
S'->S·
。 - 移进项目:下一步将当前输入符号移入栈中。如
A->α·aβ
。 - 待约项目:下一步将从余留符号串进行归约。如
A->α·Bβ
。
(II)构造DFA:
- 将
S->·S'
列入初态I0中,对其进行闭包运算I0=CLOSURE({S->·S'})
。 - 若I0中有
A->α·Xβ
,则其后面的状态Ii必含有全部形如A->αX·β
的项目(后继项目)。设后继项目的集合为J,则Ii=CLOSURE(J)
。此时我们 可以定义Ii = GO(I,X)=CLOSURE(J)
,其中Ii为后继状态,I为当前状态,X为文法符号,J就是后继项目集合。
注:求CLOSURE(I)的方法
- I中每一个项目都属于CLOUSER(I);
- 若形如
A->α·Xβ
的项目属于CLOUSER(I),且X为非终结符号,则文法中任何X-产生式的一切圆点在最左边的项目X->·γ
也都属于CLOSURE(I);- 重复上述过程,直到不再有新的项目加入为止。
此时G[S]
的DFA为M=(C,V,GO,I0,C) (C={I0,I1,...In})
项目集相容:在一个项目集中不会出现
- 移进项目和归约项目共存。
- 多个归约项目共存。
(III)构造分析表:
构造分析表的方法如下:
- 对于Ii中形如
A->α·Xβ
的项目,若GO(Ii,X)=Ij
,且X为终结符号a时,置[i,a]=sj
。若X为非终结符时,置[i,X]=j
。 - 若归约项目
A->α·
属于Ii,设A->α
为文法的第j个产生式,则对文法的任何终结符号或句子的右介符#(将他们统一记为a),置[i,a]=rj
。 - 若
S'->S·
属于Ii,则置[i,#]=acc
。 - 其余置出错。
注:
si:移入,并转换到Ii状态。
ri:按第i个产生式进行归约。
acc:分析成功。
[ 例2 ]
对于文法G[S]
:
S->A
S->B
A->aAb
A->c
B->aBb
B->d
构造LR(0)分析表。
[ 解 ] 改写成拓展文法G[S']
-
S'->S
-
S->A
-
S->B
-
A->aAb
-
A->c
-
B->aBb
-
B->d
求DFS的每个节点,并绘制DFS:
显然,每一项目集是相容的。
做出LR(0)分析表
a | b | c | d | # | S | A | B | |
---|---|---|---|---|---|---|---|---|
0 | s4 | s5 | s6 | 1 | 2 | 3 | ||
1 | acc | |||||||
2 | r1 | r1 | r1 | r1 | r1 | |||
3 | r2 | r2 | r2 | r2 | r2 | |||
4 | s4 | s5 | s6 | 7 | 9 | |||
5 | r4 | r4 | r4 | r4 | r4 | |||
6 | r6 | r6 | r6 | r6 | r6 | |||
7 | s8 | |||||||
8 | r3 | r3 | r3 | r3 | r3 | |||
9 | s10 | |||||||
10 | r5 | r5 | r5 | r5 | r5 |
总控程序将在本大节末介绍。
SLR(1)分析法
(I) 构造DFA:同LR(0)
(II) 构造分析表:
构造分析表的方法如下:(对比与LR(0)的不同)
- 对于Ii中形如
A->α·Xβ
的项目,若GO(Ii,X)=Ij
,且X为终结符号a时,置[i,a]=sj
。若X为非终结符时,置[i,X]=j
。 - 若归约项目
A->α·
属于Ii,设A->α
为文法的第j个产生式,则对于任何属于FOLLOW(A)
的输入符号a,置[i,a]=rj
。 - 若
S'->S·
属于Ii,则置[i,#]=acc
。 - 其余置出错。
[ 改例2.1 ]
对于文法G[S]
:
S->A
S->B
A->aAb
A->a
B->aBb
B->d
构造SLR(1)分析表。
[ 解 ] 改写成拓展文法G[S']
-
S'->S
-
S->A
-
S->B
-
A->aAb
-
A->a
-
B->aBb
-
B->d
求DFS的每个节点,并绘制DFS:
做出SLR(1)分析表
a | b | d | # | S | A | B | |
---|---|---|---|---|---|---|---|
0 | s4 | s6 | 1 | 2 | 3 | ||
1 | acc | ||||||
2 | r1 | ||||||
3 | r2 | ||||||
4 | s4 | r4 | s6 | r4 | 6 | 8 | |
5 | r6 | r6 | |||||
6 | s7 | ||||||
7 | r3 | r3 | |||||
8 | s9 | ||||||
9 | r5 | r5 |
对比SR(0):
a | b | c | d | # | S | A | B | |
---|---|---|---|---|---|---|---|---|
0 | s4 | s5 | s6 | 1 | 2 | 3 | ||
1 | acc | |||||||
2 | r1 | r1 | r1 | r1 | r1 | |||
3 | r2 | r2 | r2 | r2 | r2 | |||
4 | s4,r4 | r4 | s5,r4 | s6,r4 | r4 | 6 | 8 | |
5 | r6 | r6 | ||||||
6 | s7 | |||||||
7 | r3 | r3 | r3 | r3 | r3 | |||
8 | s9 | |||||||
9 | r5 | r5 | r5 | r5 | r5 |
LR(1)分析法
(I) 前置知识:
向前搜索符a:指针向前扫视的一个符号。
(II) 构造DFA:
略。
求CLOSURE(I)的方法:
- I中每一个项目都属于CLOUSER(I);
- 设项目
[A->α·Bβ,a]∈CLOSURE(I)
,并假设它对活前缀γ=δα
有效,则对文法中所有形如B->η
的产生式和每一个b∈FIRST(βα)
,形如[B->·η,b]
的全部项目也要对γ有效,故若[B->·η,b]
原不在CLOSURE(I)
中,则应将其放入。 - 重复上述过程,直到不再有新的项目加入为止。
(III) 构造分析表:
构造分析表的方法如下:
- 对于Ii中形如
[A->α·Xβ,b]
的项目,若GO(Ii,X)=Ij
,且X为终结符号a时,置[i,a]=sj
。若X为非终结符时,置[i,X]=j
。 - 若归约项目
[A->α·,a]
属于Ii,设A->α
为文法的第j个产生式,置[i,a]=rj
。 - 若
[S'->S·,#]
属于Ii,则置[i,#]=acc
。 - 其余置出错。
[ 改例2.2 ]
对于文法G[S]
:
S->A
S->Bb
A->aAb
A->a
B->aBa
B->a
构造LR(1)分析表。
[ 解 ] 改写成拓展文法G[S']
-
S'->S
-
S->A
-
S->Bb
-
A->aAb
-
A->a
-
B->aBa
-
B->a
求DFS的每个节点,并绘制DFS:
对比SLR(1):
做出LR(1)分析表:
a | b | # | S | A | B | |
---|---|---|---|---|---|---|
0 | s5 | 1 | 2 | 3 | ||
1 | acc | |||||
2 | r1 | |||||
3 | s4 | |||||
4 | r2 | |||||
5 | s6 | r6 | r4 | 7 | 9 | |
6 | s6,r6 | r4 | 11 | 13 | ||
7 | s8 | |||||
8 | r3 | |||||
9 | s10 | |||||
10 | r5 | |||||
11 | s12 | |||||
12 | r3 | |||||
13 | s14 | |||||
14 | r5 |
对比SLR(1):
a | b | # | S | A | B | |
---|---|---|---|---|---|---|
0 | s5 | 1 | 2 | 3 | ||
1 | acc | |||||
2 | r1 | |||||
3 | s4 | |||||
4 | r2 | |||||
5 | s5,r6 | r4,r6 | r4 | 6 | 8 | |
6 | s7 | |||||
7 | r3 | r3 | ||||
8 | s9 | |||||
9 | r5 | r5 |
LALR(1)分析法
(I) 前置知识:
同心集:每个LR(1)项目由两部分组成。1.核:LR(0)项目。2.向前搜索符号集。除了搜索符号集不同外,核都相同的两个LR(1)项目集就是同心集。
(II)先构造LR(1),然后在不会产生冲突的情况下合并同心集。其余同LR(1)。
[ 同例1 ]
对于文法G[E]
:
E->T|E+T
T->F|T*F
F->(E)|i
构建LALR(1)分析表。
[ 解 ] 略。
注:本文只是记录课上所学,供大家共同学习。
作者:那么神奇