编译原理 笔记 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→αβ∣αγ的产生式,增加新终结符使得:
PQ→αQ→β∣γ
一般化为P→αβ1∣…∣αβm∣γ1∣…∣γn:
PQ→αQ∣γ1∣…∣γn→β1∣…∣βm
预测分析中的出错处理
处理原则:
表驱动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)的集合