“验收必须把“合并前两态分别无冲突”“合并后只有一行”“每个合法词所需的归约”连起来。只报告状态数从14变13,无法说明压缩是否正确。若需要保留原文法与全部分析,继续使用规范LR(1),或让G…”
形式陈述
广义LR分析(GLR)允许LR动作表的一个格包含多个动作,并同时保留这些动作产生的分析分支。它的核心数据结构是图结构栈(graph-structured stack,GSS):不为每条分支复制整个栈,而把共有栈段合并保存。
下面给出一个可直接实现和核查的基础版本:文法有限、没有
一个GSS节点为
在输入位置
- 归约闭包:对每个当前顶点
以及 ACTION[ ] 中每个 归约,枚举其向后 条边、标签逆序匹配右部的路径。若到达 ,就加入顶点 和一条标记 、指向 的边 - 新边可能为已有栈顶新增归约路径,所以必须继续处理,直到该位置的顶点和边都不再变化
- 移进:对每个当前顶点的每个移进动作
,创建 ,以标记 的边指向旧顶点。完成所有这些移进后才进入下一位置 - 输入结束时,若某条可达栈路径具有accept动作则接受;若所有分支都无法继续则拒绝
同一
直觉
两条解析分支可能只在最近几个符号的分组上有区别,栈底却完全相同。分叉时共享栈底,随后若又到达同一状态和输入位置,就共享栈顶,把差别保留成多条前驱路径。这形成一个有向无环图:在本页无空串的模型中,每条边都指向更早的输入位置。
控制状态的共享与解析结果的共享是两回事。GSS回答“有哪些状态栈仍可继续”;共享打包解析森林回答“同一个区间有哪些语法树”。只保存GSS而丢掉归约来源,可以正确识别输入,却无法恢复全部结构。
森林用
例子与边界
保留两种分组,而不是替文法猜优先级
取 id+id*id。此文法没有声明乘法优先;GLR的正确结果必须保留两种分组。
消费前三个token id+id 后,下一token是 *。此时既可按
第一条在位置3就把
| 根规则 | 子区间 |
|---|---|
其中
基础校验实现使用SLR多动作表和上述GSS闭包,在长度不超过7的全部3280个 id,+,* token串上,与独立Earley识别器得到相同接受结果。这个有限核对验证了例子和实现,不替代任意长度输入上的正确性证明。
新边不能被“这个状态处理过了”吞掉
假设顶点
正确调度以待处理的归约路径或新边所触发的组合为单位;最简单的教学实现反复重扫直到边集稳定。森林中新加的分组若不改变GSS边,也仍要保存在已有森林节点下,不能因为控制图未改变就丢弃。
空规则为何另需处理
因此“GLR可以处理一般CFG”是关于完整算法家族的能力,不等于任意十几行分叉栈代码都覆盖所有空产生式、单位循环和歧义森林。使用库时应检查它具体实现的是哪一种变体。
推论与应用
正确性可由栈路径不变量说明:初始GSS只有初始栈;每条移进边对应一条合法LR移进;每条归约边对应一条真实可弹出的右部路径,所以不会凭空造出栈。反过来,每条非确定LR执行都能逐步在GSS中找到对应路径,因为算法没有丢弃冲突动作,并对新增路径完成闭包。accept因此对应至少一棵完整解析树。
只保存一份显式栈列表时,歧义可能让分支数指数增长。GSS把控制节点数限制为至多
对本页的朴素闭包实现,令最大产生式长度为
经过二叉化、去重的专门GLR变体可获得固定文法下
参考资料
- Masaru Tomita, “An Efficient Context-Free Parsing Algorithm for Natural Languages”, IJCAI, 1985,§§3.1–3.3:栈列表、共享栈和GSS;§§4.1–4.2:子树共享与局部歧义打包
- Elizabeth Scott and Adrian Johnstone, “Right Nulled GLR Parsers,” ACM TOPLAS 28(4), 2006, 577–618;作者机构出版目录
- Elizabeth Scott, Adrian Johnstone, Giorgios R. Economopoulos, “BRNGLR: A Cubic Tomita-Style GLR Parsing Algorithm,” Acta Informatica 44, 2007, 427–461:三次上界属于该改进算法