LR Parse
LR Parse
个人学习理解,可能有许多理解或者表达上的错误,若有发现错误,欢迎帮我纠正~
L: left to right parsing
R: rightmost derivation
LR 分析器结构
- Table-based
- Actions
- Shift (移进)
- Reduce (归约)
- Data Structures
- Stack of states/symbols {s}
- Action table: action[s, a]; a是终结符
- Goto table: goto[s, X]; X是非终结符
LR(K) 代表: 从左至右分析、最右边推导、超前查看 k 个单词
Bottom-up 自底向上的分析

Bottom-up 方式,最终得到的是开始符号s
, 换句话讲,构建语法分析树的时候,从叶子结点得到根,如图

LR
就是一种常见的使用 bottom-up
分析的 语法分析器。
Bottom-up 语法分析器的分类

LR(0) -> SLR(1) -> LR(1) -> LALR(1)
Shift & Reduce
移进(Shift)、归约(Reduce)是 LR 分析器工作中对输入符号进行处理的两种操作。
移进(Shift)操作:将输入的符号压入到 LR 分析栈。
归约 (Reduce)操作:将 LR 分析栈顶部的某一项或多项用某个产生式归约。换句话说,若栈顶的某些项符合某条产生式的右部,则将这几项出栈,然后将产生式的左部入栈。例如,栈顶有 ABC 三项, 有产生式P -> A B C
则将 A B C 出栈,将 P 入栈,最终栈中只剩下 P。
Action table 和 Goto table 用来指导 LR 分析器进行 Shift 还是 Reduce 操作。
Action & Goto Table
这就有点像有限自动机了, LR 分析栈中的值实际上是状态的 ID。


action 表的说明: action[s, a]
s: State ,类似 DFA 中的状态,输入符号可以转移状态。
a: 符号,类似 DFA 中的符号,输入符号可以转移状态。
每一行都是一个状态( State ), 列是非终结符和终结符组成,非终结符部分构成 Goto 表,用来记录归约后切换的目标状态。
action 表的值有 4 种类型:
- SX : Shift X X代表状态的 ID
- push state X 把 X 状态压入到分析栈
- read new a 读取一个新的字符 a
- RX: Reduce X
- 使用产生式 X: $P->Y_1Y_2…Y_k$ 归约
- 弹出栈中 k 项,后取栈顶状态 u
- 压栈: goto [ u, P ]
- A: Accept 识别句子成功
- 无值:语法错误
Goto 表,goto [ u, p ]
u 是归约后栈顶的状态 ID,P 是非终结符,其实就相当于切换到状态 u 中 P 非终结符的表格的值对应的状态。
Action/Goto 表的构造算法
这一块很抽象,涉及到很多离散数学中的理论,目前我还没系统学习离散数学,理解和表达可能有一些偏差和错误。
构造思路与 NFA 转 DFA 所采用的子集构造法类似。首先找出文法中独立的状态,然后计算每个状态对字母表中每一个字符的转移状态,最后合成一张表。
Configuration Set
- 每一个集合代表一个状态
“点” 记号
$T->T·F$
这里的 ·
是记号,在 F 的左侧,可以预测所有 F 可以推出的产生式,例如:
$T->T·F$
$F->·(T)$
$F->·id$
这些产生式构成的集合就是 Configuration Set
Closure
Closure 性质
如果 $T->X_1…X_i·X_{i+1}…X_n$ 在集合中,$X_{i+1}$是非终结符,那么$X_{i+1}->·Y_1Y_2…Y_n$ 也在该集合中。若 $Y_1$ 是非终结符,则继续像集合中添加 $Y_1->·Z_1Z_2Z_3…Z_n$ 直到
·
遇到终结符。Configuration 初始: $closure(S’->·S)$
Compute as fixed point
Closure 计算例子

Successor(I, X)
移动 Configuration Set 中点的位置。
- 保留所有“点”后面是 X 的产生式,$$X \in (N \cup T)$$
- 将“点”向右移动一个字符
- 重新计算闭包
例如 Successor(I, ‘(‘) 这里的 I 是 Closure 例子中 I 集合
I 中只有 $F->·(T)$ 满足“点”后面的字符是“(” ,将“点”的位置向右移动 1 个字符,计算 $closure({F->(·T)})$ 作为结果

合在一起,求 G 的所有 states

构造例子

构造的Action/Goto表如下

Viable Prefix in Bottom-up (不知道这个有什么用,先挂这)
两个概念
- Viable Prefix
- Handle
例子
1 |
|
Operation performed | Stack | Comments |
---|---|---|
(.E)+id | ( | shift ( |
(E.)+id | ( E | shift E |
(E).+id | ( E ) | shift ) |
B.+id | B | reduce (E) to B |
B+.id | B + | shift + |
B+id. | B + id | shift id |
A | A | reduce B + id to A |
得到 (E).+id
的下一步 (E)+
不在 Stack 中,取而代之的是 B +
, 这是因为 (E)
是一个 handle
, Stack 中的所有元素不能在 handle
的上入栈。
(
,(E
,(E)
都是 (E) handle
的 viable prefix
Shift Reduce Parser
的 Stack 中只能放 viable prefix
, 遇到 handle
要进行 reduce
操作。
通俗点讲,Parser 在工作的时候,对输入符号序列执行 shift 操作,直到遇到一个 handle。遇到 一个 handle 用相应的产生式(production)把 handle 替换成对应的非终结符,Bottom-up
用 right-side
替换 left-side
。
viable prefixes
用来辅助 Parser 决定 shift / reduce 操作。
Referrence
https://www.geeksforgeeks.org/viable-prefix-in-bottom-up-parsing/
https://www.geeksforgeeks.org/shift-reduce-parser-compiler/
http://anoopsarkar.github.io/compilers-class/assets/lectures/lr2-lr_0-parsing.pdf
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!