LL(1)语法分析器
1. 什么是LL(1)语法分析器
- 自顶向下的、递归下降的、预测分析的、适用于LL(1)文法的LL(1)语法分析器
- 自顶向下构建语法分析树
- 根节点是文法的起始符号S
- 每个中间节点表示对某个非终结符应用于某个产生式进行推导
- 叶节点是词法单元流w$
- 仅包含终结符号与特殊的文件结束符$
- 递归下降算法的实现框架
- 为每个非终结符写一个递归函数:但是工作量会比较大
- 内部按需调用其它非终结符对应的递归函数
1.1. 语法树生成例子
S→FS→(S+F)F→a
演示递归下降过程(指针移动):针对结构w=((a+a)+a)
- 首先进入S的递归函数,我们选择一条产生式(假设已经选择好),我们选择的是S→(S+F)
- 然后我们发现还是非终结符,我们仍然选择S→(S+F)
- 然后我们发现还是非终结符,我们选择S→F,
- 然后我们发现是终结符,检查F→a得到的结果是否和当前指针指向的位置的值,匹配则继续,否则报错。
- 之后省略,注意右括号被匹配意味相应部分递归结束。
以上的过程在本质上是DFS的过程。
- 每次都选择语法分析树最左边的非终结符进行展开,所以得到最左推导
- Q:同样是展开非终结符S,为什么前两次选择了S→(S+F), 而第三次选择了S→F?
- A:因为只有选择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()
1 2 3 4 5 6 7 8 9 10 11
| 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, )
|
1.4.2. 解析F()
1 2 3 4 5
| 1: procedure F() 2: if token = 'a' then 3: MATCH('a') 4: else 5: ERROR(token, )
|
1.4.3. 解析MATCH()
1 2 3 4 5
| 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(First(α)集合)
对于任意的(产生式的右部) α∈(N∪T)∗
FIRST(α)=t∈T∪ϵ∣α∗tβ∧α∗ϵ
- First(α) 是可从α推导得到的句型的首终结符号的集合
- T是终结符集合,t可能是终结符,也可能是ϵ
- 考虑非终结符A的所有产生式A→α1,A→α2,...,A→αm,如果它们对应的First(αi) 集合互不相交,则只需查看当前输入词法单元, 即可确定选择哪个产生式(或报错),如果相交,则不是LL(1)文法。
2.2. Definition(Follow(A)集合)
对于任意的(产生式的左部) 非终结符A∈N
Follow(A)={t∈T∪{$}∣∃w.S∗w=βAtγ}
- Follow(A)是可能在某些句型中紧跟在A右边的终结符的集合
- t可以是终结符和文件终止符。
- 考虑产生式:A→a,如果从α可能推导出空串(α∗ϵ),则只有当当前词法单元t∈Follow(A), 才可以选择该产生式:因为A可能推导没了,然后就是t了,我们期望t能够匹配当前位置上的对应符号。
2.3. 计算First集合
2.3.1. 先计算每个符号X的First(X)集合
- 如果Y1...Yi−1都可以推导成为空串,那么Yi的首终结符应该也是X的首终结符。
- 不断应用上面的规则, 直到每个First(X)都不再变化(闭包!!!)尤其注意递归。
2.3.2. 再计算每个符号串α的First(α)集合
α=XβFirst(α)={First(X)Fitst(X)∪First(β)ϵ∈L(X)ϵ∈/L(X)
2.3.3. 求解First(α)的例子
(1)(2)(3)(4)(5)(6)X→YX→aY→ϵY→cZ→dZ→XYZ
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}
注意不要忘记和ϵ相关的操作。
2.4. 为每个非终结符X计算Follow(X)集合
- 不断应用上面的规则, 直到每个Follow(X) 都不再变化(闭包!!!)
- Follow例子:注意闭包,可以无限扩充
(1)(2)(3)(4)(5)(6)X→YX→aY→ϵY→cZ→dZ→XYZ
FOLLOW(X)={a,c,d,$}FOLLOW(Y)={a,c,d,$}FOLLOW(Z)=∅
2.5. 如何根据First与Follow集合计算给定文法G的预测分析表?
按照以下规则, 在表格[A, t] 中填入生成式A→α(编号)
t∈First(α)α∗ϵ∧t∈Follow(A)(1)(2)
对于每一个生成式只要满足上面一条规则即可(或关系),首先,在下单元格中可以填写A→α,则可以推导出α∗ϵ∧t∈Follow(A)(必要条件),但是由于是LL(1)文法的唯一性,那么必要条件等价于充分条件,也就是这是一个充要条件。
|
t |
A |
A→α |
2.6. Definition (LL(1) 文法)
如果文法G的预测分析表是无冲突的, 则G是LL(1)文法。
(1)(2)(3)(4)(5)(6)X→YX→aY→ϵY→cZ→dZ→XYZ
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}
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∣TT→T∗F∣T/F∣FF→(E)∣id∣num
- E 在不消耗任何词法单元的情况下, 直接递归调用E, 造成死循环,E左侧没有非终结符,不能消耗符号。
- 存在FIRST(E+T)∩FIRST(T)=∅,不是LL(1)文法
- 消除左递归
3.2.2. 消除左递归
- 左递归:
E→E+T∣T
- 消除左递归(至少会消耗一个+)
E→TE′E′→+TE′∣ϵ
- 将左递归转为右递归,注: 右递归对应右结合; 需要在后续阶段进行额外处理,至少语法分析可以通过。
3.2.3. 左递归例子
A→Aα1∣Aα2∣...Aαm∣β1∣β2∣...βn
A→β1A′∣β2A′∣...∣βnA′A′→α1A′∣α2A′∣...∣αmA′∣ϵ
3.2.4. 左递归例子II
E→E+T∣TT→T∗F∣FF→(E)∣id
E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵF→(E)∣id∣num
3.2.5. 非直接左递归
S→Aa∣bA→Ac∣Sb∣ϵS⇒Aa⇒Sda
3.2.6. 消除左递归算法
Ak→Alα⇒l>k
- 不会出现左递归,也不会出现间接左递归:因为不会回到自己
- 例子:
S→Aa∣bA→Ac∣Sb∣ϵ
考虑1号非终结符S,替换掉A中的S
A→Ac∣Aad∣bd∣ϵ
考虑2号非终结符A,使用A’进行替换,首先找不含A的生成A’,然后交换包含A的位置
S→Aa∣bA→bdA′∣A′A′→cA′∣adA′∣ϵ
3.2.7. 消除左递归的例子
注意"("等部分首终结符,最好选择一个顺序:比如从下往上算。
E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵF→(E)∣id∣num
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→iEtS∣iEtSeS∣aE→b
提取左公因子
S→iEtSS′∣aS′→eS∣ϵE→b
解决二义性(人为解决): 选择S′→eS, 将else与前面最近的then关联起来
3.2.9. $符号的必要性