Skip to content

第四章:语法分析 2

4.4 FIRSTFIRST 集和 FOLLOWFOLLOW 集的计算

4.4.1 文法符合 XXFIRSTFIRST

FIRST(X)FIRST(X) :所有可以从 XX 推导的所有 串首终结符 构成的集合。

如果 XεX \Rightarrow^* \varepsilon ,那么 εFIRST(X)\varepsilon \in FIRST(X)

例如:

G:  E  TEE+TEεT  FTTFTεF  (E)id\begin{aligned} G:\; & E \;\rightarrow TE' \\ & E' \rightarrow +TE' \mid \varepsilon \\ & T \;\rightarrow FT' \\ & T' \rightarrow *FT' \mid \varepsilon \\ & F \;\rightarrow (E) \mid \text{id} \end{aligned}

推导一下 FIRSTFIRST

G:  E  TEFIRST(E)={(,id}E+TEεFIRST(E)={+,ε}T  FTFIRST(T)={(,id}TFTεFIRST(T)={,ε}F  (E)idFIRST(F)={(,id}\begin{aligned} G:\; & E \;\rightarrow TE' & FIRST(E) &= \{(,\,\text{id}\} \\ & E' \rightarrow +TE' \mid \varepsilon & FIRST(E') &= \{+,\,\varepsilon\} \\ & T \;\rightarrow FT' & FIRST(T) &= \{(,\,\text{id}\} \\ & T' \rightarrow *FT' \mid \varepsilon & FIRST(T') &= \{*,\,\varepsilon\} \\ & F \;\rightarrow (E) \mid \text{id} & FIRST(F) &= \{(,\,\text{id}\} \end{aligned}

4.4.2 计算文法符合 XXFIRSTFIRST

【算法】


不断应用下列规则,直到没有新的终结符或 ε\varepsilon 可以被加入到任何 FIRSTFIRST 集合中为止。

  • 如果 XX 是一个终结符,那么 FIRST(X)={X}FIRST(X) = \{X\}
  • 如果 XX 是一个非终结符
    • 如果 XYiYkP(k1)X \rightarrow Y_i\cdots Y_k \in P\,(k \ge 1) ,那么如果对于某个 iiaaFIRST(Yi)FIRST(Y_i) 中且 ε\varepsilon 在所有的 FIRST(Y1),,FIRST(Yi1)FIRST(Y_1),\,\cdots,\,FIRST(Y_{i-1}) 中(即 Y1Yi1εY_1\cdots Y_{i-1} \Rightarrow^* \varepsilon ),就把 aa 加入到 FIRST(X)FIRST(X)
    • 如果对于所有的 j=1,2,,kj=1,\,2,\,\cdots,\,kε\varepsilonFIRST(Yj)FIRST(Y_j) 中,那么将 ε\varepsilon 加入到 FIRST(X)FIRST(X)
  • 如果 XεPX \rightarrow \varepsilon \in P ,那么将 ε\varepsilon 加入到 FIRST(X)FIRST(X)

4.4.3 计算串 X1X2XnX_1X_2\cdots X_nFIRSTFIRST

  • FIRST(X1X2Xn)FIRST(X_1X_2\cdots X_n) 加入 FIRST(X1)FIRST(X_1) 中所有的非 ε\varepsilon 符号
  • 如果 ε\varepsilonFIRST(X1)FIRST(X_1) 中,再加入 FIRST(X2)FIRST(X_2) 中所有非 ε\varepsilon 符号
  • 如果 ε\varepsilonFIRST(X1)FIRST(X_1)FIRST(X2)FIRST(X_2) 中,再加入 FIRST(X3)FIRST(X_3) 中的所有非 ε\varepsilon 符号,以此类推
  • 如果所有的 iiε\varepsilon 都在 FIRST(Xi)FIRST(X_i) 中,那么将 ε\varepsilon 加入到 FIRST(X1X2Xn)FIRST(X_1X_2\cdots X_n)

4.4.4 计算非终结符 AAFOLLOW(A)FOLLOW(A)

FOLLOW(A)FOLLOW(A) 可能在某个句型中紧跟在 AA 后边的终结符 aa 的集合。

FOLLOW(A)={aSαaβ,aVT,α,β(VTVN)}FOLLOW(A) = \{ a \mid S \Rightarrow^* \alpha a \beta,\,a\in V_T,\,\alpha,\,\beta \in (V_T \cup V_N)^* \}

如果 AA 是某个句型的最右符号,则将结束符 $\$ 添加到 FOLLOW(A)FOLLOW(A) 中。

例如:

G:  E  TEFIRST(E)={(,id}FOLLOW(E)={$,)}E+TEεFIRST(E)={+,ε}FOLLOW(E)={$,)}T  FTFIRST(T)={(,id}FOLLOW(T)={+,$,)}TFTεFIRST(T)={,ε}FOLLOW(T)={+,$,)}F  (E)idFIRST(F)={(,id}FOLLOW(F)={,+,$,)}\begin{aligned} G:\; & E \;\rightarrow TE' & FIRST(E) = \{(,\,\text{id}\} & FOLLOW(E) = \{\$,\,)\} \\ & E' \rightarrow +TE' \mid \varepsilon & FIRST(E') = \{+,\,\varepsilon\} & FOLLOW(E') = \{\$,\,)\} \\ & T \;\rightarrow FT' & FIRST(T) = \{(,\,\text{id}\} & FOLLOW(T) = \{+,\,\$,\,)\} \\ & T' \rightarrow *FT' \mid \varepsilon & FIRST(T') = \{*,\,\varepsilon\} & FOLLOW(T') = \{+,\,\$,\,)\} \\ & F \;\rightarrow (E) \mid \text{id} & FIRST(F) = \{(,\,\text{id}\} & FOLLOW(F) = \{*,\,+,\,\$,\,)\} \end{aligned}

【算法】


不断应用下列规则,直到没有新的终结符可以被加入到任何 FOLLOWFOLLOW 集合中为止

  • $\$ 放入 FOLLOW(S)FOLLOW(S) 中,其中 SS 是开始符号, $\$ 是输入右端的结束标记
  • 如果存在一个产生式 AαBβA \rightarrow \alpha B \beta ,那么 FIRST(β)FIRST(\beta) 中除 ε\varepsilon 之外的所有符号都在 FOLLOW(B)FOLLOW(B)
  • 如果存在一个产生式 AαBA \rightarrow \alpha B ,或存在产生式 AαBβA \rightarrow \alpha B \betaFIRST(β)FIRST(\beta) 包含 ε\varepsilon ,那么 FOLLOW(A)FOLLOW(A) 中的所有符号都在 FOLLOW(B)FOLLOW(B)

4.4.5 表达式文法各个产生式的 SELECTSELECT

XXFIRST(X)FIRST(X)FOLLOW(X)FOLLOW(X)
EE(,id(,\,\text{id}$,)\$,\,)
EE'+,ε+,\,\varepsilon$,)\$,\,)
TT(,id(,\,\text{id}+,),$+,\,),\,\$
TT',ε*,\,\varepsilon+,),$+,\,),\,\$
FF(,id(,\,\text{id},+,),$*,\,+,\,),\,\$
产生式SELECTSELECT
ETEE \rightarrow TE'SELECT(1)={(,id}SELECT(1) = \{ (,\,\text{id} \}
E+TEE' \rightarrow +TE'SELECT(2)={+}SELECT(2) = \{ + \}
EεE' \rightarrow \varepsilonSELECT(3)={$,)}SELECT(3) = \{ \$,\,) \}
TFTT \rightarrow FT'SELECT(4)={(,$}SELECT(4) = \{ (,\,\$ \}
TFTT' \rightarrow *FT'SELECT(5)={}SELECT(5) = \{ * \}
TεT' \rightarrow \varepsilonSELECT(6)={+,),$}SELECT(6) = \{ +,\,),\,\$ \}
F(E)F \rightarrow (E)SELECT(7)={(}SELECT(7) = \{ ( \}
FidF \rightarrow \text{id}SELECT(8)={id}SELECT(8) = \{ \text{id} \}

这是一个 LL(1)LL(1) 文法,可以根据产生式的 SELECTSELECT 集构造预测分析表。

将每个非终结符作为行,每个输入符号作为列,那么每个产生式就是其元素。

预测分析表如下:

非终结符 / 输入符号id\text{id}++*(())$  \$\;
EEETEE \rightarrow TE'ETEE \rightarrow TE'
EE'E+TEE' \rightarrow +TE'EεE' \rightarrow \varepsilonEεE' \rightarrow \varepsilon
TTTFTT \rightarrow FT'TFTT \rightarrow FT'
TT'TεT' \rightarrow \varepsilonTFTT' \rightarrow *FT'TεT' \rightarrow \varepsilonTεT' \rightarrow \varepsilon
FFFidF \rightarrow \text{id}F(E)F \rightarrow (E)

4.4.6 LL(1)LL(1) 文法的分析方法

  • 递归的预测分析法
  • 非递归的预测分析法

4.5 递归的预测分析法

4.5.1 递归的预测分析法定义

递归的预测分析法 是指在递归下降分析中,编写每一个非终结符对应的过程时,根据预测分析表进行产生式选择。

4.5.2 示例分析

1.  <PROGRAM>program <DECLIST>:<TYPE>:<STLIST> end2.  <DECLIST>id <DECLISTN>3.  <DECLISTN>,id <DECLISTN>4.  <DECLISTN>ε5.  <STLIST>s <STLISTN>6.  <STLISTN>; s <STLISTN>7.  <STLISTN>ε8.  <TYPE>real9.  <TYPE>int\begin{aligned} 1.\;& \text{<PROGRAM>} &&\rightarrow \text{program <DECLIST>:<TYPE>:<STLIST> end} \\ 2.\;& \text{<DECLIST>} &&\rightarrow \text{id <DECLISTN>} \\ 3.\;& \text{<DECLISTN>} &&\rightarrow \text{,\,id <DECLISTN>} \\ 4.\;& \text{<DECLISTN>} &&\rightarrow \varepsilon \\ 5.\;& \text{<STLIST>} &&\rightarrow \text{s <STLISTN>} \\ 6.\;& \text{<STLISTN>} &&\rightarrow \text{; s <STLISTN>} \\ 7.\;& \text{<STLISTN>} &&\rightarrow \varepsilon \\ 8.\;& \text{<TYPE>} &&\rightarrow \text{real} \\ 9.\;& \text{<TYPE>} &&\rightarrow \text{int} \end{aligned}

SELECT(4)={:}SELECT(7)={end}\begin{aligned} SELECT(4) &= \{ : \} \\ SELECT(7) &= \{\text{end}\} \end{aligned}

各个分析过程的伪代码如下:

pascal
program DESCENT;
begin
    GETNEXT(TOKEN);
    PROGRAM(TOKEN);
    GETNEXT(TOKEN);
    if TOKEN != '$' then ERROR;
end
pascal
procedure PROGRAM(TOKEN);
begin
    if TOKEN != 'program' then ERROR;
    GETNEXT(TOKEN);
    DECLIST(TOKEN);

    if TOKEN != ':' then ERROR;

    GETNEXT(TOKEN);
    TYPE(TOKEN);

    GETNEXT(TOKEN);
    if TOKEN != ';' then ERROR;

    GETNEXT(TOKEN);
    STLIST(TOKEN);

    if TOKEN != 'end' then ERROR;
end
pascal
procedure DECLIST(TOKEN);
begin
    if TOKEN != 'id' then ERROR;
    GETNEXT(TOKEN);
    DECLISTN(TOKEN);
end
pascal
procedure DECLISTN(TOKEN);
begin
    if TOKEN = ',' then
        begin
            GETNEXT(TOKEN);
            if TOKEN != 'id' then ERROR;

            GETNEXT(TOKEN);
            DECLISTN(TOKEN);
        end
    else if TOKEN != ':' then ERROR;
end
pascal
procedure STLIST(TOKEN);
begin
    if TOKEN != 's' then ERROR;
    GETNEXT(TOKEN);
    STLISTN(TOKEN);
end
pascal
procedure STLISTN(TOKEN);
begin
if TOKEN = ';' then
    begin
        GETNEXT(TOKEN);
        if TOKEN != 's' then ERROR;

        GETNEXT(TOKEN);
        STLISTN(TOKEN);
    end
    else if TOKEN != 'end' then ERROR;
end
pascal
procedure TYPE(TOKEN);
begin
    if TOKEN != 'real' or TOKEN != 'int'
        then ERROR;
end

4.6 非递归的预测分析法

4.6.1 下推自动机

非递归的预测分析法 不需要为每个非终结符编写递归下降过程,而是根据预测分析表构造一个自动机,也叫表驱动的预测分析。

这是一个 下推自动机(Push Down Automata, PDA),下推自动机比有穷自动机的识别能力更强,因为它的记忆功能更强。

例如:

L={anbnn1}L = \{a^nb^n \mid n \ge 1\}

有穷自动机不具备专门的存储器,有穷自动机不会知道语言 LLnn 的值。有穷自动机无法识别这样的语言。

因为 nn 可以趋于无穷,而有穷自动机只有有穷个状态,这就产生了矛盾,故有穷自动机不能识别语言 LL

非递归预测分析

例如下面的预测分析表:

非终结符 / 输入符号id\text{id}++*(())$  \$\;
EEETEE \rightarrow TE'ETEE \rightarrow TE'
EE'E+TEE' \rightarrow +TE'EεE' \rightarrow \varepsilonEεE' \rightarrow \varepsilon
TTTFTT \rightarrow FT'TFTT \rightarrow FT'
TT'TεT' \rightarrow \varepsilonTFTT' \rightarrow *FT'TεT' \rightarrow \varepsilonTεT' \rightarrow \varepsilon
FFFidF \rightarrow \text{id}F(E)F \rightarrow (E)

分析过程:

剩余输入输出
E$  E\$\;id+idid$  \text{id}+\text{id}*\text{id}\$\;
TE$  TE'\$\;id+idid$  \text{id}+\text{id}*\text{id}\$\;ETEE \rightarrow TE'
FTE$  FT'E'\$\;id+idid$  \text{id}+\text{id}*\text{id}\$\;TFTT \rightarrow FT'
idTE$  \text{id}T'E'\$\;id+idid$  \text{id}+\text{id}*\text{id}\$\;FidF \rightarrow \text{id}
TE$  T'E'\$\;+idid$  +\text{id}*\text{id}\$\;
E$  E'\$\;+idid$  +\text{id}*\text{id}\$\;TεT' \rightarrow \varepsilon
+TE$  +TE'\$\;+idid$  +\text{id}*\text{id}\$\;E+TEE' \rightarrow +TE'
TE$  TE'\$\;idid$  \text{id}*\text{id}\$\;
FTE$  FT'E'\$\;idid$  \text{id}*\text{id}\$\;TFTT \rightarrow FT'
idTE$  \text{id}T'E'\$\;idid$  \text{id}*\text{id}\$\;FidF \rightarrow \text{id}
TE$  T'E'\$\;id$  *\text{id}\$\;
FTE$  *FT'E'\$\;id$  *\text{id}\$\;TFTT' \rightarrow *FT'
FTE$  FT'E'\$\;id$  \text{id}\$\;
idTE$  \text{id}T'E'\$\;id$  \text{id}\$\;FidF \rightarrow \text{id}
TE$  T'E'\$\;$  \$\;
E$  E'\$\;$  \$\;TεT' \rightarrow \varepsilon
$  \$\;$  \$\;EεE' \rightarrow \varepsilon

4.6.2 表驱动的预测分析法

【输入】一个串 ww 和文法 GG 的分析表 MM

【输出】如果 wwL(G)L(G) 中,输出 ww 的最左推导,否则给出错误指示

【算法】最初,语法分析器的格局如下:输入缓冲区中是 w$w\$\,GG 的开始符号位于栈顶,其下面是 $\$\, 。下面的程序使用预测分析表 MM 生成了处理这个输入的预测分析过程

cpp
设置 ip 使他指向 w 的第一个符号,其中 ip 是输入指针;
X = 栈顶符号;
while (X != $) { /* 栈非空 */
    if (X == ip 所指向的符号 a)
        弹出栈,ip 向前移动一个位置;
    else if (X 是一个终结符号)
        throw ERROR;
    else if (M[X, a] 是一个报错条目)
        throw ERROR;
    else if (M[X, a] = X -> Y[1]Y[2]...Y[k] ) {
        输出产生式 X -> Y[1]Y[2]...Y[k];
        弹出栈顶符号;
        将 Y[k],Y[k+1]...Y[1] 入栈,其中 Y[1] 位于栈顶;
    }
    X = 栈顶符号;
}

4.6.3 递归的预测分析法 VS 非递归的预测分析法

-递归的预测分析法非递归的预测分析法
程序规模程序规模较大,不需要载入分析表主程序规模较小,需要载入分析表
直观性较好较差
效率较低分析时间大约正比于待分析程序的长度
自动生成较难较易

4.6.4 预测分析的实现步骤

步骤:

  1. 构造文法
  2. 改造文法:消除二义性、消除左递归、消除回溯
  3. 求每个变量的 FIRSTFIRST 集和 FOLLOWFOLLOW 集,从而求得每个候选式的 SELECTSELECT
  4. 检查是否为 LL(1)LL(1) 文法,若是,构造预测分析表
  5. 预测分析
    • 对于递归的预测分析,根据预测分析表为每一个非终结符编写一个过程
    • 对于非递归的预测分析实现表驱动的预测分析算法

4.7 预测分析中的错误处理

4.7.1 预测分析中的错误检测

两种情况下可以检测到错误:

  • 栈顶的终结符和当前输入符号不匹配
  • 栈顶非终结符与当前输入符号在预测分析表对应项中的信息为空

4.7.2 预测分析中的错误恢复

恐慌模式:

  • 忽略输入中的一些符号,直到输入中出现由设计者选定的 同步词法单元(Synchronizing Token)集合中的某个词法单元
    • 其效果依赖于同步集合的选取。集合的选取应该使得语法分析器能从实际遇到的错误中快速恢复
    • 例如可以把 FOLLOW(A)FOLLOW(A) 中的所有终结符放入非终结符 AA 的同步记号集合
  • 如果终结符在栈顶而不能匹配,一个简单的办法就是弹出此终结符

例如,预测分析表如下:

非终结符 / 输入符号id\text{id}++*(())$  \$\;
EEETEE \rightarrow TE'ETEE \rightarrow TE'SynchSynch
EE'E+TEE' \rightarrow +TE'EεE' \rightarrow \varepsilonEεE' \rightarrow \varepsilon
TTTFTT \rightarrow FT'SynchTFTT \rightarrow FT'SynchSynch
TT'TεT' \rightarrow \varepsilonTFTT' \rightarrow *FT'TεT' \rightarrow \varepsilonTεT' \rightarrow \varepsilon
FFFidF \rightarrow \text{id}SynchSynchF(E)F \rightarrow (E)SynchSynch

Synch 表示根据相应非终结符的 FOLLOWFOLLOW 集得到的同步词法单元。

XXFOLLOW(X)FOLLOW(X)
EE$,)\$,\,)
EE'$,)\$,\,)
TT+,),$  +,\,),\,\$\;
TT'+,),$  +,\,),\,\$\;
FF,+,),$  *,\,+,\,),\,\$\;

分析表的使用方法:

  • 如果 M[A,a]M[A,\,a] 是空,表示检测到错误,根据恐慌模式,忽略输入符号 aa
  • 如果 M[A,a]M[A,\,a]Synch,则弹出栈顶的非终结符 AA,试图继续分析后面的语法成分
  • 如果栈顶的终结符和输入符号不匹配,则弹出栈顶的终结符

分析过程:

剩余输入输出
E$  E\$\;+id+id$  +\text{id}*+\text{id}\$\;忽略 ++
E$  E\$\;id+id$  \text{id}*+\text{id} \$\;
TE$  TE'\$\;id+id$  \text{id}*+\text{id} \$\;
FTE$  FT'E'\$\;id+id$  \text{id}*+\text{id} \$\;
idTE$  \text{id}T'E'\$\;id+id$  \text{id}*+\text{id} \$\;
TE$  T'E'\$\;+id$  *+\text{id} \$\;
FTE$  *FT'E'\$\;+id$  *+\text{id} \$\;
FTE$  FT'E'\$\;+id$  +\text{id}\$\;报错
TE$  T'E'\$\;+id$  +\text{id} \$\;
E$  E'\$\;+id$  +\text{id} \$\;
+TE$  +TE'\$\;+id$  +\text{id} \$\;
TE$  TE'\$\;id$  \text{id} \$\;
FTE$  FT'E'\$\;id$  \text{id} \$\;
idTE$  \text{id}T'E'\$\;id$  \text{id} \$\;
TE$  T'E'\$\;$  \$\;
E$  E'\$\;$  \$\;
$  \$\;$  \$\;