野牛LALR移位/减少冲突

时间:2018-09-02 17:10:20

标签: bison yacc lalr

我最近再次捡起野牛,但我仍在努力处理优先次序的工作方式以及如何解决基本的班次/减少冲突。我对编写语法规则以及递归的工作方式等感到很自在,但是我仍然无法绕过优先规则。

对于以下示例以及我的问题和理解,我将不胜感激。

test1.y

%token              ID
%token              TYPE_NAME
%token              ASTERIX

%nonassoc           F_T
%nonassoc           P_T

%%
f_type:
                    ID type         %prec F_T
;

type:
                    TYPE_NAME
|                   type ASTERIX    %prec P_T
|                   f_type
;
%%

test1.output

State 5

     1 f_type: ID type .
     3 type: type . ASTERIX

     ASTERIX  shift, and go to state 7

     ASTERIX   [reduce using rule 1 (f_type)]
     $default  reduce using rule 1 (f_type)

此示例产生移位减少冲突,因为状态机无法确定是否应减少 ID类型*->类型*->类型 ID类型*-> ID类型->输入。后者是期望的结果。我尝试通过提供以下规则类型来解决此冲突: type ASTERIX 的优先级高于 f_type:ID type ,但这似乎不起作用。我也不想为终端 ASTERIX 分配任何优先级,因为我想在其他情况下使用它。

test2.y

%token      ID
%token      DOUBLE_PLUS

%left       POSTFIX_OP
%right      PREFIX_OP

%%
exp:
            ID
|           exp DOUBLE_PLUS     %prec POSTFIX_OP
|           DOUBLE_PLUS exp     %prec PREFIX_OP
;
%%

test2.output

State 4

    2 exp: exp . DOUBLE_PLUS
    3    | DOUBLE_PLUS exp .

    DOUBLE_PLUS  shift, and go to state 6

    DOUBLE_PLUS  [reduce using rule 3 (exp)]
    $default     reduce using rule 3 (exp)

此示例会产生移位/减少冲突,因为减少 DOUBLE_PLUS exp DOUBLE_PLUS 的模棱两可。因此,我尝试为 DOUBLE_PLUS exp 分配比 exp DOUBLE_PLUS 更高的优先级,但这也不起作用。可以通过为终端 DOUBLE_PLUS 分配左或右优先级来解决此冲突,而我猜测分配左优先级意味着 exp DOUBLE_PLUS 会先减小后右意味着 DOUBLE_PLUS exp 首先会减少,但我也希望有某种方法可以通过使用%prec 注释来实现。

我也不确定我是否正确理解 .output 文件。规则中的指示堆栈中的内容和先行令牌是什么,但是为什么在后面的示例中甚至提到规则2?我的意思是 exp:exp。 DOUBLE_PLUS 应该不会有任何冲突?

1 个答案:

答案 0 :(得分:1)

这是我写的关于yacc /野牛优先算法的another answer的引文。我不知道它是否比《龙书》中的文档或说明更清楚,但这是迄今为止我能做的最好的事情。如果您感到困惑,请告诉我:

  

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

由于优先级比较永远不会在两个规则之间进行-优先级比较始终在规则和先行标记之间-优先级顺序声明必须同时包含规则(无论是隐式还是显式)和令牌名称。因此,在您的第一个示例中,F_TP_T之间的优先顺序毫无影响。同样,在第二个示例中,PREFIX_OPPOSTFIX_OP是仅与规则关联的优先级,因此优先级排序无效。

如果可以同时进行平移和归约,并且将规则与前瞻标记之间的比较表明该规则具有更高的优先级,那么将生成归约动作。如果先行令牌具有更高的优先级,则将生成移位操作。但是,只有在既有转移又有可能减少的情况下,才参考声明。如果语法只能执行一个动作,那就是它将执行的动作,无论如何。 (例外:%nonassoc声明实际上将禁止某些减少。)

如果比较结果相等(规则和令牌都在同一优先级组中),则%left组将优先使用shift,%right组将减少shift。对于一元运算符,无论是前缀还是后缀,通常都不会采用这种情况,因为在这种情况下,只能执行一个操作。

如果将标记插入优先规则中会在语法的另一部分中产生具有优先顺序的冲突,则您不能将优先声明用作捷径。您只需要编写语法就可以使优先级明确。通常这并不困难。另一方面,在两种不同的语法环境中相冲突的优先顺序可能会使人感到困惑,因此您可能需要重新考虑。

对于.output文件中的状态机输出,将打印整个状态,而不仅仅是导致冲突的部分。动作中指出了冲突; […]内的动作与其他动作发生冲突,并被bison的默认冲突解决机制所消除(更喜欢将shift设置为reduce;更喜欢reduce的规则在文件中位于较早位置)。粗略地说,移位规则在令牌前有.;化简规则的末尾有.