【问题标题】:Bison LALR shift/reduce conflicts野牛 LALR 移位/减少冲突
【发布时间】:2018-09-02 17:10:20
【问题描述】:

我最近又拿起了野牛,但我仍在努力解决优先级的工作方式以及如何解决基本的移位/减少冲突。我对编写语法规则以及递归的工作原理等非常满意,但我仍然无法理解优先规则。

我将非常感谢以下示例中的一些 cmets 以及我对它们的问题和理解。

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.输出

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 type* -> type* -> typeID type* -> ID type -> 输入。后者是期望的结果。我尝试通过赋予规则类型:type ASTERIXf_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.输出

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不应该有任何冲突吗?

【问题讨论】:

    标签: bison yacc lalr


    【解决方案1】:

    这里引用了another answer 我写的关于 yacc/bison 优先算法的内容。我不知道它是否比龙书中的文档或描述更清晰,但这是我迄今为止能够做到的最好的。如果您发现它令人困惑,请告诉我:

    回想一下,在产生式和产生式之间定义了优先关系 一个终端。它不涉及两个终端或两个产生式(和 所以不能用来解决reduce-reduce冲突)。比较 在可以减少的生产优先级和 前瞻终端确定是否会发生减少或移位。 为方便起见,产生式以 终端,通常是生产中的唯一终端;这 对应于一个常见的用例,但有时会令人困惑。在 特别是,%prec 声明仅用于为规则命名 用于优先声明,最好考虑一下 以这种方式来讨论它,而不是作为“明确”的声明。

    由于优先级比较永远不在两个规则之间——它们总是在规则和前瞻标记之间——优先顺序声明必须包括规则(无论是隐式还是显式)和标记名称。因此,在您的第一个示例中,F_TP_T 之间的优先顺序没有任何影响。同样,在第二个示例中,PREFIX_OPPOSTFIX_OP 是仅与规则关联的优先级,因此优先级排序无效。

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

    如果比较结果相等——规则和令牌都在同一个优先级组中——那么 %left 组将优先使用 shift,%right 组将优先使用 shift。这种情况通常不适用于一元运算符,无论是前缀还是后缀,因为在这种情况下,只有一个动作是可能的。

    如果在优先规则中插入标记会与语法的另一部分中的优先顺序产生冲突,那么您不能将优先声明用作捷径;你只需要编写你的语法来明确优先级。这通常并不难。另一方面,两种不同语法上下文中的冲突优先级可能会让人类非常困惑,因此您可能需要重新考虑。

    关于.output 文件中的状态机输出,打印整个状态,而不仅仅是引起冲突的部分。行动中指出了冲突; […] 包围的动作与其他动作发生冲突,并被 bison 的默认冲突解决机制消除(更喜欢 shift 而不是 reduce;更喜欢规则在文件中更早的 reduce)。粗略地说,移位规则在标记前有.; reduce 规则的末尾有.

    【讨论】:

    • 非常感谢这有很大帮助 :) 我猜一个产品的优先级是由具有最高优先级的终端定义的,如果有多个?
    • @qwerasdf:它始终是最后一个终端,除非被 %prec 声明覆盖。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-20
    • 1970-01-01
    • 2021-12-24
    • 2013-07-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多