编译原理-5-LL(1)语法分析器
5-LL(1)语法分析器
LL(1)语法分析器
1. 什么是LL(1)语法分析器
- 自顶向下的、递归下降的、预测分析的、适用于LL(1)文法的LL(1)语法分析器
- 自顶向下构建语法分析树
- 根节点是文法的起始符号 S S S
- 每个中间节点表示对某个非终结符应用于某个产生式进行推导
- 叶节点是词法单元流$w$$
- 仅包含终结符号与特殊的文件结束符$$$
- 递归下降算法的实现框架
- 为每个非终结符写一个递归函数:但是工作量会比较大
- 内部按需调用其它非终结符对应的递归函数
1.1. 语法树生成例子
S → F S → ( S + F ) F → a \begin{array}{l} S \rightarrow F \\ S \rightarrow (S+F) \\ F \rightarrow a \\ \end{array} S→FS→(S+F)F→a
演示递归下降过程(指针移动):针对结构 w = ( ( a + a ) + a ) w=((a+a)+a) w=((a+a)+a)
- 首先进入S的递归函数,我们选择一条产生式(假设已经选择好),我们选择的是 S → ( S + F ) S \rightarrow (S + F) S→(S+F)
- 然后我们发现还是非终结符,我们仍然选择 S → ( S + F ) S \rightarrow (S + F) S→(S+F)
- 然后我们发现还是非终结符,我们选择 S → F S \rightarrow F S→F,
- 然后我们发现是终结符,检查 F → a F \rightarrow a F→a得到的结果是否和当前指针指向的位置的值,匹配则继续,否则报错。
- 之后省略,注意右括号被匹配意味相应部分递归结束。
以上的过程在本质上是DFS的过程。
- 每次都选择语法分析树最左边的非终结符进行展开,所以得到最左推导
- Q:同样是展开非终结符S,为什么前两次选择了 S → ( S + F ) S \rightarrow (S + F) S→(S+F), 而第三次选择了 S → F S \rightarrow F S→F?
- A:因为只有选择 S → ( S + F ) S \rightarrow (S + F) S→(S+F)才能生成一个左括号开头的式子,这个是由文法决定的,也就是因为它们面对的当前词法单元不同
1.2. 使用预测分析表确定产生式
- 指明了每个非终结符在面对不同的词法单元或文件结束符时,该选择哪个产生式(按编号进行索引)或者报错
- 预测分析表如下图所示
- 例子:根据当前值选择使用不同的产生式
(
选择2号产生式a且S
则选择1号产生式a且F
则选择3号产生式- 上表中的空白标记记为报错。
- 问题:如何构造上面的表格?
1.3. Definition (LL(1) 文法)
- 如果文法G的预测分析表是无冲突的, 则G是LL(1)文法。
- 无冲突: 每个单元格里只有一个生成式(编号),不然就会出现选择问题。
- 对于当前选择的非终结符,仅根据输入中当前的词法单元即可确定需要使用哪条产生式
- 递归下降的、预测分析实现方法
1.4. 实现LL(1)算法的具体代码
1.4.1. 解析 S ( ) S() S()
1: procedure S()
2: if token = '(' then
3: MATCH('(')
4: S()
5: MATCH('+')
6: F()
7: MATCH(')')
8: else if token ='a' then
9: F()
10: else
11: ERROR(token, {'(', 'a'})
1.4.2. 解析 F ( ) F() F()
1: procedure F()
2: if token = 'a' then
3: MATCH('a')
4: else
5: ERROR(token, {'a'})
1.4.3. 解析 M A T C H ( ) MATCH() MATCH()
1: procedure MATCH(t)
2: if token = t then
3: token <- NEXT-TOKEN
4: else
5: ERROR(token, t)
2. 如何计算给定文法G的预测分析表?
下文则会重点解释如何计算给定文法G的预测分析表
2.1. Definition( F i r s t ( α ) First(\alpha) First(α)集合)
对于任意的(产生式的右部) α ∈ ( N ∪ T ) ∗ \alpha \in (N \cup T)^* α∈(N∪T)∗
F I R S T ( α ) = t ∈ T ∪ ϵ ∣ α ⇒ ∗ t β ∧ α ⇒ ∗ ϵ FIRST(\alpha) = {t \in T \cup {\epsilon} | \alpha \xRightarrow{*} t\beta \wedge \alpha \xRightarrow{*}\epsilon} FIRST(α)=t∈T∪ϵ∣α∗tβ∧α∗ϵ
- F i r s t ( α ) First(\alpha) First(α) 是可从 α \alpha α推导得到的句型的首终结符号的集合
- T T T是终结符集合, t t t可能是终结符,也可能是 ϵ \epsilon ϵ
- 考虑非终结符A的所有产生式 A → α 1 , A → α 2 , . . . , A → α m A \rightarrow \alpha_1,A \rightarrow \alpha_2, ... ,A \rightarrow \alpha_m A→α1,A→α2,...,A→αm,如果它们对应的 F i r s t ( α i ) First(\alpha_i) First(αi) 集合互不相交,则只需查看当前输入词法单元, 即可确定选择哪个产生式(或报错),如果相交,则不是LL(1)文法。
2.2. Definition( F o l l o w ( A ) Follow(A) Follow(A)集合)
对于任意的(产生式的左部) 非终结符 A ∈ N A \in N A∈N
F o l l o w ( A ) = { t ∈ T ∪ { $ } ∣ ∃ w . S ⇒ ∗ w = β A t γ } Follow(A) = \{t \in T \cup \{\$\} | \exist w.S \xRightarrow{*} w = \beta A t \gamma\} Follow(A)={t∈T∪{$}∣∃w.S∗w=βAtγ}
- F o l l o w ( A ) Follow(A) Follow(A)是可能在某些句型中紧跟在A右边的终结符的集合
- t t t可以是终结符和文件终止符。
- 考虑产生式: A → a A \rightarrow a A→a,如果从 α \alpha α可能推导出空串( α ⇒ ∗ ϵ \alpha \xRightarrow{*} \epsilon α∗ϵ),则只有当当前词法单元 t ∈ F o l l o w ( A ) t \in Follow(A) t∈Follow(A), 才可以选择该产生式:因为 A A A可能推导没了,然后就是 t t t了,我们期望 t t t能够匹配当前位置上的对应符号。
2.3. 计算 F i r s t First First集合
2.3.1. 先计算每个符号X的 F i r s t ( X ) First(X) First(X)集合
- 如果 Y 1 . . . Y i − 1 Y_1...Y_{i-1} Y1...Yi−1都可以推导成为空串,那么 Y i Y_i Yi的首终结符应该也是X的首终结符。
- 不断应用上面的规则, 直到每个 F i r s t ( X ) First(X) First(X)都不再变化(闭包!!!)尤其注意递归。
2.3.2. 再计算每个符号串 α \alpha α的 F i r s t ( α ) First(\alpha) First(α)集合
α = X β F i r s t ( α ) = { F i r s t ( X ) ϵ ∈ L ( X ) F i t s t ( X ) ∪ F i r s t ( β ) ϵ ∉ L ( X ) \begin{array}{l} \alpha = X \beta \\ First(\alpha) = \begin{cases} First(X) & \epsilon \in L(X) \\ Fitst(X)\cup First(\beta) & \epsilon \notin L(X) \\ \end{cases} \end{array} α=XβFirst(α)={First(X)Fitst(X)∪First(β)ϵ∈L(X)ϵ∈/L(X)
2.3.3. 求解 F i r s t ( α ) First(\alpha) First(α)的例子
( 1 ) X → Y ( 2 ) X → a ( 3 ) Y → ϵ ( 4 ) Y → c ( 5 ) Z → d ( 6 ) Z → X Y Z \begin{array}{l} (1) & X \rightarrow Y \\ (2) & X \rightarrow a \\ (3) & Y \rightarrow \epsilon \\ (4) & Y \rightarrow c \\ (5) & Z \rightarrow d \\ (6) & Z \rightarrow XYZ \\ \end{array} (1)(2)(3)(4)(5)(6)X→YX→aY→ϵY→cZ→dZ→XYZ
F I R S T ( X ) = { a , c , ϵ } F I R S T ( Y ) = { c , ϵ } F I R S T ( Z ) = { a , c , d } F I R S T ( X Y Z ) = F I R S T ( X ) ∪ F I S R T ( Y Z ) = F I R S T ( X ) ∪ F I R S T ( Y ) ∪ F I R S T ( Z ) = { a , c , d } \begin{array}{l} FIRST(X) = \{a,c,\epsilon\} \\ FIRST(Y) = \{c, \epsilon\} \\ FIRST(Z) = \{a, c, d\} \\ FIRST(XYZ) = FIRST(X) \cup FISRT(YZ) \\ = FIRST(X) \cup FIRST(Y) \cup FIRST(Z) = \{a, c, d\}\\ \end{array} FIRST(X)={a,c,ϵ}FIRST(Y)={c,ϵ}FIRST(Z)={a,c,d}FIRST(XYZ)=FIRST(X)∪FISRT(YZ)=FIRST(X)∪FIRST(Y)∪FIRST(Z)={a,c,d}
注意不要忘记和 ϵ \epsilon ϵ相关的操作。
2.4. 为每个非终结符X计算 F o l l o w ( X ) Follow(X) Follow(X)集合
- 不断应用上面的规则, 直到每个Follow(X) 都不再变化(闭包!!!)
- Follow例子:注意闭包,可以无限扩充
( 1 ) X → Y ( 2 ) X → a ( 3 ) Y → ϵ ( 4 ) Y → c ( 5 ) Z → d ( 6 ) Z → X Y Z \begin{array}{l} (1) & X \rightarrow Y \\ (2) & X \rightarrow a \\ (3) & Y \rightarrow \epsilon \\ (4) & Y \rightarrow c \\ (5) & Z \rightarrow d \\ (6) & Z \rightarrow XYZ \\ \end{array} (1)(2)(3)(4)(5)(6)X→YX→aY→ϵY→cZ→dZ→XYZ
F O L L O W ( X ) = { a , c , d , $ } F O L L O W ( Y ) = { a , c , d , $ } F O L L O W ( Z ) = ∅ \begin{array}{l} FOLLOW(X) = \{a, c, d, \$\} \\ FOLLOW(Y) = \{a, c, d, \$\} \\ FOLLOW(Z) = \emptyset \\ \end{array} FOLLOW(X)={a,c,d,$}FOLLOW(Y)={a,c,d,$}FOLLOW(Z)=∅
2.5. 如何根据 F i r s t First First与 F o l l o w Follow Follow集合计算给定文法G的预测分析表?
按照以下规则, 在表格[A, t] 中填入生成式 A → α ( 编 号 ) A \rightarrow \alpha(编号) A→α(编号)
t ∈ F i r s t ( α ) ( 1 ) α ⇒ ∗ ϵ ∧ t ∈ F o l l o w ( A ) ( 2 ) \begin{array}{l} t \in First(\alpha) & (1)\\ \alpha \xRightarrow{*} \epsilon \wedge t \in Follow(A) & (2)\\ \end{array} t∈First(α)α∗ϵ∧t∈Follow(A)(1)(2)
对于每一个生成式只要满足上面一条规则即可(或关系),首先,在下单元格中可以填写 A → α A \rightarrow \alpha A→α,则可以推导出 α ⇒ ∗ ϵ ∧ t ∈ F o l l o w ( A ) \alpha \xRightarrow{*} \epsilon \wedge t \in Follow(A) α∗ϵ∧t∈Follow(A)(必要条件),但是由于是LL(1)文法的唯一性,那么必要条件等价于充分条件,也就是这是一个充要条件。
t t t | |
---|---|
A A A | A → α A \rightarrow \alpha A→α |
2.6. Definition (LL(1) 文法)
如果文法G的预测分析表是无冲突的, 则G是LL(1)文法。
(
1
)
X
→
Y
(
2
)
X
→
a
(
3
)
Y
→
ϵ
(
4
)
Y
→
c
(
5
)
Z
→
d
(
6
)
Z
→
X
Y
Z
\begin{array}{l} (1) & X \rightarrow Y \\ (2) & X \rightarrow a \\ (3) & Y \rightarrow \epsilon \\ (4) & Y \rightarrow c \\ (5) & Z \rightarrow d \\ (6) & Z \rightarrow XYZ \\ \end{array}
(1)(2)(3)(4)(5)(6)X→YX→aY→ϵY→cZ→dZ→XYZ
F
I
R
S
T
(
X
)
=
{
a
,
c
,
ϵ
}
F
I
R
S
T
(
Y
)
=
{
c
,
ϵ
}
F
I
R
S
T
(
Z
)
=
{
a
,
c
,
d
}
F
I
R
S
T
(
X
Y
Z
)
=
F
I
R
S
T
(
X
)
∪
F
I
S
R
T
(
Y
Z
)
=
F
I
R
S
T
(
X
)
∪
F
I
R
S
T
(
Y
)
∪
F
I
R
S
T
(
Z
)
=
{
a
,
c
,
d
}
\begin{array}{l} FIRST(X) = \{a,c,\epsilon\} \\ FIRST(Y) = \{c, \epsilon\} \\ FIRST(Z) = \{a, c, d\} \\ FIRST(XYZ) = FIRST(X) \cup FISRT(YZ) \\ = FIRST(X) \cup FIRST(Y) \cup FIRST(Z) = \{a, c, d\}\\ \end{array}
FIRST(X)={a,c,ϵ}FIRST(Y)={c,ϵ}FIRST(Z)={a,c,d}FIRST(XYZ)=FIRST(X)∪FISRT(YZ)=FIRST(X)∪FIRST(Y)∪FIRST(Z)={a,c,d}
F
O
L
L
O
W
(
X
)
=
{
a
,
c
,
d
,
$
}
F
O
L
L
O
W
(
Y
)
=
{
a
,
c
,
d
,
$
}
F
O
L
L
O
W
(
Z
)
=
∅
\begin{array}{l} FOLLOW(X) = \{a, c, d, \$\} \\ FOLLOW(Y) = \{a, c, d, \$\} \\ FOLLOW(Z) = \emptyset \\ \end{array}
FOLLOW(X)={a,c,d,$}FOLLOW(Y)={a,c,d,$}FOLLOW(Z)=∅
3. LL(1) 语法分析器
- L:从左向右(left-to-right) 扫描输入
- L:构建最左(leftmost) 推导
- 1:只需向前看一个输入符号便可确定使用哪条产生式
3.1. 非递归的预测分析方法
非递归算法效率会高一些
3.2. 改造文法成为LL(1)文法
- 改造它
- 消除左递归
- 提取左公因子
- 有左递归,有左公因子,则必然不是LL(1)文法
- 没有左递归,没有左公因子,则未必是LL(1)文法
3.2.1. 左递归
E → E + T ∣ E − T ∣ T T → T ∗ F ∣ T / F ∣ F F → ( E ) ∣ i d ∣ n u m \begin{array}{l} E \rightarrow E + T | E - T | T \\ T \rightarrow T * F | T / F | F \\ F \rightarrow (E) | id | num \\ \end{array} E→E+T∣E−T∣TT→T∗F∣T/F∣FF→(E)∣id∣num
- E 在不消耗任何词法单元的情况下, 直接递归调用E, 造成死循环,E左侧没有非终结符,不能消耗符号。
- 存在 F I R S T ( E + T ) ∩ F I R S T ( T ) ≠ ∅ FIRST(E + T) \cap FIRST(T) \neq \emptyset FIRST(E+T)∩FIRST(T)=∅,不是LL(1)文法
- 消除左递归
3.2.2. 消除左递归
- 左递归:
E → E + T ∣ T E \rightarrow E + T | T E→E+T∣T
-
消除左递归(至少会消耗一个+)
E → T E ′ E ′ → + T E ′ ∣ ϵ \begin{array}{l} E \rightarrow TE' \\ E' \rightarrow +TE' | \epsilon \\ \end{array} E→TE′E′→+TE′∣ϵ -
将左递归转为右递归,注: 右递归对应右结合; 需要在后续阶段进行额外处理,至少语法分析可以通过。
3.2.3. 左递归例子
A → A α 1 ∣ A α 2 ∣ . . . A α m ∣ β 1 ∣ β 2 ∣ . . . β n \begin{array}{l} A \rightarrow A\alpha_1 | A\alpha_2 |...A\alpha_m|\beta_1|\beta_2|...\beta_n \\ \end{array} A→Aα1∣Aα2∣...Aαm∣β1∣β2∣...βn
- β i \beta_i βi都不以A开发
A → β 1 A ′ ∣ β 2 A ′ ∣ . . . ∣ β n A ′ A ′ → α 1 A ′ ∣ α 2 A ′ ∣ . . . ∣ α m A ′ ∣ ϵ \begin{array}{l} A \rightarrow \beta_1A' | \beta_2A' | ... | \beta_nA' \\ A' \rightarrow \alpha_1A'|\alpha_2A'|...|\alpha_mA'|\epsilon \\ \end{array} A→β1A′∣β2A′∣...∣βnA′A′→α1A′∣α2A′∣...∣αmA′∣ϵ
3.2.4. 左递归例子II
E → E + T ∣ T T → T ∗ F ∣ F F → ( E ) ∣ i d \begin{array}{l} E \rightarrow E + T|T \\ T \rightarrow T * F|F \\ F \rightarrow (E) | id \\ \end{array} E→E+T∣TT→T∗F∣FF→(E)∣id
- 消除左递归
E → T E ′ E ′ → + T E ′ ∣ ϵ T → F T ′ T ′ → ∗ F T ′ ∣ ϵ F → ( E ) ∣ i d ∣ n u m \begin{array}{l} E \rightarrow TE' \\ E' \rightarrow +TE' | \epsilon \\ T \rightarrow FT' \\ T' \rightarrow *FT' | \epsilon \\ F \rightarrow (E)|id|num \\ \end{array} E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵF→(E)∣id∣num
3.2.5. 非直接左递归
S → A a ∣ b A → A c ∣ S b ∣ ϵ S ⇒ A a ⇒ S d a \begin{array}{l} S \rightarrow Aa|b \\ A \rightarrow Ac|Sb|\epsilon\\ S \Rightarrow Aa \Rightarrow Sda \\ \end{array} S→Aa∣bA→Ac∣Sb∣ϵS⇒Aa⇒Sda
3.2.6. 消除左递归算法
A k → A l α ⇒ l > k A_k \rightarrow A_l\alpha \Rightarrow l > k Ak→Alα⇒l>k
- 不会出现左递归,也不会出现间接左递归:因为不会回到自己
- 例子:
S → A a ∣ b A → A c ∣ S b ∣ ϵ \begin{array}{l} S \rightarrow Aa|b \\ A \rightarrow Ac|Sb|\epsilon \\ \end{array} S→Aa∣bA→Ac∣Sb∣ϵ
考虑1号非终结符S,替换掉A中的S
A → A c ∣ A a d ∣ b d ∣ ϵ A \rightarrow Ac|Aad|bd|\epsilon A→Ac∣Aad∣bd∣ϵ
考虑2号非终结符A,使用A’进行替换,首先找不含A的生成A’,然后交换包含A的位置
S → A a ∣ b A → b d A ′ ∣ A ′ A ′ → c A ′ ∣ a d A ′ ∣ ϵ \begin{array}{l} S \rightarrow Aa|b \\ A \rightarrow bdA'|A' \\ A' \rightarrow cA'|adA'|\epsilon \\ \end{array} S→Aa∣bA→bdA′∣A′A′→cA′∣adA′∣ϵ
3.2.7. 消除左递归的例子
注意"("等部分首终结符,最好选择一个顺序:比如从下往上算。
E → T E ′ E ′ → + T E ′ ∣ ϵ T → F T ′ T ′ → ∗ F T ′ ∣ ϵ F → ( E ) ∣ i d ∣ n u m \begin{array}{l} E \rightarrow TE' \\ E' \rightarrow +TE' | \epsilon \\ T \rightarrow FT' \\ T' \rightarrow *FT' | \epsilon \\ F \rightarrow (E)|id|num \\ \end{array} E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵF→(E)∣id∣num
F i r s t 从 左 侧 开 始 找 F i r s t ( F ) = { ( , i d } F i r s t ( T ′ ) = { ∗ , ϵ } F i r s t ( T ) = { ( , i d } F i r s t ( E ) = { ( , i d } F i r s t ( E ′ ) = { + , ϵ } F o l l o w 从 右 侧 开 始 找 F o l l o w ( E ) = { ) , $ } F o l l o w ( E ′ ) = { ) , $ } F o l l o w ( T ) = { + , ) , $ } F o l l o w ( T ′ ) = { + , ) , $ } F o l l o w ( F ) = { + , ∗ , ) , $ } \begin{array}{l} First从左侧开始找 \\ First(F) = \{(,id\} \\ First(T') = \{*,\epsilon\} \\ First(T) = \{(,id\} \\ First(E) = \{(,id\} \\ First(E') = \{+ , \epsilon\} \\ Follow从右侧开始找 \\ Follow(E) = \{), \$\} \\ Follow(E') = \{), \$\} \\ Follow(T) = \{+, ), \$\} \\ Follow(T') = \{+, ), \$\} \\ Follow(F) = \{+, ∗, ), \$\} \\ \end{array} First从左侧开始找First(F)={(,id}First(T′)={∗,ϵ}First(T)={(,id}First(E)={(,id}First(E′)={+,ϵ}Follow从右侧开始找Follow(E)={),$}Follow(E′)={),$}Follow(T)={+,),$}Follow(T′)={+,),$}Follow(F)={+,∗,),$}
3.2.8. 提取左公因子
包含左公因子
S → i E t S ∣ i E t S e S ∣ a E → b \begin{array}{l} S \rightarrow iEtS|iEtSeS|a \\ E \rightarrow b \end{array} S→iEtS∣iEtSeS∣aE→b
提取左公因子
S → i E t S S ′ ∣ a S ′ → e S ∣ ϵ E → b \begin{array}{l} S \rightarrow iEtSS'|a \\ S' \rightarrow eS|\epsilon \\ E \rightarrow b \\ \end{array} S→iEtSS′∣aS′→eS∣ϵE→b
解决二义性(人为解决): 选择 S ′ → e S S' \rightarrow eS S′→eS, 将else与前面最近的then关联起来
3.2.9. $$$符号的必要性
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)