790 likes | 980 Views
第四章 自上而下语法分析. 主要内容 4.1 语法分析器的功能 4.2 自上而下语法分析 4.3 LL(1) 文法 4.4 递归下降分析器 4.5 预测分析法 4.6 LL(1) 分析中的错误处理. 4.1 语法分析器的功能. 语法分析是编译过程的核心部分 . 它的任务是在词法分析识别出单词符号串的基础上 , 分析并判定程序的语法结构是否符合语法规则 . 语法分析器在编译程序中的地位如图所示. 单词符号. 源程序. 语法分析树. 词法分析器. 语法分析器. 编译程序 后续部分. 取下一个 单词符号. 符号表.
E N D
第四章 自上而下语法分析 主要内容 • 4.1 语法分析器的功能 • 4.2 自上而下语法分析 • 4.3 LL(1)文法 • 4.4 递归下降分析器 • 4.5 预测分析法 • 4.6 LL(1)分析中的错误处理
4.1 语法分析器的功能 • 语法分析是编译过程的核心部分.它的任务是在词法分析识别出单词符号串的基础上,分析并判定程序的语法结构是否符合语法规则.语法分析器在编译程序中的地位如图所示. 单词符号 源程序 语法分析树 词法分析器 语法分析器 编译程序 后续部分 取下一个 单词符号 符号表
语法分析器的工作 • 语法分析问题就是句子的识别问题. • 按照识别句子时语法树的建立方法不同分为自上而下与自下而上两大类分析方法. • 自上而下语法分析就是从文法的识别符号出发,识别输入符号串是否为一个句子. • 自上而下分析法可能遇到的问题有回溯以及左递归带来的无限循环. • 带回溯的分析方法效率太低. • 不带回溯的分析方法要求文法没有左递归并能避免回溯,有一类被称为LL(1)文法可用递归子程序方法或预测分析法实现自上而下的语法分析.
关于识别的符号串 • 词法分析和语法分析都是对输入符号串的识别,但词法分析的输入符号串是一个单词,而语法分析的输入符号串是一个句子或者说是一个程序.我们用小写字母表示终结符号,在词法分析中,小写字母表示组成单词的字符;而在语法分析中,小写字母表示组成程序的单词.从识别方式来说,词法分析和语法分析都是对输入符号串的结构的识别,但由于单词和程序的结构是不一样的,所以具体的识别方法不一样.
4.2 自上而下语法分析 • 4.2.1.基本思想 从识别符号出发,不断建立直接推导,试图构造一个推导序列,最终由它推导出与输入符号串相同的符号串. 从语法树的角度看,自上而下分析过程将以识别符号为根结点,试图向下构造一棵语法树,其末端节点符号串正好与输入符号串相同.
4.2.2. 遇到的问题 • 1.左递归 当文法中出现左递归时,会使分析过程陷入无限循环.左递归的形式为: 存在非终结符号U, 对于它有 U->U… 或U =>U… • 2.回溯 如果对于同一个非终结符号,存在若干个产生式右部, U->x1|x2|…|xn 时,要对U展开,那么按哪一个产生式右部展开? 即:如何确定替换U的xi +
例 : 对文法 • S → c A d • A → a b |a 输入 w = cad 的分析 这时意味着A的第一个此刻不适用于构造W的语法树,此时应该回头(回溯),看A是否还有别的候选.
3.在上述的自上而下分析过程中,当一个非终结符用某一候选匹配成功时,这种成功可能仅是暂时的.3.在上述的自上而下分析过程中,当一个非终结符用某一候选匹配成功时,这种成功可能仅是暂时的. 例如,就文法(4.1)而言,考虑输入串x**y,如果首先匹配*,就可能匹配不成功,如首先匹配**,就成功,这意味着,A首先使用第二个候选所得的不成功匹配是虚假的.由于这种虚假现象,我们需要更复杂的回溯技术.一般说,要消除虚假匹配是很困难的.但若从最长的候选开始匹配,虚假匹配的现象就会减少一些. • 4.当最终报告分析不成功时,我们难于知道输入串中出错的确切位置. • 5.由于带回溯的自上而下分析实际上采用了一种穷举一切可能的试探法,因此,效率很低,代价极高.严重的低效使得这种分析法只有理论意义,而在实践上价值不大. • 后面,将集中讨论不带回溯的自上而下的分析法.
4.3.LL(1)分析法 • 4.3.1.消除左递归 • 对于某些 α,存在推导 P => P α • 引入新的非终结符 P’ • 将 P → Pα|β 替换为 • P→βP' 和 P'→αP'|ε +
E → E + T|T T → T * F|F F → ( E )|id 例 :表达式文法直接左递归的消除 E → T E' E' → + T E'|ε T → F T' T' → * F T'|ε F → ( E )|id
消除文法中所有左递归的方法 • (1).将间接左递归改为直接左递归.将文法中所有如下形式的产生式: Pj→Pjγ|β1|β2 |…|βn Pj→δ1|δ2|…|δk 改写成:Pj→δ1γ|δ2γ|…|δkγ |β1|β2 |…|βn 则可消除文法中的间接左递归. • (2).消除直接左递归.将文法中所有如下形式的产生式: P→Pα1| Pα2|…|Pαm|β1|β2|…|βn 其中每个α都不等于ε,而每个β都不以P开头,那么,消除P的直接左递归性就是把这些规则改写成: P→β1P'|β2P'|…|βnP' P'→α1P'|α2P'|…|αmP'|ε 把直接左递归都改成直接右递归.
例:消除下面文法的左递归 S → Qc|c Q → Rb|b R → Sa|a 令它的非终结符的排序是:R,Q,S 对R,不存在直接左递归,将 R 代入Q, Q → Sab|ab|b 同样不含Q直接左递归,将 Q 代入S, S → Sabc|abc|bc|c
消除S的直接左递归,得文法: S → abcS'|bcS'|cS' S' → abcS'|ε Q → Sab|ab|b R → Sa|a 显然,Q和R的规则已是多余,化简后得文法: S → abcS'|bcS'|cS' S' → abcS'|ε
由于对非终结符排序的不同,最后所得的文法在形式上可能不一样,但它们都是等价的. 如上例中若非终结符的排序为:S,Q,R, 那么最后所得的无左递归文法为: S → Qc|c Q → Rb|b R → bcaR'|caR'|aR' R' → bcaR'|ε
例: 设有文法G[S]: S → Qd Q → Rb|Se R → Sa|Qf|a 试消除其左递归. 解:令它的非终结符的排序是S,Q,R; 将S代入Q: Q → Rb|Qde 消除Q的左递归, 得到: Q → RbQ’ Q’ →deQ’|ε 将S代入R, R → Qda|Qf|a, 由于Q → RbQ’ ,得: R → RbQ’ da| RbQ’ f|a, 消除R的左递归: R →aR’ R’ → bQ’ da R’|bQ’ fR’| ε 所以,文法消除左递归后变为: S → Qd Q → RbQ’ Q’ →deQ’|ε R →aR’ R’ → bQ’ da R’|bQ’ fR’|a
例: 消除下列文法的左递归: (1).G[A] : A → Bx|Cz|w B → Ab|Bc C → Ax|By|Cp 解: 因为B → Ab, A → Bx|Cz|w 所以将B改写为:B → Bxb|Czb|wb|Bc 消除产生式B的左递归: B → CzbB’|wbB’ B’ → xbB’|cB’|ε 因为C → Ax, A → Bx|Cz|w 所以将C改写为:C → Bxx|Czx|wx|By|Cp 因为C → Bxx|By, B → CzbB’|wbB’ 所以将C改写为: C → CzbB’ xx| CzbB’ y|wbB’ xx|wbB’ y|Czx|wx|Cp 消除产生式C的左递归: C → wbB’ xxC’|wbB’ yC’|wxC’ C’ → zbB’ xxC’| zbB’ yC’|zxC’|pC’|ε
结果: 所以, 文法消除后的左递归为: A → Bx|Cz|w B → CzbB’|wbB’ B’ → xbB’|cB’|ε C → wbB’ xxC’|wbB’ yC’|wxC’ C’ → zbB’ xxC’| zbB’ yC’|zxC’|pC’|ε
4.3.2 消除回溯,提左因子 • 1.消除回溯 令G是一个不含左递归的文法,对G的所有非终结符号的每个候选α定义它的终结首符集FIRST(α)为: FIRST(α)={a|α=*>a… , a ∈VT} 特别是,若α=*>ε,则规定 ε ∈FIRST(α) 即: FIRST(α)是α的所有可能推导的开头终结符或可能的ε. 若A的任何两个不同候选αi和αj FIRST(αi ) ∩ FIRST(αj ) = φ 则当要求A匹配输入串时,A就根据它所面临的第一个输入符号a,准确地指派某一个候选前去执行任务.
2.左因子提取方法 • 对于所有形如 A→ δβ1|δβ2|...|δβn|γ1 |γ2 |...|γm的规则 (其中每个γ不以δ开头) • 改写为 A → δA'| γ1 |γ2 |...|γm 和 A'→β1|β2|...|βn • 经过反复提取左因子,就能够把每个非终结符(包括新引进者)的所有候选首符集变成为两两不相交。为此付出的代价是,大量引进新的非终结符和ε-产生式。
例 :分析符号串 i + i * i符合表达式文法: E →E+T|T T →T*F|F F →(E)|i • E → T E' • E'→ + T E'|ε • T → F T' • T'→ * F T'|ε • F → ( E )|i 按照最左推导过程,构造分析树
E → T E' E'→ + T E'|ε T → F T' T'→ * F T'|ε F → ( E )|i 句子i+i*i E 推导过程(P72) i ∈ FIRST(TE’) T E’ F T’ + T E’ i∈FIRST(FT’) ε F T’ ε i T’ →ε * F T’ i 生成语法分析树 ε i
α 4.2.5 FIRST 和 FOLLOW 集 • 令G是一个不含左递归的文法 • 定义:对G的所有非终结符的每个候选α定 义它的终结首符集FIRST(α)为 • FIRST( α ) = { a |α ==*> a …,a∈VT* } • α的所有首符号(选择产生式的依据) • 定义: FOLLOW( A )是所有句型中出现在紧接 A之后的终结符或‘#’ • FOLLOW( A ) = { a | S ==*> …Aa…,a∈VT } • A 的后续符号 a … … S … A a ...
定义 令G[S]=(VT,VN,S,P),则 FIRST(α)={ a| α*aβ,a∈VT , α、β∈V*}、 若α*ε,则ε∈FIRST(α)FIRST( X ) 的计算法 对所有语法符号 X,重复进行以下计算 • 1) 若 X ∈VT,则 FIRST( X ) = X 。 • 2) 若 X ∈VN, • 有产生式 X → a…,则将 a 加入FIRST(X); • 有产生式 X →ε,则将ε加入FIRST( X )。
3) 若有产生式 X → Y…,且 Y ∈VN, • 则把FIRST(Y)中的所有非ε-元素都加到FIRST(X)中; • 若有产生式 X → Y1…Yk , Y1 ... Yi-1都是非终结符,而且,对于任何j,1<=j<=i-1, FIRST( Yj )都含有ε(即Y1 ... Yi-1==>ε ),则把FIRST( Yi )中的所有非ε-元素都加到FIRST(X)中;特别是,若所有的FIRST( Yj)均含有ε,j=1,2,…,k,则把ε加到FIRST(X)中。 • 反复使用(1)-(3),直到FIRST(X)不再增大为止。 *
求解FIRST集合的算法 • 对每一文法符号X∈V 计算FIRST(X)(a) 若X∈VT,则FIRST(X)={X}。(b) 若X∈VN,且有产生式X→a…,a∈VT,则a∈FIRST(X)。(c) 若X∈VN,X→ε,则ε∈FIRST(X)。(d) 若X∈VN;Y1,Y2,…,Yi∈VN,且有产生式X→Y1 Y2 … Yn;当Y1 Y2 … Yi-1都ε时,(其中1≤i≤n),则FIRST(Y1)、FIRST(Y2)、…、FIRST(Yi-1)的所有非{ε}元素和FIRST(Yi)都包含在FIRST(X)中。(e) 当(d)中所有Yiε,(i=1,2,…n),则FIRST(X)=FIRST(Y1)∪FIRST(Y2)∪…∪FIRST(Yn)∪{ε}反复使用上述(b)~(e)步直到每个符号的FIRST集合不再增大为止。
求一个符号串的FIRST集合若符号串α∈V*,α=X1 X2 … Xn,当X1不能 ε,则置FIRST(α)= FIRST(X1)。若对任何j(1≤j≤i-1,2≤i≤n), ε∈FIRST(Xj) , 则 • FIRST(α)= (FIRST(Xj)-{ε}) ∪FIRST(Xi) • 当所有FIRST(Xj)(1≤j≤n)都含有{ε}时,则FIRST(α)= (FIRST(Xj))∪{ε}
文法G[S]为:S→AB| bC A→ b |ε B→ aD |εC→AD| b D→aS| c • 可产生空串的非终结符:A、B、S
例 :表达式文法的 FIRST 集 • E → T E' • E'→ + T E'|ε • T → F T' • T'→ * F T'|ε • F → ( E )|id 可产生空串的非终结符:E’、T’ FIRST( F ) = { ‘(‘, id } FIRST( T ) = FIRST( F )-{ε}= { ‘(‘, id} FIRST( E ) = FIRST( T )-{ε}= { ‘(‘, id} FIRST( E' ) = { ‘+’,ε} FIRST( T' ) = { ‘*’, ε}
例:求下列文法的FIRST和FOLLOW集合 • 1)S →ABc A →a|ε B →b|ε 可产生空串的非终结符: A、 B FIRST(A)={a,ε} FIRST(B)={b,ε} FIRST(S)={FIRST(A)-{ε}}∪{FIRST(B)-{ε}} ∪FIRST(C) ={a,b,c} FOLLOW(S)={#} FOLLOW(A)=FIRST(Bc)={FIRST(B)-{ε}}∪{c} ={b,c} FOLLOW(B)={c}
例如: • S→ABBA • A →a|ε • B →b|ε FIRST(A)={a,ε} FIRST(B)={b,ε} FIRST(S)={FIRST(A)-{ε}}∪{FIRST(B)-{ε}} ∪{ε} ={a,b,ε} FOLLOW(S)={#} FOLLOW(A)=FIRST(BBA)={FIRST(B)-{ε}}∪{c} ={b,c} FOLLOW(B)={c}
FOLLOW( A ) 的计算法 计算 (从右向左看齐) 对于所有非终结符, 重复进行以下计算 • 1) 设S是开始符号,则 # ∈ FOLLOW( S ) • 句子的结束符 • 2) 若 A → αBβ, • 则将 FIRST(β) 的非ε元素加入 FOLLOW(B) • 3) 如果 A → αB 或 A → αBβ,且 β==>ε(即ε∈FIRST(β)),则将 FOLLOW(A) 的所有元素加入 FOLLOW(B) • 反复使用(1)-(3)直到FOLLOW集不再增大为止。
例:文法G[S],其产生式如下: • S→AB|bC A→ε|b B→ε|aD C→AD|b D→a|c • 可产生空串的非终结符:A、B、S
例:表达式文法的 FOLLOW 集 • E → T E' • E'→ + T E'|ε • T → F T' • T'→ * F T'|ε • F → ( E )|id 可产生空串的非终结符:E’、T’ FOLLOW( E ) = { ‘)’, # } 由F → ( E ),则FIRST(‘)’)=FOLLOW(E) ,规则1 FOLLOW( E‘ ) = FOLLOW( E ) = { ‘)’, # } 规则3 FOLLOW( T ) = { +, ‘)’, # } FIRST(E’), FOLLOW(E’) FOLLOW( T' ) = FOLLOW( T ) = { +, ‘)’, # } 规则3 FOLLOW( F ) = { *, +, ‘)’, # } FIRST(T’), FOLLOW(T’), FOLLOW(T)
例: • 设有以下文法G[S]: • S → aAbDe|d • A → BSD|e • B → SAc|cD|ε • D → Se|ε 求出该文法的每一个非终结符U的FOLLOW集.
S → aAbDe|d A → BSD|e B → SAc|cD|ε D → Se|ε 解: • 可产生空串的非终结符:B、D • (1)因为S是识别符号,且有A→BSD,B→SAc,D→Se,所以FOLLOW(S)应包含 {FIRST(D)-{ε}}∪{FIRST(Ac)-{ε}}∪{FIRST(e)-{ε}} ∪{#}={a,d}∪{a,d,c,e}∪{e}∪{#} = {a,c,d,e,#} • (2)又因A→BSD和D →ε,所以FOLLOW(S)中还包含有FOLLOW(A).因为S → aAbDe和B → SAc,所以: FOLLOW(A)=FIRST(bDe)∪FIRST(c) = {b,c} • 综合(1),(2)得FOLLOW(S)= {a,c,d,e,#}∪{b,c} = {a,b,c,d,e,#} • (3)因为A→BSD, 所以FOLLOW(B)=FIRST(SD)={a,d} • (4)因为S→aAbDe|d, A→BSD|e 和B→SAc|cD,所以: FOLLOW(D)=FIRST(e)∪FOLLOW(A)∪FOLLOW(B) ={e}∪{b,c}∪{a,d}={a,b,c,d,e}
4.3 LL( 1 ) 文法 • 第一个 L 表示从左向右扫描输入符号串 • 第二个 L 表示生成最左推导 • 1 表示读入一个符号可确定下一步推导 • 表示了自上而下方法能够处理的文法
文法G是 LL(1) 的充要条件: • 对于 G 的每个非终结符 A 的 • 任何两个不同的产生式 A → α|β • 1) FIRST( α ) ∩ FIRST( β ) = φ • 2) 如果 β ==*> ε,则 FISRT( α ) ∩ FOLLOW( A ) = φ
对于一个LL(1)文法,可以对其输入串进行有效的无回溯的自上而下分析。假设要用非终结符A进行匹配,面临的输入符号为a,A的所有产生式为:对于一个LL(1)文法,可以对其输入串进行有效的无回溯的自上而下分析。假设要用非终结符A进行匹配,面临的输入符号为a,A的所有产生式为: A →a1|a2|a3|…|an 1)若a ∈FIRST(ai),则指派ai去执行匹配任务。 2)若a不属于任何一个候选首符集,则: a.若ε属于某个FIRST(ai)且a∈FOLLOW(A),则让A与ε自动匹配。 b.否则,a的出现是一种语法错误。 根据LL(1)文法的条件,每一步这样的工作都是确信无疑的。
表达式文法是 LL(1) 文法 • E → T E' • E'→ + T E'|ε • T → F T' • T'→ * F T'|ε • F → ( E )|id 考察 • E’: + 不在 FOLLOW( E’ ) = { ), # } • T’: * 不在 FOLLOW( T’ ) = { +, ), # } • F : ( 和 id 不同
例: • 以下文法G[S]是否是LL(1)文法? • S → aAbDe|d • A → BSD|e • B → SAc|cD|ε • D → Se|ε • 解: G[S] 不是LL(1)文法. 因为产生式 B → SAc|cD|ε 中, FIRST(SAc)∩FOLLOW(B)={a,d}<>φ
4.4 递归下降分析程序构造 • 当一个文法满足LL(1)条件时,可以为它构造一个不带回溯的自上而下分析程序,这个分析程序是由一组递归过程组成的,每个过程对应文法的一个非终结符.这样的一个分析程序称为递归下降分析器.
E → T E' E'→ + T E'|ε T → F T' T'→ * F T'|ε F → ( E )|id 4.4.1 例 :递归子程序 ADVANCE: 下一个符号. SYM: 当前符号.ERROR:错误处理程序 • F的子程序 procedure F; IF SYM=‘id' THEN ADVANCE ELSE IF SYM=‘(' THEN begin ADVANCE; E; end IF SYM=‘)' THEN ADVANCE ELSE ERROR ELSE ERROR END • T的子程序 procedure T; begin F;T’ end procedure T’; IF SYM=‘*' THEN begin ADVANCE F;T’ end • E的子程序 procedure E; begin T;E’ end procedure E’; IF SYM='+' THEN begin ADVANCE; T;E’ end
4.4.2 扩充的巴科斯范式 (1)用花括号{α}表示闭包运算α* . (2)用{α}0 表示α可重复0次至n次, {α}0 =α0 = ε (3)用[α]表示{α}0 ,即表示α的出现可有可无(等价于α| ε). 例:通常的实数可定义为: decimal →[sign]integer.{digit}[exponent] exponent→E[sign]integer integer→ digit{digit} sign → +|- n 0 1
4.4.3 例: 文法: • E → T | E+T • T → F | T*F • F → id |( E ) 可表示成: E → T { +T } T → F { *F } F → id |( E ) 推导过程: E → T E' E'→ + T E'|ε T → F T' T'→ * F T'|ε F → ( E )|id
E → T { +T } T → F { *F } F → id |( E ) 语法图表示为: E T T + T F F * F id ( E )
E → T { +T } T → F { *F } F → id |( E ) 化简的递归子程序 ADVANCE: 下一个符号. SYM: 当前符号.ERROR:错误处理程序 • F的子程序 procedure F; IF SYM=‘id' THEN ADVANCE ELSE IF SYM=‘(' THEN begin ADVANCE; E; end IF SYM=‘)' THEN ADVANCE ELSE ERROR ELSE ERROR END • T的子程序 procedure T; begin F; WHILE SYM=‘*' DO begin ADVANCE; F end end • E的子程序 procedure E; begin T; WHILE SYM='+' DO begin ADVANCE; T end end
例: 消除下列文法的左递归: (2).G[E] : E→ ET+|ET-|T T → TF*|TF/ |F F → (E)|i 解: 消除产生式E的左递归: E → T{(T+|T-)} 消除产生式T的左递归: T → F{(F*|F/)} 所以,文法消除左递归后变为: E → T{(T+|T-)} T → F{(F*|F/)} F → (E)|i
4.5 预测分析程序 实现LL(1)分析的另一种方法是使用一张分析表和一个栈进行联合控制.如图为预测分析器模型: 输入缓冲区 栈 输出 总控程序 预测分析表 二维数组[非终结符,终结符a ] 表项用于指示产生式或出错标志
4.5.1 预测分析程序工作过程 • 1.预测分析表 • 预测分析表是一个M[A,a]形式的矩阵.其中,A为非终结符,a是终结符或# (#表示输入串的结束符),矩阵元素M[A,a]中存放着一条关于A的产生式,指出当A面临输入符号a时所应采取的候选. M[A,a]中也可能存放着一个”出错标志”,指出A根本不该面临输入符号a.