自底向上的语法分析
从分析树的底部(叶节点)向顶部(根结点)方向构造分析树
自顶向下的语法分析采用最左推导,与之对应自底向上采用最左规约(反响构造最右推导)
移入规约分析
(1)从输入中移动终结符到栈中
(2)若此时栈中终结符无法与产生式列表中的右部任何一项对应,则继续移入终结符
(3)若此时栈中某一部分可以进行规约,则转换成对应可规约产生式的左部,然后再次进行(1)
(4)当输入中最后不含有终结符,且栈中最后只剩下开始符号的时候,证明规约结束
栈内符号串+剩余输入=“规范句型”
每规约一次,规范句型才会变化一次(而不是移入栈内就会规约
存在的问题
在规约的过程中,可能栈中的符号串满足多个产生式的右部,此时会出现类似二义性的问题
LR 分析法
LR 文法是最大的,可以构造出相应移入-规约语法分析器的文法
L:对输入进行左到右的扫描
R:反向构造最右推导序列
LR(k)中的 k 代表需要向前查看 k 个输入符号
基本原理就是正确识别句柄:
e.g.
S
→
b
B
B
S\to bBB
S→bBB
对于该例子,我们可以认为其分为下面几种状态:
S
→
⋅
b
B
B
S\to ·bBB
S→⋅bBB移进状态
S
→
b
⋅
B
B
S\to b·BB
S→b⋅BB待约状态
S
→
b
B
⋅
B
S\to bB·B
S→bB⋅B待约状态
S
→
b
B
B
⋅
S\to bBB·
S→bBB⋅规约状态
LR 分析表
对于产生式
S
→
B
B
S \to BB
S→BB
B
→
a
B
B \to aB
B→aB
B
→
b
B \to b
B→b
s 代表移入栈,后面的符号代表了移入栈的状态,rn代表用第 n 个产生式进行规约
(这里不需要深究分析表如何构成的,后面会解释)
假如输入为:bab$
(1)此时状态栈顶为 0,指针指向 b,对应状态表将其放入栈中,栈中现在为
b
,然后跳转到
4
号状态(
2
)此时状态栈顶为
4
,指针指向
a
,对应状态表使用第三个产生式对栈顶符号(
b
)进行规约,状态栈中
4
和符号栈中
b
被拿掉,栈中现在为
b,然后跳转到 4 号状态 (2)此时状态栈顶为 4,指针指向 a,对应状态表使用第三个产生式对栈顶符号(b)进行规约,状态栈中 4 和符号栈中 b 被拿掉,栈中现在为
b,然后跳转到4号状态(2)此时状态栈顶为4,指针指向a,对应状态表使用第三个产生式对栈顶符号(b)进行规约,状态栈中4和符号栈中b被拿掉,栈中现在为B
(3)此时状态栈顶为 0,遇到刚规约的 B,此时查看 GOTO 表可知进入 2 号状态
…
按照这个顺序不断进行,直到符号栈最后只剩下 S 为止,此时结束
LR(0)分析
在产生式右部中某位置标有圆点的产生式称为相应文法的一个 LR(0)项目,每个项目表示了一个句柄的识别状态
增广文法
在原本产生式的基础上定义一个新的开始符号,以及新的开始符号指向原开始符号的产生式,可以避免原本产生式中出现多个开始符号,保证分析器只有一个接受状态
等价状态
如果每个产生式的每个状态都认为是状态表中的一种状态,此时的状态个数会十分庞大,所以我们引入等价状态来削减状态数量,多个状态构成的等价状态才是状态机中的一个状态
e.g.
S
’
→
S
S’ \to S
S’→S
S
→
B
B
S \to BB
S→BB
B
→
a
B
B \to aB
B→aB
B
→
b
B \to b
B→b
这是上述例子的增广文法,此时对这四条产生式分析等价状态
我们就可以得到一共 7 种等价状态,而如果每个产生式都单独考虑的话就需要 10 个状态
项目集闭包
在 LR0 分析这一节中,我们定义了什么是项目,项目集就是项目的集合,而项目集的闭包则包括了所有可以从初始项目通过 0 次或多次产生式推导而到达的项目
CLOSURE 函数
CLOSURE 函数用于计算一个给定项目集的闭包,给定一个项目集 I和一个文法 G,CLOSURE(I)会返回一个新的项目集,包含 I 中的所有项目以及 I 可以推导出的所有项目
GOTO 函数
返回项目集 I 对应于文法符号 X 的后继项目集闭包,其实就是起到了跳转的作用。如在例子中,如此时处于项目集 I0,那么 GOTO(I0,a)表示的就是项目集 I3
构造 ACTION 表
(1)对于某个项目集 Ii,遍历其所有项目
如果某个项目为有移进项目的形式:
A
→
α
⋅
β
A \to \alpha·\beta
A→α⋅β
且 beta 的第一个符号为终结符 a,则 action 表第 i 行第 a 列填入 sk,k 为将 a 放入栈中后跳转的状态
(2)如果某个项目为有移进项目的形式:
A
→
α
⋅
A \to \alpha·
A→α⋅,则 action 表第 i 行所有列填入 rj,j 为对应产生式的序号
(3)如果某个位置没有任何移进或规约,则该行打上 err
(4)如果某个状态完成了所有规约只剩开始符号,该行打上 acc
构造 GOTO 表
(1)识别产生式中所有的非终结符
(2)计算当前状态集的闭包,确定通过当前栈顶的非终结符可以到达的下一个状态,重复的项目需要去重
(3)在 GOTO 表的对应位置,填入可以到达的下一个状态
LR(0)文法的形式化定义
M
=
(
C
,
V
N
∪
V
T
,
G
O
T
O
,
I
0
,
F
)
M=(C,V_N\cup V_T,GOTO,I_0,F)
M=(C,VN∪VT,GOTO,I0,F)
KaTeX parse error: Expected 'EOF', got '}' at position 73: …T,I=GOTO(J,X)\\}̲
I
0
=
C
L
O
S
U
R
E
(
S
’
→
⋅
S
)
I_0=CLOSURE(\\{S’ \to ·S\\})
I0=CLOSURE(S’→⋅S)
F
=
C
L
O
S
U
R
E
(
S
’
→
S
⋅
)
F = \\{ CLOSURE(\\{S’ \to S·\\})\\}
F=CLOSURE(S’→S⋅)
移进规约冲突
在同一个状态中,可能出现一个项目需要移进,另一个项目需要规约的情况,问题源于 LR(0)算法往前看了 0 个符号
SLR
FOLLOW 集可以帮助我们判定何时规约:当规约后的非终结符的 FOLLOW 集不包含状态集中将要移进的非终结符时,此时不应该规约
SLR 分析法的基本思想
SLR 与 LR0
在 SLR 分析表中,原本 LR0一整行都为规约动作中,某一可能因为冲突而转为移进,其余地方没有差别
SLR 分析表中的冲突
有时候 FOLLOW 集不能够完全解决冲突的问题,我们还需要更多的限制来完成冲突的区分
LR(1)分析
SLR 只是简单的考察了规约与 FOLLOW 集的关系,下一个输入符号
a
∈
F
O
L
L
O
W
(
A
)
a \in FOLLOW(A)
a∈FOLLOW(A)只是规约 a 的一个必要条件,而非充分条件
对于产生式
A
→
a
A \to a
A→a的归约,在不同位置,A 会要求不同的后继符号
在特定位置,A 的后继符号集是 FOLLOW(A)的子集
LR(1)项目的规范
将一般产生式
[
A
→
α
⋅
β
,
a
]
[A \to \alpha·\beta,a]
[A→α⋅β,a]的项称为 LR(1)项,a 是一个终结符,它表示在当前状态下,A 后面必须紧跟终结符,称为展望符
当 beta 不等于 epsilon 时,a 不起作用;当 beta 等于 epsilon 时,只有在下一个输入符号为 a 时才可以按照产生式进行规约
等价 LR(1)项目
∣
A
→
α
⋅
b
β
,
a
∣
|A \to \alpha·b\beta,a|
∣A→α⋅bβ,a∣
若
B
→
γ
∈
P
B \to \gamma \in P
B→γ∈P
则等价于
∣
B
→
γ
,
b
∣
|B \to \gamma,b|
∣B→γ,b∣
b
∈
F
I
R
S
T
(
β
a
)
b \in FIRST(\beta a)
b∈FIRST(βa)
当 beta 为空字符的时候,b=a,称为继承的后继符,否则叫自身的后继符。
LR(1)自动机
如果除了展望符以外,两个 LR1 项目集是相同的,则称这两个 LR1 项目集是同心的,如 I4 和 I11
LALR 分析法
同心项目集合并为同一个项目集,根据合并后的项目集族构造语法分析表,若语法分析表没有错误则为 LALR 分析法
规约规约冲突
在同心项目集中,两个相同的都需要规约的产生式对应的展望符号不相同,此时会出现矛盾无法确定合并后按照哪个展望符进行规约
LALR 分析法的特点
形式上与 LR(1)相同
大小上与 LR(0)/SLR 相当
分析能力:LR0<SLR<LALR<LR1