跳至主要內容

LL语法分析

西风逍遥游大约 3 分钟

LL语法分析

父章 语法分析递归下降手写了预测分析:每个非终结符一个函数,靠 peek() 选分支。把「当前非终结符 + 当前向前看终结符 → 该用哪条产生式」做成一张表,运行时只查表、进栈出栈,就是表驱动的 LL(1) 分析。本节讲这张表怎么造。

构建LL语法分析表

LL(1) 表示:从左到右扫描输入(Left-to-right),最左推导(Leftmost derivation),向前看 1 个终结符即可唯一决定产生式。

FIRST 与 FOLLOW

对文法符号串 α\alpha

  • FIRST(α)\mathrm{FIRST}(\alpha):从 α\alpha 能推导出的串的开头终结符集合;若 αε\alpha \Rightarrow^* \varepsilon,则 ε\varepsilon 也记入 FIRST。
  • FOLLOW(A)\mathrm{FOLLOW}(A):在某个句型中,紧跟在非终结符 AA 右边可能出现的终结符集合;若 AA 可以出现在句型末尾,则把结束符 $\$ 放入 FOLLOW(A)\mathrm{FOLLOW}(A)

计算要点(反复直到不动):

FIRST

  1. 终结符 aaFIRST(a)={a}\mathrm{FIRST}(a)=\{a\}
  2. AεA\to \varepsilon,则 εFIRST(A)\varepsilon\in\mathrm{FIRST}(A)
  3. AY1Y2YkA\to Y_1 Y_2\cdots Y_k,把 FIRST(Y1)\mathrm{FIRST}(Y_1) 中非 ε\varepsilon 的符号加入 FIRST(A)\mathrm{FIRST}(A);若 Y1εY_1\Rightarrow^*\varepsilon,再并入 FIRST(Y2)\mathrm{FIRST}(Y_2),以此类推;若全部可空,则 εFIRST(A)\varepsilon\in\mathrm{FIRST}(A)

FOLLOW

  1. 开始符号 SS$\$\in\mathrm{FOLLOW}(S)$。
  2. 若有 AαBβA\to \alpha B\beta,则 FIRST(β){ε}\mathrm{FIRST}(\beta)\setminus\{\varepsilon\} 并入 FOLLOW(B)\mathrm{FOLLOW}(B)
  3. 若有 AαBA\to \alpha B,或 AαBβA\to \alpha B\betaβε\beta\Rightarrow^*\varepsilon,则 FOLLOW(A)\mathrm{FOLLOW}(A) 并入 FOLLOW(B)\mathrm{FOLLOW}(B)

填表规则

分析表 M[A,a]M[A,a](行:非终结符;列:终结符或 $\$):

  1. 对每条 AαA\to\alpha,对每个终结符 aFIRST(α)a\in\mathrm{FIRST}(\alpha),令 M[A,a]=AαM[A,a]=A\to\alpha
  2. εFIRST(α)\varepsilon\in\mathrm{FIRST}(\alpha),则对每个 bFOLLOW(A)b\in\mathrm{FOLLOW}(A),令 M[A,b]=AαM[A,b]=A\to\alpha
  3. 其余格子为报错。

若某格被两条产生式写入,则出现冲突,文法不是 LL(1)。常见原因:左递归、公共左因子。需先改写文法(消左递归、提取左公因子),再算 FIRST/FOLLOW 填表。父章的递归下降能直接写,正是因为那份四则运算文法(或改写后)满足 LL(1)。

例子:消去左递归后的表达式骨架

左递归的 EE+TTE\to E+T\mid T 不是 LL(1)。改写为:

E  → T E'
E' → + T E' | ε
T  → F T'
T' → * F T' | ε
F  → ( E ) | id

FIRST

符号FIRST
FF{(,id}\{(\,,\,\mathrm{id}\}
TT'{,ε}\{\ast\,,\,\varepsilon\}
TT{(,id}\{(\,,\,\mathrm{id}\}
EE'{+,ε}\{+\,,\,\varepsilon\}
EE{(,id}\{(\,,\,\mathrm{id}\}

FOLLOW

符号FOLLOW
EE{),$}\{)\,,\,\$\}
EE'{),$}\{)\,,\,\$\}
TT{+,),$}\{+\,,\,)\,,\,\$\}
TT'{+,),$}\{+\,,\,)\,,\,\$\}
FF{,+,),$}\{\ast\,,\,+\,,\,)\,,\,\$\}

(由 F(E)F\to(E))FOLLOW(E))\in\mathrm{FOLLOW}(E);由 ETEE\to T E' 等规则按上面三条传播。)

LL(1) 分析表(空格表示出错):

id\mathrm{id}+*()$\$
EEETEE\to T E'ETEE\to T E'
EE'E+TEE'\to + T E'EεE'\to\varepsilonEεE'\to\varepsilon
TTTFTT\to F T'TFTT\to F T'
TT'TεT'\to\varepsilonTFTT'\to * F T'TεT'\to\varepsilonTεT'\to\varepsilon
FFFidF\to\mathrm{id}F(E)F\to( E )

分析过程示意

输入 id + id,栈底为 $\$,开始时栈为 $ E\$\ E(顶在右):

栈(顶在右)输入动作
$ E\$\ Eid+id$\mathrm{id}\,+\,\mathrm{id}\,\$ETEE\to T E'
$ ET\$\ E'\, Tid+id$\mathrm{id}\,+\,\mathrm{id}\,\$TFTT\to F T'
$ ETF\$\ E'\, T'\, Fid+id$\mathrm{id}\,+\,\mathrm{id}\,\$FidF\to\mathrm{id}
$ ETid\$\ E'\, T'\, \mathrm{id}id+id$\mathrm{id}\,+\,\mathrm{id}\,\$匹配 id\mathrm{id}
$ ET\$\ E'\, T'+id$+\,\mathrm{id}\,\$TεT'\to\varepsilon
$ E\$\ E'+id$+\,\mathrm{id}\,\$E+TEE'\to + T E'
$ ET+\$\ E'\, T\, ++id$+\,\mathrm{id}\,\$匹配 +
继续至栈与输入皆为 $\$

与递归下降对照:查 M[E,id]M[E,\mathrm{id}] 相当于在 parseE 里看到 id/( 时调用 parseT 再处理 E';表只是把分支选择显式化,便于工具生成。

小结

概念作用
FIRST产生式「能以哪些终结符开头」
FOLLOW可空产生式「在哪些向前看符号下仍可选」
LL(1) 表M[A,a]M[A,a] 唯一决定 AA 的展开
冲突需消左递归 / 提左公因子,或改用 LR 族

下一篇:LR 语法分析(自底向上的分析表)。