Precedence/associativity 实施
Precedence/associativity implmentation
我目前正在实现一个 LR(k) 解析器解释器,只是为了好玩。
我正在尝试实现优先级和结合性。
当谈到如何为 'action' 部分分配关联性和优先级时,我有点卡住了,即减少的优先级和关联性应该是什么。
如果我们有作品
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。这是一个有用的 hack,有时是有益的——正如您所说,它可以减少状态的数量和减少单位的频率——但需要谨慎对待。
的确,有人提出了一种基于语法重写的优先级替代模型。 (例如,参见 Ali Afroozeh 等人 2013 年的论文“Safe Specification of Operator Precedence Rules”)。这个模型要精确得多,但部分由于这种精确性,它不适合(误)用于其他目的,例如解决悬挂-else 冲突。
我目前正在实现一个 LR(k) 解析器解释器,只是为了好玩。
我正在尝试实现优先级和结合性。
当谈到如何为 'action' 部分分配关联性和优先级时,我有点卡住了,即减少的优先级和关联性应该是什么。
如果我们有作品
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。这是一个有用的 hack,有时是有益的——正如您所说,它可以减少状态的数量和减少单位的频率——但需要谨慎对待。
的确,有人提出了一种基于语法重写的优先级替代模型。 (例如,参见 Ali Afroozeh 等人 2013 年的论文“Safe Specification of Operator Precedence Rules”)。这个模型要精确得多,但部分由于这种精确性,它不适合(误)用于其他目的,例如解决悬挂-else 冲突。