语法分析
Parser 分为两类:
- 基于推导 —— 自顶向下
- 基于规约 —— 自底向上
自顶向下分析(Top-Down Parsing)
主要解决的问题:当一个产生式有多个可选项(Alternatives)时,应该选择哪一个
对文法的预处理:
- 提取最大公共左因子:
- 例如产生式 \(A \rightarrow \alpha\beta\)\ |\ \(\alpha\gamma\),其中 \(\alpha\) 就是最大公共左因子,可以将它拆分为:\(A \rightarrow \alpha B\),\(B \rightarrow \beta\ |\ \gamma\)(\(\alpha,\beta,\gamma \in (V_T \cup V_N)^*\))
- 消除左递归
- 左递归:形如 \(A \rightarrow A\alpha\ |\ \beta\)
- 原因:如果不消除左递归,那么对于上面的产生式,可以得到:\(A \Rightarrow \beta \alpha^n\) 和 \(A \Rightarrow \beta\),我们会发现,二者有公共左因子 \(\beta\)
- 左递归的几种形式
- 直接左递归(立即左递归):\(A \rightarrow A\alpha\ |\ \beta\)
- 方法:变换为如下产生式:\(A \rightarrow \beta R\),\(R \rightarrow \alpha R\ |\ \epsilon\)
- 间接左递归:\(P \rightarrow Aa\ |\ b\),\(A \rightarrow Pc\ |\ d\)
- 方法:规定优先级,非终结符只能使用自己或者更低优先级的非终结符表示
- 混合左递归:\(S \rightarrow Qc\ |\ c\ |\ Sd\),\(Q \rightarrow Rb\ |\ b\),\(R \rightarrow Sa\ |\ a\ |\ Qe\)
- 方法:画出依赖关系,按照 “从内到外,从左到右” 的顺序化简
- 直接左递归(立即左递归):\(A \rightarrow A\alpha\ |\ \beta\)
预测分析
基本步骤:
- 如果栈顶是非终结符,那么查表找到表项 PPT(A, a)(Predict Parse Table,预测分析表)。如果非空,将产生式的右部逆序入栈(注意不读取下一个 token!);否则,失败
- 如果栈顶是终结符,那么将其与输入的 token 匹配。如果能够匹配,则弹出栈顶终结符,并且读取下一个 token;否则,匹配失败(或进入错误处理)
构建预测分析表
构建预测分析表需要两个关键的函数:FIRST 和 FOLLOW
- FIRST
- 作用
- 有多个可选项时,使用哪个可选项
- 何时(读取到哪些 token 时)使用当前产生式
- 步骤(产生式 \(A \rightarrow \alpha_1 \alpha_2 \alpha_3 \cdots\))
- 如果 X 是一个终结符,那么 FIRST(X) =
- 如果 X 是一个非终结符,并且有产生式 \(X \rightarrow Y_1Y_2 \cdots Y_k\)(其中 \(k \ge 1\))。如果 \(\epsilon \notin FIRST(Y_1)\),那么 \(FIRST(X) = FIRST(Y_1)\);否则如果 \(\epsilon \notin FIRST(Y_2)\),\(FIRST(X) = (FIRST(Y_1) - \{\epsilon\}) \cup FIRST(Y_2)\),以此类推
- 如果有产生式 \(X \rightarrow \epsilon\),那么将 \(\epsilon\) 加入 FIRST(X)
- 作用
- FOLLOW
- 作用:处理 \(\epsilon\) - 产生式
- 步骤
- 将 $ 加入 FOLLOW(S),其中 $ 是输入右端的结束标记
- 如果有产生式 \(A \rightarrow \alpha B \beta\),那么 \(FIRST(\beta)\) 中除 \(\epsilon\) 之外的所有符号都在 FOLLOW(B) 中
- 如果有产生式 \(A \rightarrow \alpha B\),或者有产生式 \(A \rightarrow \alpha B \beta\) 并且 \(\epsilon \in FIRST(\beta)\),那么 FOLLOW(A) 中所有的符号都在 FOLLOW(B) 中
3.1 假设有产生式 \(S \stackrel{+}{\Longrightarrow} \eta A \theta \Rightarrow \eta \alpha B \beta \theta\),如果 \(\beta\) 为 \(\epsilon\),那么 \(FOLLOW(B) = FIRST(\theta) = FOLLOW(A)\)
注意,此处求解 FIRST 和 FOLLOW 都要遵循 “最小集合原则”。比如文法:
E -> T E'
E' -> + T E' | epsilon
T -> F T'
T' -> * F T' | epsilon
F -> ( E ) | id
FOLLOW(E') = FOLLOW(E) \(\cup\) FOLLOW(E'),按照集合的计算,FOLLOW(E) \(\subset\) FOLLOW(E'),但在这里,FOLLOW(E') = FOLLOW(E),即 FOLLOW(E') 是满足 FOLLOW(E) \(\subset\) FOLLOW(E') 的最小的集合,所以 FOLLOW(E') = FOLLOW(E) = { ), $}
更多资料
[1] 《Parsing Techniques》
[2] 《Parsing Techniques》读书笔记 - 目录