“SLR(1) 保留LR(0)的项目自动机与状态栈,只改变归约表的填法。状态 $I$ 中出现 $A\to\alpha\cdot$ 时,不再把归约填满整行,而是只填到”
形式陈述
LR(0) 分析把“已经读过的符号可能对应哪几条产生式的哪一段”编码成有限状态,再由这个状态驱动移进和归约。它从左向右读取输入,反向构造最右推导;括号里的 0 表示归约决定不使用向前看 token 来区分上下文。
对文法加新开始规则
点左边是已经识别的右部,右边是尚待识别的部分。
给项目集
从
同一个驱动器,两个表
若终结符边
同一 ACTION 格不能有两个不同动作。移进与归约同时出现称移进/归约冲突,两条不同归约称归约/归约冲突。无冲突时,这张表定义 LR(0) 解析器。
状态栈初始为
直觉
状态栈保存了沿“当前文法符号栈”运行 DFA 所经过的状态。移进是在这条路径末端续上一条边。归约则先撤去一段表示右部的路径,再从它之前的状态沿左部非终结符边前进。正因保留了整条状态历史,归约后不用从
文法符号栈是某个最右句型中不越过最右句柄的前缀,称为可行前缀。闭包中的预测项目表达:若下一段打算识别
例子与边界
七个状态完整解析 cdd
给产生式编号:
| 状态 | 项目 |
|---|---|
全部表格为:
| 状态 | c | d | S | C | |
|---|---|---|---|---|---|
| 0 | s3 | s4 | — | 2 | 1 |
| 1 | s3 | s4 | — | — | 5 |
| 2 | — | — | acc | — | — |
| 3 | s3 | s4 | — | — | 6 |
| 4 | r3 | r3 | r3 | — | — |
| 5 | r1 | r1 | r1 | — | — |
| 6 | r2 | r2 | r2 | — | — |
s3 表示消费一个 token 后压状态 3,r3 表示按产生式 3 归约。输入 cdd 的每一步为:
| 状态栈 | 符号栈 | 剩余输入 | 动作 |
|---|---|---|---|
| 0 | 空 | cdd |
s3 |
| 0,3 | c | dd |
s4 |
| 0,3,4 | cd | d |
|
| 0,3,6 | cC | d |
|
| 0,1 | C | d |
s4 |
| 0,1,4 | Cd | ||
| 0,1,5 | CC | ||
| 0,2 | S | acc |
第二次归约弹掉的是状态 3 和 6,露出 0,再查 GOTO[0,C]=1。若误用归约前的栈顶 6 查询 GOTO,就会查不到正确后继。
输入 cd 则只形成第一个
完整项目也可能过早
改用
这个冲突没有证明语言有歧义;语言只是非空的
推论与应用
LR 驱动器的栈不变量解释了为何无需回溯:若表无冲突,选定动作对应唯一可行的句柄处理;每次归约都是反向走一条最右推导。接受时归约得到开始符号且输入恰好结束,因此生成的树确实属于文法。完整性还依赖 LR(0) 无冲突条件,不能对任意 CFG 的任意冲突消解作相同承诺。
令项目总数为
状态编号没有数学意义。本页编号由按符号排序的可达搜索确定;检查另一份表时应比较项目集和转移,而不是要求“状态3”在所有资料中都代表同一集合。
参考资料
- Stanford CS143, Lecture 8: Bottom-Up Parsing,可行前缀、项目自动机、LR(0) 与 SLR 部分,尤其 slides 54–81
- Cornell CS4120, LR(1) and LALR Parsing,LR(0) 状态与向前看扩展的衔接
- Aho, Lam, Sethi, Ullman, Compilers, 2nd ed., 2007,§4.6