下面是一个Bison语法,说明了我的问题。我正在使用的实际语法更复杂。
%glr-parser
%%
s : e | p '=' s;
p : fp | p ',' fp;
fp : 'x';
e : te | e ';' te;
te : fe | te ',' fe;
fe : 'x';
输入的一些例子是:
x
x = x
x,x = x,x
x,x = x;x
x,x,x = x,x;x,x
x = x,x = x;x
我所追求的是'='左侧的x与右侧的x不同。但是,可能出现在'=' - 符号右侧的合法“表达式”集合大于左侧的那些(因为';')。
Bison打印消息(输入文件为test.y):
test.y: conflicts: 1 reduce/reduce.
必须解决这个问题。在C中,您有类似的情况。下面的程序通过gcc没有错误。
int main(void) {
int x;
int *px;
x;
*px;
*px = x = 1;
}
在这种情况下,'px'和'x'的处理方式会有所不同,具体取决于它们是出现在'=' - 符号的左侧还是右侧。
答案 0 :(得分:2)
您正在使用%glr-parser
,因此无需“修复”减少/减少冲突。 Bison只是告诉你有一个,所以你知道你的语法可能含糊不清,所以你可能需要用%dprec
或%merge
指令添加歧义解决方案。但在你的情况下,语法并不含糊,所以你不需要做任何事情。
冲突不是错误,它只是表明你的语法不是LALR(1)。
答案 1 :(得分:2)
语法中的reduce-reduce冲突来自上下文:
... = ... x ,
此时,解析器必须决定x
是fe
还是fp
,并且它无法知道一个符号前瞻。实际上,它无法通过任何有限的预测知道,在此点之后您可以有x ,
的任意数量的重复,而不会遇到=
,;
或输入的结尾,其中任何一个会揭示答案。
这与C问题不完全相同,可以使用单符号前瞻解决。然而,C示例是为什么SLR(1)语法不如LALR(1)语法强大的经典例证 - 它在龙书中用于此目的 - 并且类似问题的语法是一个例子。 LALR(1)和LR(1);它可以在野牛手册(here)中找到:
def: param_spec return_spec ',';
param_spec: type | name_list ':' type;
return_spec: type | name ':' type;
type: "id";
name: "id";
name_list: name | name ',' name_list;
(Bison手册解释了如何为LALR(1)语法解决此问题,尽管使用GLR语法总是有可能。)
在不使用GLR语法的情况下解决此类冲突的关键是避免强制解析器做出过早的决策。
例如,传统上在语法上区分左值和右值,并且一些语言继续这样做。但是,C和C ++没有;这在C ++中是一个非常强大的功能,因为它允许定义可以充当左值的函数。
在C中,我认为只是简化语法:C语法允许任何一元运算符的结果出现在赋值运算符的左侧,但是一元运算符实际上是左值的混合({ {1}},*v
)和rvalues(v[expr]
,sizeof v
)。语法可以区分两种一元运算符,但它无法解决实际限制,即只有可修改的左值可能出现在左侧赋值运算符。
C ++允许任意表达式出现在赋值运算符的左侧(尽管有些需要用括号括起来);因此,以下是完全合法的:
f(expr)
在您的情况下,您可以通过将(predicate(x) ? *some_pointer : some_variable) = 42;
替换为te
来语法上解决冲突,因为两个非终端都会生成相同的派生集。这可能不是一般的解决方案,除非在你的完整语法中确实左侧表达式是右侧表达式的严格子集。在一个完整的语法中,你最终可能会得到三种类型的表达式(仅左,右,常见),这可能使语法相当复杂,并且留下用于语义分析的分辨率可能会更容易(甚至,在C ++的情况下,非常有用)。