我正在尝试在Treesitter语法中获得正确的运算符优先级。Treesitter是一个LR1解析器生成器。
我有一个直截了当的艺术语法,部分看起来像这样:
multiply_expression: $ => prec.left(2, seq(
$._expression,
'*',
$._expression,
)),
addition_expression: $ => prec.left(1, seq(
$._expression,
'+',
$._expression,
)),
这可以正常工作。multiply_expression
确实比addition_expression
有更高的优先权。
但是,当我添加中间规则时,优先级会发生变化:
_partial_multi: $ => seq(
$._expression,
'*',
),
multiply_expression: $ => prec.left(2, seq(
$._partial_multi,
$._expression,
)),
我将$.expression, '*'
迁移到了它自己的规则中。对我来说,这似乎是一个等价的语法,我希望不会有任何变化。但是,随着此更改,优先级不再正确。保持不变的addition_expression
似乎比multiply_expression
具有更高的优先级。
为什么引入额外的步骤会改变优先级?有没有这个问题的名称,或者我在哪里可以找到关于它的更多信息?在编写语法或解决优先级问题时,有没有需要遵循的规则或思考这个问题的方法?
发布于 2021-04-20 20:12:06
这里是你的完整语法,为了重现性:
module.exports = grammar({
name: 'github_example',
conflicts: $ => [],
rules: {
source_file: $ => $._expression,
_expression: $ => choice(
$.number,
$.multiply_expression,
$.addition_expression
),
number: $ => /\d+/,
_partial_multi: $ => seq(
$._expression,
'*',
),
multiply_expression: $ => prec.left(2, seq(
$._partial_multi,
$._expression,
)),
addition_expression: $ => prec.left(1, seq(
$._expression,
'+',
$._expression,
)),
}
});
您可以通过向_partial_multi
规则添加优先级并从multiply_expression
规则中删除左关联优先级来解决此问题:
_partial_multi: $ => prec(2, seq(
$._expression,
'*',
)),
multiply_expression: $ => seq(
$._partial_multi,
$._expression,
),
你在这里所做的是将乘法定义为优先级为2的右结合运算符,这就是你在语法中定义左或右结合的方式,这些语法不会将其公开为基元。你可以通过编写如下代码来实现左关联乘法:
_partial_multi: $ => prec(2, seq(
'*',
$._expression,
)),
multiply_expression: $ => seq(
$._expression,
$._partial_multi,
),
您实际上偶然发现了一件非常有趣的事情,那就是您不需要显式的语言结构来定义语法中的优先级和结合性!它们只是让语法更容易读写的“语法糖”。有关如何通过分解规则来指定优先级和关联性的更多信息,请参见this page。您可以看到,完全通过语法构造指定优先级和结合性是令人困惑的,而且除非您仔细考虑,否则几乎会向后阅读您所期望的内容!正如您还发现的,混合使用这两种方法(通过语言结构和语法结构指定优先级和结合性)可能会导致混淆行为。最好是坚持其中之一。
https://stackoverflow.com/questions/67138113
复制相似问题