我正在尝试学习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被移动为NUMBER。我的问题是,它是否减少到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
后出现不可避免的语法错误吗?
答案 0 :(得分:5)
您所描述的问题是创建LR(0)
解析器的问题 - 也就是说,自下而上的解析器不会对他们正在解析的当前符号之外的符号做任何预测。你所描述的语法似乎不是LR(0)
语法,这就是为什么你在尝试用前瞻解析它时遇到麻烦的原因。然而, 似乎是LR(1)
,因此通过在输入中查找前面的1个符号,您可以轻松确定是要移位还是减少。在这种情况下,LR(1)
解析器在堆栈上有1
时会向前看,看到下一个符号是+
,并意识到它不应该减少过去{ {1}}(因为这是唯一可以减少的东西,它仍然会匹配第二个位置A
的规则)。
+
语法的一个有趣属性是,对于LR
LR(k)
的任何语法,都可以构造一个等价的k>1
语法。但是,同样不会一直延伸到LR(1)
- 有许多语法无法转换为LR(0)
。
有关LR(0)
- ness:
答案 1 :(得分:1)
我不完全确定Yacc / Bison解析算法,当它更喜欢转换为reduce时,但我知道Bison支持LR(1)解析,这意味着它有一个先行令牌。这意味着令牌不会立即传递给堆栈。相反,他们会等到不再发生减少。然后,如果移动下一个标记是有意义的,它将应用该操作。
首先,在你的情况下,如果你正在评估1 + 2,它将会转移1.它会将该标记减少为A,因为'+'预测标记表示它是唯一有效的过程。由于没有更多的减少,它会将'+'标记移到堆栈上并保持2作为前瞻。它将移动2并减少到M,因为A + M产生A并且表达式完成。