目录
自顶向下分析
- 自顶向下分析就是从文法的开始符号出发,按最左推导方式向下推导,试图推出要分析的输入串,为输入的符号串建立分析树
- 构造最左推导的关键问题:
- 在构造最左推导的过程中,面对当前读入的单词符号 a a a 和当前被替换的非终极符 A A A,应该选择 A A A 的哪条候选式去替换它(推导)
- 关键:找出选择一个非终极符号的候选式的方法
上下文无关文法的特性
- 自嵌入性:一个文法
G
G
G 是自嵌入的,若存在一个非终极符
A
A
A,有
A
⇒
∗
α
A
β
,
α
、
β
∈
V
+
A\Rightarrow^* αA β, α、β∈V^+
A⇒∗αAβ,α、β∈V+ 非空
- 例如, A → a A b ∣ a b A→aAb|ab A→aAb∣ab 是自嵌入的,而 A → a A ∣ a b A→aA|ab A→aA∣ab 不具有自嵌入性,是三型文法(正则文法)
- 能够进行确定分析的语言称为确定语言
- 对上下文无关语言的分析,并不总是能以确定的方式进行,需要回溯
带回溯的自顶向下语法分析
即 不确定的自顶向下分析
回溯的自顶向下语法分析实质
- 它是一个反复使用不同的产生式进行试探,以谋求匹配输入串的过程。
- 该方法分析效率低,在实际中并不适用,但给出了自顶向下语法分析的思想
例
G
[
S
]
:
S
→
a
A
b
A
→
∗
∗
∣
∗
\begin{aligned}G[S]:S &→aAb\\ A &→** | *\end{aligned}
G[S]:SA→aAb→∗∗∣∗
利用自顶向下语法分析,分析 a ∗ b a*b a∗b 是否是合法的句子
引起回溯的原因
- 由于文法中同一非终极符的各候选式有公共的左因子或隐含有公共的左因子,使得推导无法选择唯一的候选式,导致盲目试探,大大影响速度
- 如: S → a S ∣ a b S →aS|ab S→aS∣ab,它们都有公共的左因子 a a a,当下一个想使用最左推导得出的终结符号为 a a a 时,需要依次试探这两条规则
- 左递归问题:无论是直接左递归的, 还是间接左递归的,都会使分析过程无法终止
- 如: U → U α ∣ b U → Uα|b U→Uα∣b 若推导 b α α bαα bαα: U ⇒ U α ⇒ U α α ⇒ … U \Rightarrow Uα \Rightarrow Uαα \Rightarrow … U⇒Uα⇒Uαα⇒… (每次都使用第一条规则)
消除回溯与左递归
消除回溯
- 对文法加以一定的限制,使每次对候选式的选择是唯一的,从而进行确定的自上而下分析
- 例如 L L ( 1 ) LL(1) LL(1) 文法保证文法中的各条规则没有公共左因子
消除左递归
- 解决方法1:重排规则顺序
例
S → S a ∣ b S →Sa | b S→Sa∣b, 改为: S → b ∣ S a S →b | Sa S→b∣Sa ,若推导 ’ b a a ’ ’baa’ ’baa’
- 解决方法2:将左递归→右递归(或迭代)
例
左递归文法 S → S a ∣ b S →Sa | b S→Sa∣b 生成语言 L = b a ∗ L = ba^* L=ba∗;对应的右递归文法: S → b A , A → a A ∣ ε S →bA,A →aA|ε S→bA,A→aA∣ε;或迭代 S → b { a } S →b\{a\} S→b{a}
迭代形式便于程序实现,但考试及作业中还是要写成规范的文法形式
例
表达式文法
E
→
E
+
T
∣
T
T
→
T
∗
F
∣
F
F
→
(
E
)
∣
a
\begin{aligned}E &→E+T | T \\ T &→T*F | F \\ F &→(E) | a\end{aligned}
ETF→E+T∣T→T∗F∣F→(E)∣a
消除左递归,得
E
→
T
E
’
E
’
→
+
T
E
’
∣
ε
T
→
F
T
’
T
’
→
∗
F
T
’
∣
ε
F
→
(
E
)
∣
a
\begin{aligned}E &→TE’\\ E’ &→+TE’ | ε \\ T &→FT’\\ T’ &→*FT’ |ε \\ F &→(E) | a\end{aligned}
EE’TT’F→TE’→+TE’∣ε→FT’→∗FT’∣ε→(E)∣a或
E
→
T
{
+
T
}
(
迭
代
)
T
→
F
{
∗
F
}
F
→
(
E
)
∣
a
\begin{aligned} E &→T\{+T\} (迭代)\\ T &→F\{*F\} \\ F &→(E) | a\end{aligned}
ETF→T{+T}(迭代)→F{∗F}→(E)∣a
但要注意,不能直接将文法改成下列形式来消除左递归:
E
→
T
+
E
∣
T
T
→
F
∗
T
∣
F
F
→
(
E
)
∣
a
\begin{aligned}E &→T+E | T \\ T &→F*T | F \\ F &→(E) | a\end{aligned}
ETF→T+E∣T→F∗T∣F→(E)∣a原因是:如果画出语法树会发现,这样更改之后,原本运算符号的左结合性变成了右结合性
若关于
A
A
A 的全部规则为
A
→
A
α
1
∣
A
α
2
∣
…
∣
A
α
m
∣
β
1
∣
β
2
∣
…
∣
β
n
A →Aα_1 | Aα_2 | …| Aα_m | β_1 | β_2 | … | β_n
A→Aα1∣Aα2∣…∣Aαm∣β1∣β2∣…∣βn
其中,每个
α
i
α_i
αi 都不等于
ε
ε
ε ,每个
β
i
β_i
βi 都不以
A
A
A 开头则消除左递归,得
A
→
(
β
1
∣
β
2
∣
…
∣
β
n
)
A
’
A
’
→
(
α
1
∣
α
2
∣
…
∣
α
m
)
A
’
∣
ε
A →(β_1 | β_2 | … | β_n )A’\\ A’ →(α_1 | α_2 | …| α_m )A’ | ε
A→(β1∣β2∣…∣βn)A’A’→(α1∣α2∣…∣αm)A’∣ε或使用迭代形式:
A
→
(
β
1
∣
β
2
∣
…
∣
β
n
)
{
α
1
∣
α
2
∣
…
∣
α
m
}
A →(β_1 | β_2 | … | β_n )\{α_1 | α_2 | …| α_m \}
A→(β1∣β2∣…∣βn){α1∣α2∣…∣αm}
消除间接左递归
例
G
[
S
]
:
S
→
Q
c
∣
c
Q
→
R
b
∣
b
R
→
S
a
∣
a
\begin{aligned}G[S]: S &→Qc | c\\ Q &→Rb | b\\ R &→Sa | a\end{aligned}
G[S]:SQR→Qc∣c→Rb∣b→Sa∣a
因
S
⇒
Q
c
⇒
R
b
c
⇒
S
a
b
c
S \Rightarrow Qc \Rightarrow Rbc \Rightarrow Sabc
S⇒Qc⇒Rbc⇒Sabc, 是左递归文法
代入法:
S
→
(
R
b
∣
b
)
c
∣
c
S
→
R
b
c
∣
b
c
∣
c
S
→
(
S
a
∣
a
)
b
c
∣
b
c
∣
c
S
→
S
a
b
c
∣
a
b
c
∣
b
c
∣
c
\begin{aligned}S &→(Rb | b)c | c\\ S &→Rbc | bc | c\\ S &→(Sa | a)bc | bc | c\\ S &→Sabc | abc | bc | c\end{aligned}
SSSS→(Rb∣b)c∣c→Rbc∣bc∣c→(Sa∣a)bc∣bc∣c→Sabc∣abc∣bc∣c消除左递归,得
S
→
a
b
c
S
’
∣
b
c
S
’
∣
c
S
’
S
’
→
a
b
c
S
’
∣
ε
\begin{aligned}S &→abcS’ | bcS’ | cS’\\ S’ &→abcS’ | ε\end{aligned}
SS’→abcS’∣bcS’∣cS’→abcS’∣ε
确定的自顶向下分析
- 确定的自顶向下分析方法对文法要求比较严格,必须无二义性、且消除左递归和回溯
- 递归下降分析: 利用程序设计语言的递归函数实现,每个函数对应文法的一个非终极符。便于手工构造语法分析器
- LL(K)分析:由一张预测分析表和一个总控程序组成。预测分析表给出了当面临输入符号时,运用到非终极符的推导所选用的产生式。便于用语法分析器的自动构造工具,自动构造语法分析器
例
构造最左推导(
a
a
b
b
a
a
aabbaa
aabbaa) (自顶向下)
S
→
a
A
S
∣
a
A
→
S
b
A
∣
S
S
∣
b
a
S→aAS|a\\ A →SbA |SS|ba
S→aAS∣aA→SbA∣SS∣ba
LL 分析法(预测分析法)
L L ( K ) LL(K) LL(K) 含义
-
L
L
(
K
)
LL(K)
LL(K) 含义:
- 从左( L L L)到右( R R R)扫描输入串,并建立它的最左推导
- K K K:在选择同一非终极符的不同规则时,通过向前看 K K K 个符号决定
- L L ( K ) LL(K) LL(K) 文法:是非歧义文法中能构造出确定的自顶向下分析器的最大文法类
- L L LL LL 分析器又称预测分析器,是 P D A PDA PDA 特例
预测分析方法
- 在自顶向下分析中,如果在每步推导中,面对被替换的非终结符号
A
A
A 和当前从左至右读入输入串读到的单词符号
a
a
a,如果
A
A
A 的定义(无
ε
ε
ε-产生式)
A → α 1 ∣ α 2 ∣ … ∣ α n A→\alpha_1 |\alpha_2 | …|\alpha_n A→α1∣α2∣…∣αn中,只有 α i \alpha_i αi( 1 ≤ i ≤ n 1≤i≤n 1≤i≤n) 推导出的第一个终极符号是 a a a,那么就可以用产生式 A → α i A→\alpha_i A→αi 构造最左推导,即用 α i \alpha_i αi 替换 A A A
- 一类简单文法的分析器工作过程:
G G G 为 S − S- S− 文法,当且仅当它满足:- 每条规则的右部都以 V T V_T VT 开头
- 如果一个 V N V_N VN 有多条规则,那么它们都以不同的 V T V_T VT 开头
例
S
→
a
S
∣
b
A
,
A
→
d
∣
c
c
A
S →aS | bA, A →d | ccA
S→aS∣bA,A→d∣ccA
- 列出预测分析矩阵(表)/ LL分析表:
# \# # 代表句子结束
- 对于上例,在推导过程中,完全可以根据当前的向前看符号决定选择哪个产生式进行推导,因此,分析过程是完全确定的。这种分析称为自顶向下的预测分析
- 原因在于,文法中,
∀
A
∈
V
N
\forall A\in V_N
∀A∈VN,
A → α 1 ∣ α 2 ∣ . . . ∣ α n ∀ i , j ( 1 ≤ i , j ≤ n & i < > j ) A→\alpha_1|\alpha_2| ...|\alpha_n \ \ \ \ \ \ \forall i,j (1\leq i, j \leq n\ \ \ \&\ \ \ i<>j) A→α1∣α2∣...∣αn ∀i,j(1≤i,j≤n & i<>j)从 α i \alpha_i αi 推导出来的第一个终结符号集合(称为 F I R S T ( α i ) FIRST( \alpha_i) FIRST(αi) )和从 α j \alpha_j αj 推导出来的第一个终结符号集合(称为 F I R S T ( α j ) FIRST(\alpha_j) FIRST(αj) )是不相交的,即:
F I R S T ( α i ) ∩ F I R S T ( α j ) = ϕ FIRST(\alpha_i) \cap FIRST(\alpha_j)=\phi FIRST(αi)∩FIRST(αj)=ϕ
< > <> <> 表示不等于
预测分析器
- 分析器的动作总是由 栈顶元素
X
X
X 以及 当前输入符号
a
a
a 决定的
分析器的动作有3种:
- 若栈顶符号
X
X
X 是一个非终极符,则去查分析表
M
[
X
,
a
]
M[X, a]
M[X,a]
- M [ X , a ] M[X, a] M[X,a] 是一文法规则 X → α X →α X→α,则用 α α α 去替换栈顶的 X X X ( α α α 逆序推入栈中),并输出文法规则编号。(相当于进行了一次最左推导)
- M [ X , a ] M[X, a] M[X,a] 无定义,则报错并转错误处理程序
- 上述两种情况均不读下一符号
- 若栈顶符号 X X X 是一个终极符且 X = a X=a X=a,则将栈顶符号弹出,并读下一个符号,若 X ∈ V T X∈V_T X∈VT , 且 X ≠ a X≠a X=a,则报错
- 若 X = “ # ” = a X=“\#”=a X=“#”=a,则分析器停机,输入串被接受
L L ( 1 ) LL(1) LL(1) 文法
- 对于 L L ( k ) LL(k) LL(k) 文法, k = 1 k=1 k=1 最常见,只需根据栈顶符号 X X X 和当前输入符号 a a a,即可确定用哪条规则进行推导
FIRST 集 (开头符号集)
- 串
α
∈
(
V
N
∪
V
T
)
∗
α∈(V_N∪V_T)^*
α∈(VN∪VT)∗ 的 FIRST 集:
F I R S T ( α ) = { a ∣ α ⇒ ∗ a β , a ∈ V T , α , β ∈ V ∗ } FIRST(α)=\{a| α\Rightarrow^*aβ, a ∈V_T, α, β ∈V^*\} FIRST(α)={a∣α⇒∗aβ,a∈VT,α,β∈V∗}若 α ⇒ ∗ ε α \Rightarrow^*ε α⇒∗ε, 则 ε ∈ F I R S T ( α ) ε ∈FIRST(α) ε∈FIRST(α)
定义1
- 设 G G G 是不带 ε ε ε 规则的文法,若对所有 A → α 1 ∣ α 2 ∣ … ∣ α n A →α_1 | α_2 | …| α_n A→α1∣α2∣…∣αn 的规则,有 F I R S T ( α i ) ∩ F I R S T ( α j ) = Φ , i ≠ j FIRST(α_i) ∩FIRST(α_j)=Φ,i≠j FIRST(αi)∩FIRST(αj)=Φ,i=j,则 G G G 为 L L ( 1 ) LL(1) LL(1) 文法
下面讨论有 ε ε ε 规则的文法
例
-
S
→
a
A
,
S
→
d
,
A
→
b
A
S
,
A
→
ε
S →aA, S →d , A →bAS,A →ε
S→aA,S→d,A→bAS,A→ε
若对 a b d abd abd 串, S ⇒ ① a A ⇒ ③ a b A S ⇒ ④ a b S ⇒ ② a b d S\Rightarrow^① aA \Rightarrow^③ abAS \Rightarrow^④ abS\Rightarrow^②abd S⇒①aA⇒③abAS⇒④abS⇒②abd
- 何时选用
A
→
ε
A →ε
A→ε 的空规则?
- 在分析时,只有见到它的后继符号才能决定使用这条规则(使用这条规则之后可以匹配跟在它后面的终结符号)—引进 跟随集 (Follow 集)
Follow 集定义:
- 若 G = ( V N , V T , P , S ) G=(V_N,V_T,P,S) G=(VN,VT,P,S) 是 CFG, A ∈ V N A ∈ V_N A∈VN, F o l l o w ( A ) = { a ∣ S ⇒ ∗ α A β , a ∈ F i r s t ( β ) , β ∈ V + } Follow(A)=\{a | S\Rightarrow^* αAβ, a∈First(β), β ∈V^+\} Follow(A)={a∣S⇒∗αAβ,a∈First(β),β∈V+}即:句型中紧跟在 A A A 后面的那些终极符
- 规定 # \# # 属于开始符号 S S S 的 Follow 集, # \# # 是句子结束符
- 若 β ⇒ ∗ ε β \Rightarrow^* ε β⇒∗ε,则 # ∈ F o l l o w ( A ) \# ∈Follow(A) #∈Follow(A);换句话说:若 S ⇒ ∗ … A S \Rightarrow^* …A S⇒∗…A,则规定 # ∈ F o l l o w ( A ) \# ∈Follow(A) #∈Follow(A)
定义2
- 一个文法
G
G
G 是
L
L
(
1
)
LL(1)
LL(1) 文法,当且仅当对文法中形如
A
→
α
1
∣
α
2
∣
…
∣
α
n
A→α_1 | α_2 | …| α_n
A→α1∣α2∣…∣αn 都满足 LL(1)条件,即:
- F i r s t ( α i ) ∩ F i r s t ( α j ) = Φ , i ≠ j First(α_i) ∩First(α_j)=Φ, i≠j First(αi)∩First(αj)=Φ,i=j
- 如果 α i ⇒ ∗ ε α_i\Rightarrow^* ε αi⇒∗ε,则 F i r s t ( α j ) ∩ F o l l o w ( A ) = Φ , i ≠ j First(α_j) ∩Follow(A)=Φ, i≠j First(αj)∩Follow(A)=Φ,i=j
- 为简化 LL(1) 条件,引进选择集
S
e
l
e
c
t
(
A
→
α
)
Select(A→α)
Select(A→α)
定义3
- 一个文法
G
G
G 是 LL(1) 文法,当且仅当对文法中的所有形如
A
→
α
1
∣
α
2
∣
…
∣
α
n
A→α_1| α_2|…| α_n
A→α1∣α2∣…∣αn 的产生式, 其各候选式都满足如下的 LL(1) 条件:
s e l e c t ( A → α i ) ∩ s e l e c t ( A → α j ) = φ ( i ≠ j ) select( A→α_i) ∩ select( A→α_j) =φ (i≠j) select(A→αi)∩select(A→αj)=φ(i=j) - 只要一个 CFG 满足 L L ( 1 ) LL(1) LL(1) 条件,就是 L L ( 1 ) LL(1) LL(1) 文法,能采用预测分析法进行确定的自顶向下分析
- 利用
s
e
l
e
c
t
select
select 集构造
L
L
(
1
)
LL(1)
LL(1) 分析表, 每个元素
M
[
A
,
a
]
M[A,a]
M[A,a] 按下述规则确定: (其中,
A
∈
V
N
,
a
∈
V
T
∪
{
#
}
A∈V_N, a∈V_T ∪\{\#\}
A∈VN,a∈VT∪{#} )
- 对于每个形如
A
→
α
A→α
A→α 的产生式:
若 a ∈ s e l e c t ( A → α ) a∈ select(A→α) a∈select(A→α) , 则置 M [ A , a ] = “ A → α ” M [A,a]=“A→α” M[A,a]=“A→α”,否则置错,用空白表示
- 对于每个形如
A
→
α
A→α
A→α 的产生式:
- 判断 L L ( 1 ) LL(1) LL(1) 文法可从分析表判断,若 L L ( 1 ) LL(1) LL(1) 分析表中每一项没有多重定义(即无二义性),就是 L L ( 1 ) LL(1) LL(1) 文法
- 对不同的 L L ( 1 ) LL(1) LL(1) 文法,其预测分析器的总控程序完全一样。不同仅在于分析表不同
F o l l o w Follow Follow 集的计算 (求 s e l e c t select select 集的关键)
- 设 S S S 为 G G G 中开始符号,则 # ∈ F O L L O W ( S ) \#∈FOLLOW(S) #∈FOLLOW(S)
- 若
A
→
α
B
β
A →αBβ
A→αBβ 是一产生式,则把
F
I
R
S
T
(
β
)
FIRST(β)
FIRST(β) 的非空元素加入到
F
O
L
L
O
W
(
B
)
FOLLOW(B)
FOLLOW(B) 中
- 如果 β ⇒ ∗ ε β\Rightarrow^* ε β⇒∗ε,即有: A → α B A →αB A→αB,则把 F O L L O W ( A ) FOLLOW(A) FOLLOW(A) 也加入到 F O L L O W ( B ) FOLLOW(B) FOLLOW(B) 中
- 反复使用上一步,直到每个 V N V_N VN 的 FOLLOW 集不再增大为止
例
给定文法
S
→
0
S
∣
1
S
∣
ε
S→0S|1S|ε
S→0S∣1S∣ε 是否为
L
L
(
1
)
LL(1)
LL(1) 文法?
解
- 构造 LL(1) 分析表:
- 因为表中无多重定义, 所以该文法是 LL(1) 文法
例
给定文法
S
→
0
S
0
∣
1
S
1
∣
ε
S→0S0| 1S1 |ε
S→0S0∣1S1∣ε 是否为 LL(1) 文法?
解
- 构造 LL(1) 分析表:
- 因为表中出现多重定义, 所以该文法不是 LL(1) 文法
例
左递归文法是 LL(1) 文法吗?
解
- 因为左递归文法意味着有隐含的左公共因子,所以左递归文法不是 LL(1) 文法
- 例如:
A
→
A
a
∣
b
A→Aa|b
A→Aa∣b
因为: F i r s t ( A a ) = { b } , F i r s t ( b ) = { b } First(Aa)= \{ b\}, First(b)=\{b\} First(Aa)={b},First(b)={b}所以: F i r s t ( A a ) ∩ F i r s t ( b ) = { b } First(Aa) \cap First(b)= \{b\} First(Aa)∩First(b)={b}
- 例如:
A
→
A
a
∣
b
A→Aa|b
A→Aa∣b
很明显,歧义文法也不是 LL(1) 文法
例
判断
G
[
S
]
G[S]
G[S] 是否是
L
L
(
1
)
LL(1)
LL(1) 文法,如果是,构造
L
L
(
1
)
LL(1)
LL(1) 分析表
1.
S
→
A
B
∣
b
C
2.
A
→
ε
∣
b
3.
B
→
ε
∣
a
D
4.
C
→
A
D
∣
b
5.
D
→
a
S
∣
c
\begin{aligned}1. S &→AB|bC\\ 2. A &→ε|b\\ 3. B &→ε|aD\\ 4. C &→AD|b\\ 5. D &→aS|c\end{aligned}
1.S2.A3.B4.C5.D→AB∣bC→ε∣b→ε∣aD→AD∣b→aS∣c
解
- 求
V
N
V_N
VN 的
F
i
r
s
t
First
First 集、
F
o
l
l
o
w
Follow
Follow 集
-
F
i
r
s
t
First
First 集
-
F
o
l
l
o
w
Follow
Follow 集
- 由规则1: F O L L O W ( B ) = F O L L O W ( S ) , F O L L O W ( C ) = F O L L O W ( S ) FOLLOW(B)=FOLLOW(S), FOLLOW(C)=FOLLOW(S) FOLLOW(B)=FOLLOW(S),FOLLOW(C)=FOLLOW(S)
- 由规则3、4: F O L L O W ( D ) = F O L L O W ( B ) ∪ F O L L O W ( C ) = F O L L O W ( S ) FOLLOW(D)=FOLLOW(B)\cup FOLLOW(C)=FOLLOW(S) FOLLOW(D)=FOLLOW(B)∪FOLLOW(C)=FOLLOW(S)
- 由规则1、4:
F
O
L
L
O
W
(
A
)
=
F
I
R
S
T
(
B
)
∪
F
O
L
L
O
W
(
S
)
∪
F
I
R
S
T
(
D
)
FOLLOW(A)=FIRST(B)\cup FOLLOW(S)\cup FIRST(D)
FOLLOW(A)=FIRST(B)∪FOLLOW(S)∪FIRST(D)
( B ⇒ ∗ ε B\Rightarrow^* ε B⇒∗ε) - 由规则5: F O L L O W ( S ) = F O L L O W ( D ) = F O L L O W ( S ) = { # } FOLLOW(S)=FOLLOW(D)=FOLLOW(S)=\{\#\} FOLLOW(S)=FOLLOW(D)=FOLLOW(S)={#}
- 因此,
F
O
L
L
O
W
(
S
)
=
{
#
}
F
O
L
L
O
W
(
B
)
=
F
O
L
L
O
W
(
C
)
=
F
O
L
L
O
W
(
D
)
=
{
#
}
F
O
L
L
O
W
(
A
)
=
{
a
,
c
,
#
}
FOLLOW(S)=\{\#\} \\ FOLLOW(B)=FOLLOW(C)=FOLLOW(D)=\{\#\}\\ FOLLOW(A)=\{a,c,\#\}
FOLLOW(S)={#}FOLLOW(B)=FOLLOW(C)=FOLLOW(D)={#}FOLLOW(A)={a,c,#}
-
F
i
r
s
t
First
First 集
- 求
s
e
l
e
c
t
select
select 集
-
L
L
(
1
)
LL(1)
LL(1) 分析表
文法的变换
- 某些非 L L ( 1 ) LL(1) LL(1) 文法,经过变换 (消除左递归及公共左因子) 后,有可能改造成 L L ( 1 ) LL(1) LL(1) 文法
改写之后的文法不一定就是 L L ( 1 ) LL(1) LL(1) 文法,还需要进一步用 L L ( 1 ) LL(1) LL(1) 条件判断
左递归文法的变换
例
给定文法
G
[
S
]
:
S
→
i
∣
(
E
)
E
→
E
+
S
∣
E
−
S
∣
S
\begin{aligned}G[S]: S&→ i | (E)\\ E&→E+S | E-S | S\end{aligned}
G[S]:SE→i∣(E)→E+S∣E−S∣S
(1) 求每一非终极符的
F
i
r
s
t
First
First 集和
F
o
l
l
o
w
Follow
Follow 集
(2) 判断该文法是否为
L
L
(
1
)
LL(1)
LL(1) 文法,为什么?能否进行文法变换成为
L
L
(
1
)
LL(1)
LL(1) 文法?若能,给出
L
L
(
1
)
LL(1)
LL(1) 分析表
解
(1)
(2) 因为该文法是左递归文法,所以不是
L
L
(
1
)
LL(1)
LL(1) 文法。下面变换左递归文法:
E
→
E
(
+
S
∣
−
S
)
∣
S
E→E(+S|-S)|S
E→E(+S∣−S)∣S
消除左递归:
E
→
S
E
′
E
′
→
(
+
S
∣
−
S
)
E
′
∣
ε
\begin{aligned}E&→ SE'\\ E'&→(+S|-S)E'|\varepsilon\end{aligned}
EE′→SE′→(+S∣−S)E′∣ε因此,消除左递归之后的文法为:
G
′
[
S
]
:
S
→
i
∣
(
E
)
E
→
S
E
′
E
′
→
+
S
E
′
∣
−
S
E
′
∣
ε
\begin{aligned}G'[S]: S&→ i | (E)\\ E&→ SE'\\ E'&→+SE'|-SE'|\varepsilon\end{aligned}
G′[S]:SEE′→i∣(E)→SE′→+SE′∣−SE′∣ε进一步构建
G
′
[
S
]
G'[S]
G′[S] 的
L
L
(
1
)
LL(1)
LL(1) 分析表:
因为
L
L
(
1
)
LL(1)
LL(1) 分析表无多重定义,所以变换后的文法
G
′
[
S
]
G'[S]
G′[S] 是
L
L
(
1
)
LL(1)
LL(1) 文法
提取左公共因子变换
例
变换下列文法
G
[
S
]
:
S
→
a
S
b
∣
a
S
c
∣
ε
G[S]:S→ aSb|aSc|\varepsilon
G[S]:S→aSb∣aSc∣ε,并判断是否为
L
L
(
1
)
LL(1)
LL(1) 文法
解:
提取左公共因子变换:
S
→
a
S
(
b
∣
c
)
∣
ε
S→ aS(b|c)|\varepsilon
S→aS(b∣c)∣ε令:
B
→
b
∣
c
B→ b|c
B→b∣c
变换后的文法:
G
’
[
S
]
:
S
→
a
S
B
∣
ε
B
→
b
∣
c
\begin{aligned}G’[S]: S&→ aSB|\varepsilon\\ B&→ b|c\end{aligned}
G’[S]:SB→aSB∣ε→b∣c
G ′ [ S ] : S e l e c t ( S → a S B ) = { a } S e l e c t ( S → ε ) = { b , c , # } S e l e c t ( B → b ) = { b } S e l e c t ( B → c ) = { c } \begin{aligned} G'[S] :Select(S→ aSB)&=\{a\} \\ Select(S→ \varepsilon)&=\{b,c,\#\} \\ Select(B →b)&=\{b\}\\ Select(B →c)&=\{c\}\end{aligned} G′[S]:Select(S→aSB)Select(S→ε)Select(B→b)Select(B→c)={a}={b,c,#}={b}={c}
具有相同左部的产生式的 S e l e c t Select Select 集两两不相交,故 G ′ [ S ] G'[S] G′[S] 为 L L ( 1 ) LL(1) LL(1) 文法
例
能否通过文法变换,将下面文法转换为
L
L
(
1
)
LL(1)
LL(1) 文法
S
→
i
B
t
S
e
S
∣
i
B
t
S
∣
a
B
→
b
\begin{aligned}S &→iBtSeS | iBtS | a\\ B &→b\end{aligned}
SB→iBtSeS∣iBtS∣a→b解
S
→
i
b
t
S
S
’
∣
a
S
’
→
e
S
∣
ε
\begin{aligned}S &→ibtSS’ | a\\ S’ &→eS|ε\end{aligned}
SS’→ibtSS’∣a→eS∣ε
因为 s e l e c t ( S ’ → ε ) = F o l l o w ( S ’ ) = { # , e } select(S’ →ε)=Follow( S’ )=\{\#, e\} select(S’→ε)=Follow(S’)={#,e} ,与 { e } \{e\} {e} 是相交的,不满足 L L ( 1 ) LL(1) LL(1) 条件,所以不是 L L ( 1 ) LL(1) LL(1) 文法
角替换
- 规则的角:一规则右部的最左出现
- 如: S → c S ∣ A c S→cS|Ac S→cS∣Ac 中 c , A c,A c,A 即为规则的角
- 角替换:对一规则的非终极符的角,用其规则右部去替换它
- 非终极符角的出现意味着左公共因子可能是隐式的。消除隐式左公共因子可采用角替换
- 注意,角替换可能会使某些产生式变成无用产生式,即伴随着对文法的化简
角替换与提取左公共因子往往同时进行,角替换本身不改变文法的 L L ( 1 ) LL(1) LL(1) 性质
例
下列文法是否为
L
L
(
1
)
LL(1)
LL(1) 文法?
G
[
S
]
:
S
→
c
S
∣
A
c
A
→
a
S
∣
b
\begin{aligned}G[S]:S&→cS|Ac\\ A&→aS|b\end{aligned}
G[S]:SA→cS∣Ac→aS∣b
解
- 产生式 S → A c S→Ac S→Ac 中含有角 A A A,角替换为: S → a S c ∣ b c S→aSc|bc S→aSc∣bc
- 由于
G
′
[
S
]
G'[S]
G′[S] 中非终极符
A
A
A 变成不可达符号,
A
→
a
S
∣
b
A→aS|b
A→aS∣b 应予以删除。改造后的文法
G ′ [ S ] : S → c S ∣ a S c ∣ b c S e l e c t ( S → c S ) = { c } S e l e c t ( S → a S c ) = { a } S e l e c t ( S → b c ) = { b } \begin{aligned}G'[S] : S→cS|a&Sc|bc\\ Select(S→cS )&=\{c\}\\ Select(S→aSc)&=\{a\}\\ Select(S→bc )&=\{b\}\end{aligned} G′[S]:S→cS∣aSelect(S→cS)Select(S→aSc)Select(S→bc)Sc∣bc={c}={a}={b} - 显然, 具有相同左部的产生式的 S e l e c t Select Select 集两两相交为空,故为 L L ( 1 ) LL(1) LL(1) 文法
例
下列文法是否为
L
L
(
1
)
LL(1)
LL(1) 文法?如果不是,是否能改写为
L
L
(
1
)
LL(1)
LL(1) 文法?
G
[
S
]
:
S
→
A
B
(
1
)
A
→
B
a
∣
ε
(
2
)
B
→
D
b
∣
D
(
3
)
D
→
d
∣
ε
(
4
)
\begin{aligned}G[S]:S&→AB\ \ \ \ \ \ \ \ (1)\\ A&→Ba|\varepsilon\ \ \ \ \ \ (2)\\ B&→Db|D\ \ \ \ \ (3)\\ D&→d|\varepsilon\ \ \ \ \ \ \ \ \ \ (4) \end{aligned}
G[S]:SABD→AB (1)→Ba∣ε (2)→Db∣D (3)→d∣ε (4)
解
- 对于
S
→
A
B
S→ AB
S→AB 和
A
→
B
a
∣
ε
A→Ba| \varepsilon
A→Ba∣ε 来说,有隐含的左公共因子,
B
→
D
b
∣
D
B→Db|D
B→Db∣D 也有左公共因子,所以不是
L
L
(
1
)
LL(1)
LL(1) 文法。下面进行角替换:
-
(
2
)
(2)
(2) 代入
(
1
)
(1)
(1),得:
S
→
B
a
B
∣
B
S→ BaB|B
S→BaB∣B
提取左公共因子: S → B S ’ , S ’ → a B ∣ ε S→BS’, S’→aB|\varepsilon S→BS’,S’→aB∣ε - ( 3 ) (3) (3) 提取左公共因子 : B → D B ’ , B ’ → b ∣ ε B→DB’,B’→ b| \varepsilon B→DB’,B’→b∣ε
- 删除
(
2
)
(2)
(2),得
G ’ [ S ] : S → B S ’ S ’ → a B ∣ ε B → D B ’ B ’ → b ∣ ε D → d ∣ ε \begin{aligned} G’[S]: S&→BS’\\ S ’&→aB|\varepsilon\\ B&→DB’\\ B’&→b|\varepsilon\\ D&→d|\varepsilon\end{aligned} G’[S]:SS’BB’D→BS’→aB∣ε→DB’→b∣ε→d∣ε
-
(
2
)
(2)
(2) 代入
(
1
)
(1)
(1),得:
S
→
B
a
B
∣
B
S→ BaB|B
S→BaB∣B
发现代入后无公共左因子时,就不用继续角替换了
- 因为预测分析表无多重定义,所以改写后的文法是 L L ( 1 ) LL(1) LL(1) 文法
例
下列文法是否为
L
L
(
1
)
LL(1)
LL(1) 文法?如果不是,是否能改写为
L
L
(
1
)
LL(1)
LL(1) 文法?
S
→
A
p
∣
B
q
,
A
→
a
A
p
∣
d
,
B
→
a
B
q
∣
e
S →Ap | Bq, A →aAp | d, B →aBq | e
S→Ap∣Bq,A→aAp∣d,B→aBq∣e解
角替换:
S
→
(
a
A
p
∣
d
)
p
∣
(
a
B
q
∣
e
)
q
S →(aAp | d)p | (aBq | e)q
S→(aAp∣d)p∣(aBq∣e)q
即:
S
→
a
A
p
p
∣
a
B
q
q
∣
d
p
∣
e
q
,
A
→
a
A
p
∣
d
,
B
→
a
B
q
∣
e
S →aApp | aBqq | dp | eq,A →aAp | d, B →aBq | e
S→aApp∣aBqq∣dp∣eq,A→aAp∣d,B→aBq∣e
对
S
S
S 变换,
S
→
a
S
’
∣
d
p
∣
e
q
,
S
’
→
A
p
p
∣
B
q
q
S →aS’ | dp | eq, S’ →App | Bqq
S→aS’∣dp∣eq,S’→App∣Bqq
对
S
‘
S‘
S‘ 角替换,
S
’
→
(
a
A
p
∣
d
)
p
p
∣
(
a
B
q
∣
e
)
q
q
S’ →(aAp | d)pp | (aBq | e)qq
S’→(aAp∣d)pp∣(aBq∣e)qq 再提取左因式,
S
’
→
a
S
’
’
∣
d
p
p
∣
e
q
q
,
S
’
’
→
A
p
p
p
∣
B
q
q
q
S’ →aS’’ | dpp | eqq,S’’ →Appp | Bqqq
S’→aS’’∣dpp∣eqq,S’’→Appp∣Bqqq
继续重复,仍然有左因式。因此,无法改写为
L
L
(
1
)
LL(1)
LL(1) 文法
例:
判断下列文法是否是
L
L
(
1
)
LL(1)
LL(1) 文法,并给出
L
L
(
1
)
LL(1)
LL(1) 分析表
G
[
E
]
:
E
→
T
E
′
E
′
→
+
T
E
′
∣
ε
T
→
F
T
′
T
′
→
∗
F
T
′
∣
ε
F
→
i
∣
(
E
)
\begin{aligned}G[E]:E&→TE'\\ E'&→+TE'|ε\\ T&→FT'\\ T'&→*FT'|ε \\ F&→i|(E)\end{aligned}
G[E]:EE′TT′F→TE′→+TE′∣ε→FT′→∗FT′∣ε→i∣(E)
因为
L
L
(
1
)
LL(1)
LL(1) 分析表无多重定义,所以该文法是
L
L
(
1
)
LL(1)
LL(1) 文法
L L ( 1 ) LL(1) LL(1) 分析器
-
L
L
(
1
)
LL(1)
LL(1) 分析器是由一张
L
L
(
1
)
LL(1)
LL(1) 分析表,一个控制程序和一个分析栈组成。分析栈中存放文法符号
- 分析器初态:分析栈中压入“#”和文法开始符号 S S S,栈顶元素是 S S S。待匹配指针指向输入串的第一个待匹配符
- 伪代码: 设
w
是待匹配符号串,ip
指向w#
的第一个符号
do
{
让 X 等于栈顶符号,并且让 a 是 ip 指向的符号
if( X 是终极符或 '#')
{
if (X == a)
{
把 X 从栈顶弹出,并推进 ip;
}else
{
Error;
}
}else if (M[X, a] == "X→y1y2…yk”)
{
从栈中弹出X;
把yk, yk-1, …, y1压入栈; /* y1 在栈顶 */
else{
Error;
}
} while( X != '#')
例
以输入串
i
+
i
∗
i
i+i*i
i+i∗i 为例,给出用
L
L
(
1
)
LL(1)
LL(1) 分析器识别符号串的过程
G
[
E
]
:
E
→
T
E
′
E
′
→
+
T
E
′
∣
ε
T
→
F
T
′
T
′
→
∗
F
T
′
∣
ε
F
→
i
∣
(
E
)
\begin{aligned}G[E]:E&→TE'\\ E'&→+TE'|ε\\ T&→FT'\\ T'&→*FT'|ε \\ F&→i|(E)\end{aligned}
G[E]:EE′TT′F→TE′→+TE′∣ε→FT′→∗FT′∣ε→i∣(E)
先求
L
L
(
1
)
LL(1)
LL(1) 分析表:
下面给出部分过程:
递归子程序
- 递归下降分析法是一种确定的自顶向下的语法分析方法,其分析过程是从文法开始符号出发,执行一组递归过程,不断向下推导,直到推出句子
- 针对文法的每一非终极符,根据相应产生式各候选式的结构,为其编写一个递归函数,用来识别该非终极符所表示的语法范畴
在编写递归子程序时,需要注意:
- 文法必须是 L L ( 1 ) LL(1) LL(1) 文法 (确定的语法分析)
- 待匹配符号串是要被分析的源程序,是用助记符或整数码表示的单词类别串 (因为上一步已经经过了词法分析)
- 例如待匹配符号串
ID:=ID+NUM#
, 按从左向右扫描输入串的顺序,开始的时候,当前匹配指针指向的输入符号是ID
- 例如待匹配符号串
- 如果遇到终极符,判断是否与输入的符号匹配。若匹配,则读取下一个待匹配符号;如果匹配失败,则意味着有语法错误
- 如果遇到非终极符,则直接调用该非终极符对应的子程序
- 如果非终极符 A A A 的右部只有一个候选式,则按从左向右的顺序依次构造 A A A 的识别过程
- 如果非终极符 A A A 的右部有多个候选式,则根据每个候选式的第一个符号确定识别非终极符 A A A 的流程控制转向
在给出示例伪代码之前,先做出如下约定:
- 定义辅助函数
getsym()
的作用是读取当前指针指向的待匹配符,并使待匹配指针向前移,指向下一个待匹配符。变量sym
用来存储当前匹配指针指向的输入符号 - 默认进入子程序时已经取到了当前的输入符号;从子程序返回时,也已经取到了下一次准备匹配的输入符号
例
文法
Z
→
(
U
)
∣
a
U
b
,
U
→
d
Z
∣
e
Z→(U)| aUb ,U → dZ|e
Z→(U)∣aUb,U→dZ∣e,为其构造递归下降分析子程序。并对输入串
a
e
b
aeb
aeb 进行语法分析
解
文法中有两个非终结符号
Z
Z
Z 和
U
U
U,需要分别编两个函数来完成
Z
Z
Z 和
U
U
U 规则的识别
- 对于规则 Z → ( U ) ∣ a U b Z→(U)|aUb Z→(U)∣aUb,右部有两个候选式,因此, Z Z Z 的识别过程有两个分支,分别根据符号 ( ( ( 和 a a a 来判别
- 同理,对规则 U → d Z ∣ e U→dZ|e U→dZ∣e 设计的过程也分为两个分支
对于输入串
a
e
b
aeb
aeb,
- 从 Z Z Z 函数开始识别(开始符号),由于 s y m sym sym 不等于 ( ( (,等于 a a a,所以选择 Z Z Z 函数的右边分支,表示选择了 Z → a U b Z→aUb Z→aUb 规则。读下一个符号,使 s y m = e sym=e sym=e;
- 调 U U U 函数,因 s y m = e sym=e sym=e,表示使用 U → e U→e U→e 规则, 读下一个符号,使 s y m = b sym=b sym=b,并返回调用程序 Z Z Z 函数右边分支 U U U 的下方,接着判断 s y m = b sym=b sym=b,读下一个符号,并退出 Z Z Z.
- 在主函数中判断:若 s y m = # sym=\# sym=#, 则分析过程结束,从而判定输入串 a e b aeb aeb 语法分析成功
- 这个过程相当于构造了如下推导过程: Z ⇒ a U b ⇒ a e b Z\Rightarrow aUb\Rightarrow aeb Z⇒aUb⇒aeb
例
为下列文法的每个非终极符构造相应的递归函数
G
[
Z
]
:
Z
→
(
U
)
∣
a
U
b
U
→
d
Z
∣
e
\begin{aligned}G[Z]: Z&→(U) | aUb\\ U&→dZ | e\end{aligned}
G[Z]:ZU→(U)∣aUb→dZ∣e
解
main()
{
sym = getsym(); // 读取当前输入串的第一个符号
Z(); // 从文法开始符号对应的子程序开始识别
if (sym == '#')
OK; //”#”是程序结束的标志
else
error("error in main");
}
Z() // Z→(U) | aUb
{
if(sym == '(')
{
sym = getsym();
U();
if(sym != ')')
{
error( "缺 )" ) ;
exit(0);
} else
sym = getsym();
}
else if(sym == 'a')
{
sym = getsym();
U();
if(sym != 'b')
{
error ("缺b");
exit(0);
} else
sym = getsym();
}
else {
error ("缺 ( 或 a");
exit(0);
}
}
U() // U→dZ | e
{
if(sym == 'd')
{
sym = getsym();
Z();
}else if(sym == 'e')
sym = getsym();
else {
error( "缺 d 或 e" );
exit(0);
}
}
例
为下列文法的每个非终极符构造相应的递归函数.
G
[
E
]
:
E
→
T
E
′
E
′
→
+
T
E
′
∣
ε
T
→
F
T
′
T
′
→
∗
F
T
′
∣
ε
F
→
i
∣
(
E
)
\begin{aligned}G[E]: E&→TE'\\ E'&→+TE'|\varepsilon\\ T&→FT'\\ T'&→*FT'|\varepsilon\\ F &→ i |(E)\end{aligned}
G[E]:EE′TT′F→TE′→+TE′∣ε→FT′→∗FT′∣ε→i∣(E)
解
main()
{
sym = getsym(); // 读取当前输入串的第一个符号
E(); // 从文法开始符号对应的子程序开始识别
if (sym == '#') // 这里的 == 可能需要调用词法分析器来识别单词
OK; //”#”是程序结束的标志
else
error (“error in main”);
}
E() // E→TE'
{
if(sym == 'i' || sym == '(') /* if (sym is in first(T)) */
{
T();
E'();
}else {
error("非法符号", sym);
exit(0);
}
}
E'() // E'→+TE'|epsilon
{
if (sym == '+')
{
sym = getsym();
T();
E'();
}else if (sym != '#' && sym != ')') /* if (sym is not in follow(E')) */
{
error("非法符号", sym);
exit(0);
}
} //意味着用 E'→epsilon 推导
T() // T→FT'
{
if (sym == 'i' || sym == '(' ) /* if (sym is in first(F)) */
{
F();
T'();
}else {
error("非法符号", sym);
exit(0);
}
}
T'() // T'→*FT'|epsilon
{
if (sym == '*')
{
sym = getsym();
F();
T'();
}else if (sym != '#' && sym != '+' && sym != ')') // if (sym is not in follow(T'))
{
error("非法符号", sym);
exit(0);
}
} //意味着用 T'→epsilon 推导
F() // F → i | (E)
{
if(sym == 'i')
sym = getsym();
else if(sym == '(')
{
sym = getsym();
E();
if(sym == ')')
sym = getsym();
else {
error("缺)");
exit(0);
}
}else {
error("缺(或i ");
exit(0);
}
}
- 一般在用递归子程序时,表达式都是用迭代形式实现,更加简洁
可以比较一下下面的例子与上面的例子
例
G
[
E
]
:
E
→
E
+
T
∣
+
T
∣
−
T
T
→
T
∗
F
∣
F
F
→
i
∣
(
E
)
\begin{aligned}G[E]: E&→E+T | +T|-T \\ T&→T*F | F\\ F&→i | (E)\\\end{aligned}
G[E]:ETF→E+T∣+T∣−T→T∗F∣F→i∣(E)
设计递归子程序
解
对文法
G
G
G 消除左递归为
G
′
(
E
)
G'(E)
G′(E):(使用迭代形式)
G
′
[
E
]
:
E
→
[
+
∣
−
]
T
{
+
T
}
T
→
F
{
∗
F
}
F
→
i
∣
(
E
)
\begin{aligned}G'[E]:E&→[+|-]T\{+T\} \\ T&→F\{*F\}\\ F&→i|(E)\end{aligned}
G′[E]:ETF→[+∣−]T{+T}→F{∗F}→i∣(E)
E() // E→ [+|-] T{+T}
{
if (sym == '+' || sym == '-')
{
sym = getsym();
if(sym == 'i' || sym == '(')
T();
else {
error;
exit(0);
}
} else if (sym == 'i' || sym == '(' )
T();
else {
error;
exit(0);
}
while (sym == '+')
{
sym = getsym();
if (sym == 'i' || sym == '(' )
T();
else {
error;
exit(0);
}
}
}
T() // T→ F{*F}
{
if(sym == 'i' || sym == '(' )
F();
else {
error;
exit(0);
}
while (sym == '*')
{
sym = getsym();
if (sym == 'i' || sym == '(')
F();
else {
error;
exit(0);
}
}
}
F() // F → i | (E)
{
if(sym == 'i')
sym = getsym();
else if(sym == '(')
{
sym = getsym();
E();
if(sym == ')')
sym = getsym();
else {
error("缺)");
exit(0);
}
}else {
error("缺 ( 或 i ");
exit(0);
}
}