消除二进制操作的移位/减少错误

时间:2011-11-23 17:31:09

标签: parsing f# grammar fsyacc

fsyacc正在为所有二进制操作发出shift / reduce错误。

我有这个递归制作:

scalar_expr:
    | scalar_expr binary_op scalar_expr { Binary($2, $1, $3) }

将其更改为

scalar_expr:
    | constant binary_op constant { Binary($2, Constant($1), Constant($3)) }

消除了错误(但不是我想要的)。优先级和关联性定义如下:

%left BITAND BITOR BITXOR
%left ADD SUB
%left MUL DIV MOD

以下是列表文件的摘录,显示产生错误的状态(另一个状态具有相同的错误)。

state 42:
  items:
    scalar_expr -> scalar_expr . binary_op scalar_expr
    scalar_expr -> scalar_expr binary_op scalar_expr . 

  actions:
    action 'EOF' (noprec):   reduce scalar_expr --> scalar_expr binary_op scalar_expr
    action 'MUL' (explicit left 9999):   shift 8
    action 'DIV' (explicit left 9999):   shift 9
    action 'MOD' (explicit left 9999):   shift 10
    action 'ADD' (explicit left 9998):   shift 6
    action 'SUB' (explicit left 9998):   shift 7
    action 'BITAND' (explicit left 9997):   shift 11
    action 'BITOR' (explicit left 9997):   shift 12
    action 'BITXOR' (explicit left 9997):   shift 13

我认为你可以看到解析器在所有情况下都会发生变化,这是正确的。我至少没有发现行为不正确的情况。

如何重述语法以消除这些错误?

2 个答案:

答案 0 :(得分:1)

binary_op实际上是一个制作,即你有类似的东西:

binary_op:
   | ADD { OpDU.Add }
   | SUB { OpDU.Sub }
   ...

如果是这样,我认为这就是问题所在,因为我认为你定义的优先规则不会在constant binary_op constant中得到尊重。您需要明确枚举每个scalar_expr模式,例如

scalar_expr:
    | scalar_expr ADD scalar_expr { Binary(OpDU.Add, $1, $3) }
    | scalar_expr SUB scalar_expr { Binary(OpDU.Sub, $1, $3) }
    ...

(我认为没有办法用FsYacc抽象出这种重复性)

答案 1 :(得分:1)

斯蒂芬在他的回答中指出,如果你将它们转移到一个单独的生产中,你的运营商的优先规则将不适用。这很奇怪,因为您发布的州似乎正确地尊重他们。

但是,您应该能够声明并应用明确的优先级规则,例如:你可以将它们定义为:

%left ADD_SUB_OP
%left MUL_DIV_OP

并像这样应用它们:

scalar_expr:
    | scalar_expr add_sub_ops scalar_expr %prec ADD_SUB_OP { Binary($2, $1, $3) }
    | scalar_expr mul_div_ops scalar_expr %prec MUL_DIV_OP { Binary($2, $1, $3) }

这样您仍然需要定义多个规则,但是您可以将所有运算符组合在一个组中具有相同的优先级(请注意,我选择的名称是一个不好的示例;您可能希望使用反映优先级的名称或描述内部的操作员很清楚他们指出了什么。)

这个解决方案是否值得,取决于我认为每组操作员的数量。