6.2 LR 项(item)与 LR 分析直觉
LR 语法分析器(parser)不必把全部历史词法单元(token)记下来;它以 LR 项 构成的有限自动机状态(state)概括当前可行前缀(viable prefix)。项是带点的产生式:E -> E + . T 表示已识别 E +、期待 T;E -> E + T . 表示规则完整并可能归约。点不是源码词法单元。
closure 与 GOTO
从增广规则 S' -> . Program 开始。点前是非终结符(nonterminal)时,闭包(closure) 必须加入其所有候选规则的起点,并递归进行。例如 Program -> . StmtList EOF 会加入 StmtList -> . Stmt StmtList、StmtList -> . epsilon,再加入 Stmt 的规则。epsilon 规则的点一开始就在末尾,但是否可归约还取决于向前看(lookahead)符号。
GOTO(I,X) 把 I 中点前为 X 的项跨过 X,再取闭包。终结符(terminal)边是移进,非终结符边是归约后的 GOTO。A -> alpha . a beta 提供 shift a;A -> alpha . 提议 reduce;S' -> Program . 在 EOF accept。LR(0) 的归约信息太粗,后续 SLR 与 LR(1) 会补充向前看符号。
手工构造一个状态
取 S' -> E、E -> E + n | n。从 S' -> . E 开始,闭包加入 E -> . E + n、E -> . n。GOTO(I,n) 只推进后者,得到 E -> n .:number 已构成 E,可考虑归约,但不是对任何向前看符号都可归约。
GOTO(I,E) 则同时含 S' -> E . 与 E -> E . + n,它既表示 EOF 时可以接受,也表示 + 可以继续扩展 E。这就是 LR 状态的价值:它总结前缀仍允许哪些延续,而不重读历史输入。
把整个 LR(0) 自动机画出来
把上面的手工构造继续下去,S' -> E、E -> E + n | n 的全部 LR(0) 状态只有五个:
I0: S' -> . E I1: S' -> E . I2: E -> n .
E -> . E + n E -> E . + n
E -> . n
I3: E -> E + . n I4: E -> E + n .状态之间的边(GOTO/shift)可以列成一张表:
| 状态 | 遇到 E | 遇到 n | 遇到 + | 含义 |
|---|---|---|---|---|
| I0 | I1 | I2 | — | 初始闭包 |
| I1 | — | — | I3 | 可在 EOF 接受,或用 + 扩展 E |
| I2 | — | — | — | 归约 E -> n |
| I3 | — | I4 | — | 等待 + 后的 n |
| I4 | — | — | — | 归约 E -> E + n |
I1 同时含 S' -> E . 与 E -> E . + n,说明同一个状态可以既提议接受、又允许移进 +。I2 与 I4 是只含完成项的归约状态。把这张图和 6.1 的分析表对照,就能看清状态旁的数字到底从哪来。
读点号
- 点前终结符:可能移进。
- 点前非终结符:闭包加入其开始规则。
- 点在末尾:只是归约候选。
- 完全相同的项集应复用为同一状态。