Parsing 降档:何时停止降档?

Parsing 降档:何时停止降档?,parsing,theory,context-free-grammar,formal-languages,shift-reduce,Parsing,Theory,Context Free Grammar,Formal Languages,Shift Reduce,我正在尝试学习移位减少解析。假设我们有以下语法,使用强制操作顺序的递归规则,其灵感来自: 我们想用shift-reduce解析来解析1+2。首先,1作为一个数字移位。我的问题是,它是否会减少到P,然后是M,然后是A,最后是S?它怎么知道在哪里停 假设它一路减少到S,然后移动“+”。我们现在有了一个包含以下内容的堆栈: S '+' 如果我们将“2”移位,则减少量可能为: S '+' NUMBER S '+' P S '+' M S '+' A S '+' S 现在,在最后一行的任一侧,S可以是

我正在尝试学习移位减少解析。假设我们有以下语法,使用强制操作顺序的递归规则,其灵感来自:

我们想用shift-reduce解析来解析1+2。首先,1作为一个数字移位。我的问题是,它是否会减少到P,然后是M,然后是A,最后是S?它怎么知道在哪里停

假设它一路减少到S,然后移动“+”。我们现在有了一个包含以下内容的堆栈:

S '+'
如果我们将“2”移位,则减少量可能为:

S '+' NUMBER
S '+' P
S '+' M
S '+' A
S '+' S
现在,在最后一行的任一侧,S可以是p、M、A或数字,从某种意义上说,任何组合都是文本的正确表示,它仍然是有效的。解析器如何“知道”如何实现它

A '+' M
这样就可以把整个表达式简化为A,那么S?换句话说,它如何知道在转移下一个标记之前停止减少?这是LR解析器生成中的一个关键难点吗


编辑:下面是对问题的补充

现在假设我们解析
1+2*3
。一些换班/减班操作如下:

Stack    | Input | Operation
---------+-------+----------------------------------------------
         | 1+2*3 | 
NUMBER   | +2*3  | Shift
A        | +2*3  | Reduce (looking ahead, we know to stop at A)
A+       | 2*3   | Shift
A+NUMBER | *3    | Shift (looking ahead, we know to stop at M)
A+M      | *3    | Reduce (looking ahead, we know to stop at M)

这是正确的吗(当然,它还没有完全解析)?此外,向前看1个符号是否也告诉我们不要将
A+M
减少到
A
,因为这样做会在阅读
*3
后导致不可避免的语法错误?

您描述的问题是创建
LR(0)
解析器的问题-也就是说,自底向上的解析器不会对当前正在解析的符号以外的符号进行任何前瞻。您描述的语法似乎不是
LR(0)
语法,这就是为什么您在尝试使用lookahead解析它时遇到麻烦的原因。但是,它看起来确实是
LR(1)
,因此通过查看输入中的前1个符号,您可以轻松确定是移位还是缩小。在这种情况下,当
LR(1)
解析器在堆栈上有
1
时,它会向前看,看到下一个符号是
+
,并意识到它不应该减少过去的
a
(因为这是它可以减少到的唯一一个仍然与第二位置的
+
匹配的规则)

LR
语法的一个有趣特性是,对于
LR(k)
for
k>1
的任何语法,都可以构造一个等价的
LR(1)
语法。然而,这一点并没有一直延伸到
LR(0)
——有许多语法无法转换为
LR(0)

有关
LR(k)
-ness的更多详细信息,请参见此处:


我不太清楚Yacc/Bison解析算法以及它何时更喜欢移位而不是还原,但是我知道Bison支持LR(1)解析,这意味着它有一个先行标记。这意味着令牌不会立即传递到堆栈。相反,他们要等到再也不能削减了。然后,如果移动下一个标记有意义,它将应用该操作


首先,在你的例子中,如果你在计算1+2,它会移动1。它会将该令牌减少为A,因为“+”前瞻令牌指示它是唯一有效的课程。由于没有更多的缩减,它将把“+”标记移到堆栈上,并保留2作为前瞻。它将移动2并减少到M,因为A+M生成A,并且表达式是完整的

“1+2”不会为您提供的语法产生移位/减少冲突吗?不会。Bison毫无怨言地接受了它(在用%token NUMBER\n%%\n..\n%%包装它之后)。如果我解析1+2*3,据我所知,堆栈在某一点上以A+M结束。这可以简化为A,但这在这里是不正确的,因为它会产生一个*..,没有规则存在。向前看1个符号是否表明不应出现这种减少?我在原来的帖子中添加了更多的细节。是的,确实如此-因为当你在堆栈上有
A+M
时,你向前看
*
,你会发现你必须在
*
的左边有一个
M
,所以你知道如果这会导致堆栈顶部不是
M
,就不要减少。
Stack    | Input | Operation
---------+-------+----------------------------------------------
         | 1+2*3 | 
NUMBER   | +2*3  | Shift
A        | +2*3  | Reduce (looking ahead, we know to stop at A)
A+       | 2*3   | Shift
A+NUMBER | *3    | Shift (looking ahead, we know to stop at M)
A+M      | *3    | Reduce (looking ahead, we know to stop at M)