C# 语法分析器(四)二义性文法
系列导航
- (一)语法分析介绍
- (二)LR(0) 语法分析
- (三)LALR 语法分析
- (四)二义性文法
- (五)错误恢复
- (六)构造语法分析器
二义性文法,指的是一个可以为某个句子生成多颗语法分析树。最常见的例子就是算式的例子:
$0.\ E' \to E$
$1.\ E \to E + E$
$2.\ E \to E * E$
$3.\ E \to id$
$4.\ E \to (E)$
它可以为 $id + id * id$ 生成两个最左推导:
$$\begin{aligned}
E & \Rightarrow E+E & \quad E & \Rightarrow E * E \\
& \Rightarrow id+E & & \Rightarrow E+E * E \\
& \Rightarrow id+E * E & & \Rightarrow id+E * E \\
& \Rightarrow id+id * E & & \Rightarrow id+id * E \\
& \Rightarrow id+id * id & & \Rightarrow id+id * id \\
\end{aligned}$$
二义性文法显然无法被 LR 语法分析器处理,那为什么要使用二义性文法?主要原因就是在表达式这样的语言构造中,二义性文法能够提供比任何等价的二义性文法更短、更自然的归约;同时,我们还可以通过一些消除二义性的规则,使得语言的归约在整体上是无二义性的,并构造出其 LR 语法分析器。
以上面的二义性文法为例,看一下二义性的来源,以下是 LR(0) 项集族:
$$\begin{aligned}
I_0:\quad & E' \to \cdot E & \quad I_3:\quad & E \to id \cdot & \quad I_6:\quad & E \to (E \cdot ) \\
& E \to \cdot E+E & & & & E \to E \cdot +E \\
& E \to \cdot E * E & \quad I_4:\quad & E \to E+ \cdot E & & E \to E \cdot * E \\
& E \to \cdot id & & E \to \cdot E+E & & \\
& E \to \cdot (E) & & E \to \cdot E * E & \quad I_7:\quad & E \to E+E \cdot \\
& & & E \to \cdot id & & E \to E \cdot +E \\
I_1:\quad & E' \to E \cdot & & E \to \cdot (E) & & E \to E \cdot *E \\
& E \to E \cdot +E & & & & \\
& E \to E \cdot * E & \quad I_5:\quad & E \to E * \cdot E & \quad I_8:\quad & E \to E * E \cdot \\
& & & E \to \cdot E+E & & E \to E \cdot +E \\
I_2:\quad & E \to ( \cdot E) & & E \to \cdot E * E & & E \to E \cdot * E \\
& E \to \cdot E+E & & E \to \cdot id & & \\
& E \to \cdot E * E & & E \to \cdot (E) & \quad I_9:\quad & E \to (E) \cdot \\
& E \to \cdot id & & & & \\
& E \to \cdot (E) & & & & \\
\end{aligned}$$
图 1 二义性算式文法的 LR(0) 项集族
再构造得到 LALR 语法分析表(包含冲突):
$$\begin{array}
{|c|cccccc|ccc|}
状态 & id & + & * & ( & ) & $ & E \\
0 & s3 & & & s2 & & & 1 \\
1 & & s4 & s5 & & & acc & \\
2 & s3 & & & s2 & & & 6 \\
3 & & r3 & r3 & & r3 & r3 & \\
4 & s3 & & & s2 & & & 7 \\
5 & s3 & & & s2 & & & 8 \\
6 & & s4 & s5 & & s9 & & \\
7 & & s4/r1 & s5/r1 & & r1 & r1 & \\
8 & & s4/r2 & s5/r2 & & r2 & r2 & \\
9 & & r4 & r4 & & r4 & r4 & \\
\end{array}$$
图 2 二义性算式文法的 LALR 语法分析表(包含冲突)
可以看到,由于 $+$ 和 $*$ 都在状态 7 和 8 的向前看符号中,因此 LALR 语法分析无法解决这里的冲突,不能确定这里应该移入还是归约。即使使用 LR(1),甚至更高的 LR(k) 都会产生这样的冲突。
消除二义性的一个实用方法就是优先级和结合性,如果我们设定 $ * $ 的优先级高于 $+$,那么在状态 7 需要选择“按照 $E \to E+E$ 归约”和“移入 $ * $” 时,就应当选择优先级更高的符号,即“移入 $ * $”。其它情况由于移入的符号优先级不高于归约的符号,且符号是左结合的,应当选择归约动作。
这里还有一个细节,就是为什么 $E \to E+E$ 的优先级是 $+$?这里使用一个简单的选择:产生式的优先级是它最右非终结符的优先级,或者也允许用户自行指定。
对于其它未指定的情况,可以选择使用通用策略:在遇到移入-归约冲突时优先选择移入,遇到归约-归约冲突时选择列在前面的产生式。或者可以报错要求开发者解决。
本系列相关代码都可以在这里找到。
作者:CYJB
出处:http://www.cnblogs.com/cyjb/
GitHub:https://github.com/CYJB/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。