Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

歧义文法

复习

  • 上下文无关文法:产生式与推导
  • 表达式的优先级与结合性:约定运算顺序
  • 语法树:结构的树形表示

TL;DR

  • 歧义文法指同一串输入能得到多棵语法树
  • 歧义会让编译器“不知道该按哪种解释”
  • 常见成因是缺少优先级或结合性的约定
  • 消除歧义可以靠改写文法或补充规则

正文

  前面说 1 - 2 - 3 必须约定结合性。如果不约定,会发生什么?答案是:文法变成歧义的(ambiguous)。

一棵输入,多棵树

  歧义文法,指的是这样一类文法:存在某个输入串,它可以被推导出不止一棵语法树。

  回到 1 - 2 - 3。如果文法写成:

表达式 → 表达式 - 表达式
表达式 → 数字

  那么 1 - 2 - 3 既可以被推成 (1 - 2) - 3 的树,也可以被推成 1 - (2 - 3) 的树。两棵都“合法”,但含义不同、结果也不同

为什么危险

  对编译器来说,歧义是件麻烦事,因为它不知道该听哪一棵树

  语法树决定了语义:(1-2)-3 得 -4,1-(2-3) 得 2,完完全全是两个程序。如果编译器“随便选一棵”,那么同一段代码在不同编译器上可能算出不同结果,这显然不能接受。一个合格的文法,不该让人和机器都无所适从。

  顺带一提,还有个有名的歧义例子叫“悬空 else”:if A if B S1 else S2 里,那个 else 到底配哪个 if?不同配法会产生不同的语法树。

怎么消除歧义

  消除歧义,通常有两种思路:

  • 改写文法:把它改成无歧义的等价形式。比如给表达式分层,用层次体现优先级和结合性,1 - 2 - 3 就只剩一种树了
  • 补充约定:在文法之外,明确规定优先级、结合性等规则,让分析器遇到选择时按约定来

  前面讲优先级与结合性,本质上就是在消除表达式的歧义。可见这两个话题是紧密相连的。

  一句话总结:好的文法,应当让每个合法输入都只有唯一一棵语法树。 这也是设计程序语言时,必须认真对待的一环。

思考题 1

  为什么歧义文法对编译器是危险的?

思考题 2

  消除歧义,常见有哪些办法?

小结

知识点

  • 歧义文法指同一输入可得到多棵语法树
  • 歧义会导致语义不唯一
  • 常见成因是缺少优先级/结合性约定
  • 可通过改写文法或补充规则消除歧义

参考资料

  1. Wikipedia(zh):歧义文法:存在多棵语法树的文法
  2. Wikipedia(zh):悬空else问题:经典的语法歧义例子

思考题答案(仅供参考)

思考题 1

  因为歧义意味着同一段代码可以对应多棵语法树,而不同语法树往往语义不同、结果不同。编译器若无法唯一确定该用哪棵树,就可能产生与预期不符甚至跨平台不一致的行为。

思考题 2

  常见办法有两种:一是改写文法,把它变换成无歧义的等价形式,例如给表达式分层来体现优先级和结合性;二是补充文法之外的约定(如优先级、结合性规则),让分析器按约定做出唯一选择。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪