【问题标题】:Shift-reduce: when to stop reducing?Shift-reduce:何时停止减少?
【发布时间】:2011-02-07 06:38:15
【问题描述】:

我正在尝试学习 shift-reduce 解析。假设我们有以下语法,使用强制操作顺序的递归规则,受到ANSI C Yacc grammar 的启发:

S: A;

P
    : NUMBER
    | '(' S ')'
    ;

M
    : P
    | M '*' P
    | M '/' P
    ;

A
    : M
    | A '+' M
    | A '-' M
    ;

我们想使用 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 或 NUMBER,并且在任何组合都是文本的正确表示的意义上,它仍然是有效的。解析器是如何“知道”它的

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 后不可避免的语法错误?

【问题讨论】:

  • “1+2”不会对您提供的语法产生移位/减少冲突吗?
  • 不。 Bison 毫无怨言地接受了它(当然,在用 %token NUMBER\n%%\n...\n%% 包装之后)。

标签: parsing theory context-free-grammar formal-languages shift-reduce


【解决方案1】:

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

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

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

http://en.wikipedia.org/wiki/LR_parser

【讨论】:

  • 如果我解析 1+2*3,根据我的理解,堆栈最终会在 A+M 处结束。这可以简化为 A,但这在这里是不正确的,因为它会产生 A*...,但不存在任何规则。向前看 1 个符号是否表明这种减少也不应该发生?我在原帖中添加了更多细节。
  • 是的,确实如此 - 因为当您在堆栈中有 A+M 并且向前看 * 时,您会看到您必须有一个 M* 的左侧,所以如果这会导致堆栈顶部不是M,你知道不要减少。
【解决方案2】:

我不太确定 Yacc / Bison 解析算法以及它何时更喜欢转移而不是归约,但是我知道 Bison 支持 LR(1) 解析,这意味着它有一个前瞻标记。这意味着令牌不会立即传递到堆栈。相反,他们等到没有更多的减少发生。然后,如果移动下一个标记有意义,则应用该操作。

首先,在您的情况下,如果您正在评估 1 + 2,它将移动 1。它将将该标记减少为 A,因为“+”前瞻标记表明它是唯一有效的课程。由于没有更多的减少,它会将“+”标记移到堆栈上并保持 2 作为前瞻。它将移动 2 并归约为 M,因为 A + M 产生 A 并且表达式是完整的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-19
    • 2017-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多