优先级/关联性实施



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

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

当谈到如何为"动作"部分分配关联性和优先级时,我有点卡住了,即还原的优先级和关联性应该是什么。

如果我们有生产

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

应该明确的是,action1应该与+action2应该与*相同。但总的来说,我们不能仅仅假设一个产品中的规则只有一个具有优先级的符号。玩具示例

E -> E + E - E { action }

其中-和+是一些四元运算符,具有一定的优先级和可结合性。该操作是否应与-关联,因为它位于最后一个E之前?

我知道如何在转换/减少之间做出选择的规则,这不是我所要求的。

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

这个优先级模型已经被证明是有用的,尽管它并非没有问题——见下文——但它可能是简单解析器生成器的最佳实现,即使只是因为它的怪癖至少有文档记录。

这种惯例延续了这样一种观点,即优先权是非终端(或"运算符"(的一个特征。如果您正在构建一个运算符优先级解析器,但它与LR(k(解析不对应,那么这是有效的。充其量,这是一个粗略的近似值,可能会产生很大的误导。

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

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

事实上,一些人已经提出了一种基于语法重写的替代优先级模型。(例如,参见Ali Afropeeh等人2013年的论文《运营商优先规则的安全规范》(。这个模型要精确得多,但部分由于这种精确性,它不适合用于其他目的,例如解决悬而未决的其他冲突。