无论我改变什么,我都会发出奇怪的转移减少警告。减少语法:
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
但警告保持不变。有人可以解释一下我在这里缺少什么吗?
答案 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。)
为了解决歧义,我们需要明确说明意图。但在我看来,不太可能通过优先声明解决它。请记住,优先级关系始终位于可能的缩减(位于解析器堆栈顶部)和前瞻符号之间。他们从不比较两个先行符号或两个产品。如果前瞻符号获胜,则将其移动到堆栈上;如果减少获胜,则减少堆栈。它并不复杂。
因此,如果优先权有所帮助,则相关令牌必须为','
,而不是NUMBER
。 NUMBER
必须始终转移到解析堆栈上。由于没有生成以','
结束,因此当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_list
和list_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