如何使用Bison正确地将优先级规则应用于表达式?

时间:2018-09-21 17:53:24

标签: bison

野牛新手在这里。

我正在尝试向玩具计算器添加功能,这是可以在任何变量之前省略乘法运算符*的可能性,以便它可以解析诸如3x * 2y之类的内容。 / p>

我将程序简化为一个非常简单的解析器,仅支持乘法(显式和省略),其中数字由字符n表示,变量由字符{{1}表示}。

下面是一个输入示例,与上面的示例类似,应该接受:

v

更复杂的示例也应该起作用(请忽略空格):

nv * nv

这是代码文件nvv * n * nvvv

calc.y

“编译”为:

%start  expression
%left '*'

%%
expression: 
    'n'
|   'v'
|   expression 'v' %prec '*'
|   expression '*' expression
;
%%

,我收到以下警告:

bison calc.y

从我对此的有限了解来看,似乎calc.y: warning: 1 shift/reduce conflict [-Wconflicts-sr] 没事。我如何告诉Bison按照它们出现的顺序匹配任一规则?谢谢!

2 个答案:

答案 0 :(得分:1)

几乎可以肯定,最好不要使用优先级来消除这种结构的歧义。可以使它起作用,但比其他方法更难理解(至少在我看来。)以下是一些SO答案,这些答案探讨了为包含“不可见”运算符的表达式编写具有明确优先级的语法: How can I have an “implicit multiplication” rule with Bison?Is it possible to make this YACC grammar unambiguous? expr: … | expr expr

yacc /野牛优先级算法在bison manual中进行了描述,并在许多SO答案中进行了简要说明,其中一些引用了以下段落(最初来自here。)

  

回想一下,在产品和终端之间定义了优先级关系。它既不涉及两个终端,也不涉及两个产品(因此不能用于解决减少-减少冲突)。可以减少的生产优先级与超前的终端之间的比较确定是减少还是转移。为了符号上的方便,产品以终端的名称表示,通常是产品中唯一的终端;这对应于一个普通的用例,但有时会造成混淆。特别是,%prec声明仅用于为规则指定优先级声明中使用的名称,以这种方式考虑它可能比将其作为“显式”声明更好。

因此,您的优先级声明无济于事以解决

中的歧义
expression 'v' %prec '*'

因为相关的先行标记是'v',并且在优先级表中没有任何声明的顺序。

此外,您可能并不希望相邻乘法与显式乘法具有相同的优先级,因为2*3v似乎不是(2*3)v2*3*v被解析为) ,而是2*(3v)。并非所有人都同意这一点-这是相邻乘法存在问题的部分原因-有些人会说2/3v的意思是(2/3)*v。我们可以同意对此有所不同,但这是需要提防的。

无论如何,如果您确实想使用优先级关系进行此操作,则需要在优先级顺序中包括所有可能位于expression开头的标记。 (它们都应位于列表末尾的单个优先级中。)

this answer的末尾有关于SO邻接的SO答案列表;其中一些解决方案包括优先顺序排序的示例。在您的情况下,您有两种选择,具体取决于您相信显式和隐式乘法应该如何相互作用:

%left '*'
%left 'v' /* And any other token which could start an expression */

%left '*' 'v' /* Again, add other tokens which could start an expression */

由于在两种情况下'v'都具有优先级顺序,因此无需修改expression 'v'产生的默认优先级;默认优先级(v,因为它是生产环境中的最后一个终端)可以正常工作:

expression: 'n' | expression 'v' | expression '*' expression

但是,如果有朝一日您决定2(3+v)是有效的隐式乘法,那么您需要将其更改为:

expression: 'n' | '(' expression ')' | expression '*' expression
          | expression expression %prec 'v'

因为%prec没有默认优先级,现在需要expression expression声明。

您可以将其添加到优先顺序,在这种情况下,您可以从生产中删除%prec声明:

答案 1 :(得分:0)

如果我正确理解了@rici的答案,那么使用优先级来解决这个问题不是一个好主意,因为我们必须为'expression'之后的令牌设置优先级,在本例中该令牌只是'v ',但可能有很多(我们必须为所有这些设置)。

另一种选择是限制出现在“ v”之前的事物,这样一来就不会产生歧义(请注意,“ v”之前的事物如何不能包含显式乘法):

%start  expression
%left '*'

%%
term:
    'n'
|   'v'
|   term 'v'
;

expression: 
    term
|   expression '*' expression
;
%%