优先级/关联性实现

问题描述

我目前正在实现一个 LR(k) 解析器解释器,只是为了好玩。

我正在尝试实现优先级和关联性。

当谈到如何为“动作”部分指定关联性和优先级时,我有点困惑,即减少的优先级和关联性应该是什么。

如果我们有作品

E -> 
  | E + E { action1 }
  | E * E { action2 } 
  | (E)   { action3 }
  | ID    { action4 }

应该清楚,action1 应该具有与 + 相同的关联性和优先级 和 action2 应该与 * 相同。但总的来说,我们不能仅仅假设产生式中的规则只有一个具有优先级的符号。一个玩具示例

E -> E + E - E { action }

其中 - 和 + 是一些任意运算符,具有一些优先级和结合性。动作是否应该与 - 相关联,因为它在最后一个 E 之前?

我知道如何在 shift/reduce 之间进行选择的规则,这不是我所要求的。

解决方法

由 yacc(以及许多派生类)实现的经典优先级算法使用每个产生式中的最后一个非终结符来定义其默认优先级。这并不总是产生式所需的优先级,因此解析器生成器通常还会为其用户提供一种机制,用于显式指定产生式的优先级。

这种优先级模型已被证明是有用的,虽然它并非没有问题——见下文——它可能是简单解析器生成器的最佳实现,因为它的怪癖至少被记录在案。

>

这个约定延续了优先级是非终结符(或“操作符”)的特征的想法。如果您正在构建运算符优先级解析器,则这是有效的,但它不对应于 LR(k) 解析。充其量只是一个粗略的近似值,可能会产生极大的误导。

如果底层语法确实是运算符优先语法——也就是说,没有产生式有两个连续的终结符,并且推算的优先关系是明确的——那么它可能是一个可接受的近似值,尽管值得注意的是,运算符优先关系是不可传递,因此它们通常不能总结为单调比较。但是 yacc 样式优先级的许多用途都超出了这个范围,甚至会导致严重的语法错误。

问题在于,将优先级建模为标记之间的简单传递比较会导致优先级声明被用来消除(从而隐藏)不相关的冲突。总之,在LR解析中使用优先级声明基本上是一种hack。这是一个有用的技巧,有时也是有益的——正如你所说,它可以减少状态数量和单位减少的频率——但需要谨慎对待。

确实,有人提出了一种基于语法重写的替代优先级模型。 (例如,参见 Ali Afroozeh 等人在 2013 年发表的论文“运算符优先规则的安全规范”)。这个模型要精确得多,但部分由于这种精确性,它不容易(误)用于其他目的,例如解决悬空冲突。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...