自底向上的语法分析
从分析树的底部(叶节点)向顶部(根结点)方向构造分析树
自顶向下的语法分析采用最左推导,与之对应自底向上采用最左规约(反响构造最右推导)
移入规约分析
(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