1. 自上而下分析法思路
从文法开始符号出发,向下推导,推出句子
自上而下分析的主旨
针对输入串,试图用一切可能的办法,从文法开始符号出发,自上而下的为输入串建立一颗语法树
2. 自上而下分析法面临的问题
2.1 回溯
当一个非终结符用某一个候选匹配成功时,这种匹配可能是暂时的
如果匹配到后面,发现出错,则必须回溯到前面某一个状态
2.2 左递归问题
如果存在非终结符 P ,满足 P⟹+Pα ,如果刚好匹配到了 P 处,则会陷入无限循环中
如果是右递归则不会出现该问题,因为扫描指针会一直在移动,而不是停止不动,只要扫描指针在移动,那么输入串一定会扫描完
3. LL(1) 分析法
两个目标:克服回溯;消除左递归
3.1 消除左递归
一个文法可以消除左递归的条件
不含回路 P⟹+P
3.1.1 直接消除左递归
引例:
P→Pα∣β ⟶ P⇒Pα⇒Pαα⇒⋯⋯⇒Pα⋯α⇒βα⋯α
消除左递归后: PP′→βP′→αP′∣ϵ ⟶ P⇒βP′⇒βαP′⇒βααP′⇒⋯⋯⇒βα⋯αP′⇒βα⋯α
一般而言,假定 P ,满足 P→Pα1∣Pα2∣⋯∣Pαm∣β1∣β2∣⋯∣βn ,其中,每个 α 都不等于 ϵ ,每个 β 都不以 P 开头
消除左递归的通式: PP′→β1P′∣β2P′∣⋯∣βnP′→α1P′∣α2P′∣⋯∣αmP′∣ϵ
例:文法 G(E) :
ETF→E+T∣T→T∗F∣F→(E)∣i 消除左递归 EE′TT′F→TE′→+TE′∣ϵ→FT′→∗FT′∣ϵ→(E)∣i
3.1.2 间接消除左递归
对于文法 G(S) :
SQR→Qc∣c→Rb∣b→Sa∣a
虽然没有直接左递归,但 S、Q、R都是左递归的 S⇒Qc⇒Rbc⇒Sabc
SQR→Qc∣c→Rb∣b→Sa∣a SQR→Qc∣c→Sab∣ab∣b→Sa∣a SQR→Sabc∣abc∣bc∣ c→Sab∣ab∣b→Sa∣a
3.2 消除左递归算法
把文法 G 的所有非终结符按任一种顺序排列成 P1,P2,…,Pn; 按此顺序执行
FOR i := 1 TO n DO
BEGIN
FOR j := 1 TO i - 1 DO
把形如 Pi→Pjγ 的规则改写成
Pi→δ1γ∣δ2γ∣⋯∣δkγ;
(其中 Pj→δ1∣δ2∣⋯∣δk 是关于 Pj 的所有规则)
消除关于 Pi 规则的直接左递归性
END
化简由 2 所得的文法,去除那些从开始符号出发永远都无法到达的非终结符产生的规则
3.3 举例
P1P2PiPn−1Pn→P2a1→P3a2→⋯→Pnan−1→P1an∣γn
算法过程:
when i : 1 to n -1, no change
when i = n:
j = 1; Pn→P1an∣γn⟹Pn→P2a1an∣γn
j = 2; Pn→P2a1an∣γn⟹Pn→P3a2a1an∣γn
⋯⋯
j = n - 1; Pn→Pn−1an−2an−3⋯a1an∣γn⟹Pn→Pnan−1an−2⋯a1an∣γn
消除左递归后的结果:
Pn→γnPn′Pn′→an−1an−2⋯a1anPn′∣ϵ
3.4 消除回溯
确保做出的选择唯一正确,这样就可以避免回溯
在讨论消除回溯时,文法已经不存在左递归了
两个步骤:构建终结首符集 FIRST(α) 、构建 FOLLOW(A)
构造
FIRST(α)
FIRST(α) 的作用是:明确告诉下一个要匹配的字符哪一个非终结符可以推导出来,即保存每个非终结符可以推出的第一个字符的集合。
构造前的处理:提取公共左因子
假设 A 的规则是: A→δβ1∣δβ2∣⋯∣δβn∣γ1∣γ2∣…γm ,其中,每个 γ 不以 δ 开头,那么就可以改写这些规则来消除功能左因子 δ ,改写后为: A→δA′∣γ1∣γ2∣…γmA′→β1∣β2∣⋯∣βn
正式构造:唯一原则,把能推出的第一个字符加入集合,如果能推出的第一个是非终结符,则先放着
构造规则:
A→∗⟹FIRST(A)={∗}
A→∗T⟹FIRST(A)={∗}
A→TE⟹FIRST(A)=FIRST(T) ,非 ϵ 元素
A→TEF
若 FIRST(T) 不含 ϵ ,则只把 FIRST(T) 中的元素加入 FIRST(A)
若 FIRST(T) 含有 ϵ ,则先把 FIRST(T) 中非 ϵ 元素加入 FIRST(A) ,再把 FIRST(E) 中非 ϵ 元素加入 FIRST(A)
若 FIRST(E) 含有 ϵ ,则把 FIRST(F) 中非 ϵ 元素加入 FIRST(A)
若 FIRST(T),FIRST(E),FIRST(F) ,均含 ϵ ,则把 ϵ 加入 FIRST(A)
构造
FOLLOW(A)
紧跟非终结符 A 后面的元素的集合
构造规则:
将 # 置于开始符号的 FOLLOW 集合中
E→TE′ ,将 FIRST(E′) 中的元素加入到 FOLLOW(T) 中
A→αBβ ,若 FIRST(β) 含有 ϵ ,则把 FOLLOW(A) 中的元素加入到 FOLLOW(B) 中
A→αB ,则把 FOLLOW(A) 中的元素加入到 FOLLOW(B) 中
FOLLOW 集合中永远不加 ϵ
3.5 举例
4. 递归下降分析程序构造
前提:必须消除了文法的左递归性、克服了回溯
主要思路:
分析程序由一组递归过程构成,对每一语法变量(非终结符)构造一个相应的子程序,识别对应的语法单位
几个全局过程和变量
ADVANCE :把输入串指示器 IP 指向下一个输入符号,即读入一个单词符号
递归下降子程序
A→TE′∣BC∣ϵ
PROCEDURE A;
BEGIN
IF SYM in FIRST(TE') THEN
BEGIN T; E' END
ELSE IF SYM in FIRST(BC) THEN
BEGIN B; C END
ELSE IF SYM not in FOLLW(A) THEN
ERROR
END;
文法的另一种表示法和转换图
在元符号 → 和 ∣ 的基础上、扩充几个元语言符号:
用花括号 {α} 表示闭包运算 α∗
用 {α}0n 表示可任意重复 0 次至 n 次
用方括号 [α] 表示 {α}01 ,即表示 α 的出现可有可无(等价于 α∣ϵ )
E→T∣E+TT→F∣T∗FF→i∣(E) ⟹ E→T{+T}T→F{∗F}F→i∣(E)
用语法图来表示语言的文法
5. 预测分析程序
LL(1)分析法 ——— 假设要用非终结符 A 进行匹配,面临的输入符号为 a ,A 的所有产生式为 A→α1∣α2∣…αn
若 a∈FIRST(αi) ,则指派 αi 执行匹配任务
若 a 不属于任何一个候选符集,则:
若 ϵ 属于某个 FIRST(αi) 且 a∈FOLLOW(A) ,则让 A 与 ϵ 自动匹配
5.1 预测分析程序构成
分析开始时:
总控程序根据现行栈顶符号 X 和当前输入符号 a ,执行下列三种动作之一:
若 X=a=′#′,则宣布分析成功,停止分析
若 X=a=′#′ ,则把 X 从 STACK 栈顶逐出,让 a 指向下一个输入符号
若 X 是一个非终结符,则查看分析表 M
若 M[X,a] 中存放着关于 X 的一个产生式,把 X 逐出STACK栈顶,把产生式的右部符号串按反序一一推进STACK栈(若右部符号为 ϵ ,则意味不推进任何东西进栈)。在把产生式的右部符号推进栈的同时应做这个产生式相应的语义动作。
若 M[X,a] 中存放着关于出错标志,则调用出错程序 ERROR
5.2 预测分析程序的总控程序
FLAG := TRUE;
WHILE FLAG DO
BEGIN
把 STACK 栈顶符号上托出去并放在 X 中;
IF X∈VT THEN
IF X=a THEN 把下一个输入符读进 a
ELSE ERROR
ELSE IF X=′#′ THEN
IF X=a THEN FLAG := FALSE
ELSE ERROR
ELSE IF M[X,a]={X→X1,X2,…,Xk} THEN
把 Xk,Xk−1,…,X1 一一推进 STACK 栈
ELSE ERROE
END OF WHILE
STOP
END
5.3 举例
文法 G(E) , E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵF→(E)∣i
输入串为 i∗i+i ,预测分析表:
E→TE′
E→TE′
E′→+TE′
E′→ϵ
E′→ϵ
T→FT′
T→FT′
T′→ϵ
T′→∗FT′
T′→ϵ
T′→ϵ
F→(E)
过程:
E→TE′
T→FT′
T′→∗FT′
T′→ϵ
E′→+TE′
T→FT′
T′→ϵ
E′→ϵ
5.4 分析表
M[A,a] 的构造
前提:构造 FIRST(α) 和 FOLLOW(A)
构造分析表步骤:
对文法 G 的每个产生式 A→α 执行第 2 步和第 3 步
对每个终结符 a∈FIRST(α) ,把 A→α 加至 M[A,a] 中
若 ϵ∈FIRST(α) ,则对任何 b∈FOLLOW(A) 把 A→α 加至 M[A,b] 中
把所有无定义的 M[A,a] 标上出错标志
5.5 举例
文法 G(E) , E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵF→(E)∣i 集合
预测分析表:
E→TE′
E→TE′
E′→+TE′
E′→ϵ
E′→ϵ
T→FT′
T→FT′
T′→ϵ
T′→∗FT′
T′→ϵ
T′→ϵ
F→(E)
5.6 LL(1) 文法与二义性
若一个文法是左递归或者二义性的,那么,M 至少含有一个多重定义入口