编译原理 笔记 1
自顶向下语法分析
自顶向下的核心思想是从文法的开始符号出发,每一步推导得到一个句型,最终产生一个句子即为期待的终结符串
带回溯的自顶向下分析
由于每一步推导的终结符串和使用的文法都是不确定的,因此复杂度很高,只能进行不断回溯,因此我们进行改进:每步推导总是用最左边的非终结符,产生最左推导,如果想进一步确认使用的文法,则需改进为:
确定的自顶向下分析
在从左向右扫描的过程中,向前查看常数个的单词,以确定每一步使用的文法
例如对于文法 G:
SAB→AB→aA∣ε→b∣bB
则在自顶向下分析 anbm(n≥0,m>0) 时,只需要向前查看2个单词,即可预测每步所应该使用的文法
如果想要实现这种分析,需要保证文法不含左递归与左公因子,否则:
考虑文法 G:
S→Sa∣b
则在分析 ban 时需要向前看的单词数为 n+2 个
考虑文法 G:
SA→aAb∣aAc→a∣aA
则在分析 an(b+c) 时需要向前看的单词数为 n+2 个
都不为常数,无法确定地分析
LL(1) 分析
最常用的预测分析方法,要求文法是LL(1)文法
- 从左向右扫描单词
- 每步产生最左推导
- 向前看一个单词
重要的集合
First集合
First集合定义为:
设 G=(VT,VN,P,S) 是上下文无关文法,则对 α∈(VT∪VN)∗:
First(α)={a∣α⇒∗aβ,a∈VT,β∈(VT∪VN)∗ or a=β=ε}
即任意句型或句子的 First 是指这个句型或句子能推导出的串中首个单词的集合
First(α) 计算过程为:
- 先置所有 First(α)=∅
- 若 α∈VT∪{ε},则First(α)={α}
- 若 α=X1X2…Xk∈(VT∪VN)∗,则先置 First(α)=First(X1)
遍历 Xi,若X1…Xi⇒∗ε
则 First(α)∪=First(Xi+1),其中 Xk+1=ε
- 若 α∈VN,且α→X1X2…Xk,
则 First(α)∪=First(X1X2…Xk)
Follow集合
Follow集合定义为:
设 G=(VT,VN,P,S) 是上下文无关文法,则对 A∈VN:
Follow(A)={a∣S#⇒∗αAβ#,a∈First(β#),α,β∈(VT∪VN)∗}
也即 Follow 集合被定义为可能在某些句型中紧跟在 A 右边的终结符集合
Follow(A) 的计算方法为:
- 置 Follow(S)={#},其他均为 ∅
- 循环直到所有集合不变:
- 对于 A→αBβ,Follow(B)∪=First(β)−{ε}
- 若 ε∈First(β),则 Follow(B)∪=Follow(A)
预测集合
预测集合的定义为:
设 G=(VT,VN,P,S) 是上下文无关文法,则对 A→α∈P:
PS(A→α)={First(α)(First(α)−{ε})∪Follow(A)ε∈/First(α)ε∈First(α)
预测集合给出了读入了什么字符的时候需要采用产生式A→α
LL(1)文法
文法 G 是 LL(1) 文法当且仅当对于 G 的每个非终结符 A 的任何两个不同产生式 A→α∣β,满足:
PS(A→α)∩PS(A→β)=∅
递归下降LL(1)分析程序
每个非终结符对应一个子程序,每个子程序的行为根据语法描述来明确:
- 根据当前非终结符的PS集合与下一个输入符号选择产生式
- 如果产生式右端遇到非终结符,则调用相应的子程序
- 如果产生式右端遇到终结符,判断当前读入的单词是否与该终结符相匹配,匹配则继续读取,反之报错
实际应用中,可以将产生式的右端添加新运算,使之更加简洁,例如将 S→XSS→ε 替换为 S→{X} 等,具体来说:
- {X}=X∗
- [X]=X∣ε
- (X) 代表 X 优先
表驱动LL(1)分析程序
由PS集合形成一个预测分析表,即根据非终结符和下一个单词决定产生式的表,并利用该表和一个下推栈实现:
- 将 # 和 S 依次入栈
- 若栈顶为终结符,则判断读入的单词和终结符是否匹配,匹配则出栈并继续读取,反之报错
- 若栈顶为非终结符,则查表找到产生式,若为
None 则报错,反之非终结符出栈,产生式右端从右至左依次入栈,无需继续读入
- 直到栈顶和下一位输入都是 #
可以证明,预测分析表的每一项都只包含一个产生式,当且仅当文法是 LL(1) 的
文法变换
主要包含消除左递归与提取左公因子两种,通常用于将一些文法转换成 LL(1) 文法
消除左递归
消除直接左递归
对 P→Pα∣β,α=ε 且 β 首字符不是 P,则消除方法为引入新终结符 Q 使得:
PQ→βQ→αQ∣ε
对于一般形式 P→Pα1∣…∣Pαm∣β1∣…∣βn,则:
PQ→β1Q∣…∣βmQ→α1Q∣…∣αnQ∣ε
消除一般左递归
对于无环无 ε 产生式的文法,消除一般左递归的方法为:
- 排列非终结符A1,A2,…,An
for i in 1..n:
for j in 1..(i-1):
- 对于形如 Ai→Ajr 的规则
- 其中 Aj 的全部产生式为 Aj→α1∣α2∣…∣αk
- 将 Ai 产生式替换为 Ai→α1r∣α2r∣…∣αkr
- 再消除 Ai 的直接左递归
- 化简文法
提取左公因子
对于形如 P→αβ∣αγ 的产生式,增加新非终结符 Q 使得:
PQ→αQ→β∣γ
一般化为 P→αβ1∣…∣αβm∣γ1∣…∣γn:
PQ→αQ∣γ1∣…∣γm→β1∣…∣βn
预测分析中的出错处理
处理原则:
表驱动LL(1)分析中的错误处理
对于栈顶终结符与输入不匹配,直接弹出终结符
对于栈顶非终结符与输入符号在表中找不到产生式,我们采用恐慌模式,即跳过一些符号以找到同步符号
同步符号集合的构建为:
- Follow(A)中的所有符号都是A的同步符号
- 将First(B)中的符号加入A同步符号,代表A遇到错误的时候可以从B开始继续分析
递归下降分析的错误处理
当递归进入某个语法单位的时候,检查当前符号是否属于该单位的开始符号,离开该语法单位的时候检查符号是否属于该单位的结束符号
若不属于则不断滤去直到到达补救集合(即开始符号与结束符号的并集)中的符号并重新判断
LL(k)的结论
LL(k) 文法的定义是 LL(1) 的推广,有关的结论有:
- 给定 k>0,一个 CFG是否为 LL(k) 是可判定的
- 给定CFG,是否存在 k 使得该文法是 LL(k) 是不可判定的
- 给定CFG,是否存在与之等价的 LL(k) 是不可判定的
- 两个 LL(k) 是否相等是可判定的
- LL(k) 无二义
- LL(k) 中不存在左递归
- 给定 k>0,不含 ε 产生式的 LL(k) 的集合真包含于不含 ε 产生式的 LL(k+1) 的集合