野牛转移减少了逗号的冲突

时间:2016-05-09 09:49:44

标签: parsing bison lalr

无论我改变什么,我都会发出奇怪的转移减少警告。减少语法:

expr : NUMBER 
     | NUMBER ',' expr 
     | expr ',' NUMBER ',' NUMBER

Bison报告用逗号将第二条规则减少。我试图设置优先级:

%nonassoc num_p
%nonassoc exp_p

expr : NUMBER %prec num_p
     | NUMBER ',' expr %prec exp_p 
     | expr ',' NUMBER ',' NUMBER

但警告保持不变。有人可以解释一下我在这里缺少什么吗?

2 个答案:

答案 0 :(得分:1)

很明显,以下内容含糊不清:

expr : NUMBER %prec num_p
     | NUMBER ',' expr %prec exp_p 
     | expr ',' NUMBER ',' NUMBER

因为可以以各种方式解析任何三个或更多个数字的列表。粗略地说,我们可以从列表的开头取出单个数字,或者从列表的末尾取出一对数字,直到我们在中间的某个地方相遇;但是,没有关于中间点可能位置的定义。

例如,考虑可以产生1, 2, 3, 4, 5的各种解析树。这里只有两个(数字表示用于扩展expr的生产):

    expr(2)                       expr(3)
    /    \                       /   |  \
  /       \                     /    |   |  
 |    expr(2)                  /     |   |  
 |     /   \                  /      |   |
 |    /     \             expr(3)    |   |
 |   /    expr(3)          / | \     |   |
 |   |    /  | \          /  |  \    |   |
 |   |expr(1)|  \     expr(1)|   |   |   |
 |   |   |   |   |       |   |   |   |   |
 1 , 2 , 3 , 4 , 5       1 , 2 , 3 , 4 , 5

在某种意义上,上述两棵树都是最大的。左边的那个使用生产2获得尽可能多的单个NUMBER,直到只剩下两个NUMBER用于生产3.右边的那个NUM尽可能多地生产3。 (如果数字列表具有均匀长度,则需要单次应用生产2。)

为了解决歧义,我们需要明确说明意图。但在我看来,不太可能通过优先声明解决它。请记住,优先级关系始终位于可能的缩减(位于解析器堆栈顶部)和前瞻符号之间。他们从不比较两个先行符号或两个产品。如果前瞻符号获胜,则将其移动到堆栈上;如果减少获胜,则减少堆栈。它并不复杂。

因此,如果优先权有所帮助,则相关令牌必须为',',而不是NUMBERNUMBER必须始终转移到解析堆栈上。由于没有生成以','结束,因此当NUMBER是先行符号时,永远不可能减少堆栈。相比之下,当','是前瞻符号时,通常可以减少解析器堆栈的顶部或移动','以准备进行不同的缩减。

在我们看到NUMBER并且正在查看','的情况下,唯一可以做出此决定的地方是,我们必须决定是否应用生产1,以准备生产3,或转移',',留下生产2作为唯一的选择。这些决定都不会繁荣:如果我们选择减少,生产3可能会变得不可能,因为列表中没有足够的数字;如果我们选择转移,那么生产3将永远不会被使用。

在从左到右的解析中,上面产生右侧解析的算法是不可能的,因为在我们到达结尾之前我们无法知道列表是偶数还是奇数,此时它是为时已晚,无法追溯减少前两个数字。另一方面,左手解析将需要四个令牌的前瞻,而不是一个,以便决定在哪个点停止使用生产2.这使得可以构造LR(1)语法,因为有一种将LR(k)语法重写为LR(1)语法的方法,但结果语法通常很复杂。

我怀疑这些都不是你的意图。为了推荐一个解决方案,有必要知道准确的意图。

一种可能性(由评论推动)是expr还包括既不是数字也不是数字列表的东西:

expr: NUMBER
    | complex_expression

在这种情况下,可能是语法意图捕获两种可能性:

  • 包含NUMBER s的列表,最后可能为complex_expression;

  • 包含偶数NUMBER个,开头可能为complex_expression的列表。

上述公式中含糊不清的是仅由NUMBER组成的列表,因为第一个或最后一个数字可以解析为expr。这里只有几个合理的可能决议:

  • NUMBER列表始终被解析为第一个选项(最后为expr

  • 当且仅当列表中有奇数个元素时,NUMBER的列表才会被解析为第二个选项(开头为expr)。

第一个分辨率要容易得多,所以我们可以从它开始。在这种情况下,列表中的第一个元素实际上决定了列表的解析方式,因此无法将第一个 NUMBER缩减为expr。我们可以通过分离不同类型的expr

来表达这一点
expr: list_starting_expr | list_ending_expr
list_starting_expr: complex_expression ',' NUMBER ',' NUMBER
                  | list_start_expr ',' NUMBER ',' NUMBER
list_ending_expr  : complex_expression
                  | NUMBER ',' list_ending_expr 
                  | NUMBER

上例中的最后一个产品允许将NUMBER s的完整列表解析为list_ending_expr

它还允许将仅包含单个complex_expression的列表解析为list_ending_expr,而要求list_starting_expr至少包含三个元素。如果没有这个,只包含complex_expression的列表就会含糊不清。在问题的示例语法中,隐式禁止仅包含complex_expression的列表;可以通过将list_ending_expr的基本制作从list_ending_expr: complex_expression更改为list_ending_expr: NUMBER ',' complex_expression来复制。

但是,如果我们想要第二个分辨率怎么办?我们仍然可以识别语言,但构建正确的解析树可能需要一些手术。我们可以从列出仅包含NUMBER s。

的情况开始
expr: list_starting_expr | list_ending_expr | ambiguous_list
list_starting_expr: complex_expression ',' NUMBER ',' NUMBER
                  | list_starting_expr ',' NUMBER ',' NUMBER
list_ending_expr  : NUMBER ',' complex_expression
                  | NUMBER ',' list_ending_expr 
ambiguous_list    : NUMBER
                  | NUMBER ',' ambiguous_list

尽管经常重复声称在自下而上的语法中应该避免右递归,但ambiguous_listlist_ending_expr必须是正确的递归,因为我们无法区分这两者直到我们到达列表末尾的可能性。

现在我们可以使用语义操作来简单地计算列表中元素的数量。该行动需要与ambiguous_list减少到expr

相关联
expr: list_starting_expr | list_ending_expr
    | ambiguous_list {
        if (count_elements($1) % 2 == 1) {
          $$ = make_list_starting_expr($1);
        }
        else {
          $$ = make_list_starting_expr($1);
        }
      }

但我们实际上可以用语法区分这两种情况,正是因为正确的递归:

expr: list_starting_expr
    | list_ending_expr
    | even_list_of_numbers
    | odd_list_of_numbers
list_starting_expr  : complex_expression ',' NUMBER ',' NUMBER
                    | list_starting_expr ',' NUMBER ',' NUMBER
list_ending_expr    : NUMBER ',' complex_expression
                    | NUMBER ',' list_ending_expr 
odd_list_of_numbers : NUMBER
                    | NUMBER ',' NUMBER ',' odd_list_of_numbers
even_list_of_numbers: NUMBER ',' NUMBER 
                    | NUMBER ',' NUMBER ',' even_list_of_numbers

将其写成:

可能更有意义
expr: expr_type_one | expr_type_two
expr_type_one: list_starting_expr | even_list_of_numbers
expr_type_two: list_ending_expr | odd_list_of_numbers
 /* Remainder as above */

注意: 上述语法与原始问题中的语法一样,不允许expr只包含complex_expression。如果希望只处理单个complex_expression的情况,则可以将该语法直接添加到expr的作品中(或expr_type_one或{{1}中的任何一个是合适的。

答案 1 :(得分:-2)

有时候将左递归重写为右递,有点像这样:

expr : NUMBER
     | expr ',' NUMBER
     ;

可以在那里找到理论基础:https://cs.stackexchange.com/questions/9963/why-is-left-recursion-bad