一、Kernel(核心项目):项目集的“骨架”
1. 什么是核心项目?
核心项目是指在 LR(1) 项目集中,不是通过“闭包运算”生成的项目。它们是项目集的初始成员,其他项目(非核心项目)都是由核心项目通过闭包规则扩展而来的。
具体来说,核心项目包括:
-
拓广文法的初始项目
[S' → ·S, #](这是整个分析的起点); -
所有通过“状态转换”生成的项目(即
GOTO运算后,· 移动到下一个位置的项目)。
例如:
如果有项目 [A → α·Xβ, a],通过 GOTO 运算(遇到符号 X 时移动 ·)得到 [A → αX·β, a],这个新项目就是核心项目。
2. 核心项目的作用
-
作为项目集的“种子”:每个项目集都是围绕核心项目展开的,非核心项目只是核心项目的“附属品”(通过闭包规则添加)。
-
简化项目集的表示:在实际构建 LR(1) 项目集时,只需记录核心项目即可,非核心项目可以通过闭包运算动态生成,减少冗余。
二、搜索符(Lookahead):解决冲突的“指南针”
1. 什么是搜索符?
搜索符是 LR(1) 项目中逗号后面的符号(终结符或 #,# 表示输入结束),格式为 [A → α·β, a] 中的 a。它的作用是指示“当前项目完成后,下一个可能出现的符号”,用于精确判断分析动作(移进或归约)。
2. 搜索符的来源
搜索符的产生遵循严格的规则,主要来自两个场景:
(1)初始项目的搜索符
拓广文法的初始项目 [S' → ·S, #] 中,搜索符固定为 #(表示输入结束),因为整个分析的最终目标是推导出 S 并遇到结束符。
(2)闭包运算中生成的搜索符
当通过闭包规则扩展项目集时,新项目的搜索符由“父项目”的信息推导而来:
-
若父项目是
[A → α·Bβ, a](其中 B 是非终结符),则对于 B 的产生式B → γ,新生成的项目[B → ·γ, b]中,b是First(βa)中的所有终结符(First集表示 βa 能推导出的第一个终结符集合)。
简单说:子项目的搜索符,是父项目中“· 后面的符号串 + 父项目的搜索符”所能推导出的首个终结符。
示例:
父项目 [A → α·Bβ, a],B 有产生式 B → γ。
-
计算
First(βa)(β 后面跟 a 能产生的第一个终结符),假设结果为{b, c}; -
则闭包运算会生成项目
[B → ·γ, b]和[B → ·γ, c],搜索符b和c来自First(βa)。
(3)状态转换中继承的搜索符
当通过 GOTO 运算(移动 ·)生成新的核心项目时,搜索符与原项目保持一致。
例如:
原项目 [A → α·Xβ, a],通过 GOTO(X) 生成 [A → αX·β, a],新项目的搜索符仍是 a。
3. 搜索符的作用
-
精确区分归约条件:避免 LR(0) 中“盲目归约”的问题。例如,两个归约项目
[A → α·, a]和[B → β·, b],只有当搜索符匹配当前输入符号时才执行归约,若a ≠ b则不会冲突。 -
解决移进-归约冲突:例如,项目
[A → α·aβ, b](移进 a)和[B → γ·, b](归约),若当前输入符号是a则移进,是b则归约,通过搜索符明确区分。