语法分析器从词法分析器获得 Token 序列
- 对于语法错误的程序
报告错误信息, - 对于语法正确的程序
生成语法分析树, 例如抽象语法树, Abstract Syntax Tree, AST( )
自底向上指的是
Shift-Reduce
将输入的串使用分割符 |
分成两部分|
右侧的部分是仍未被分析器处理的终结符|
左侧的部分既包含终结符又包含非终结符
初始状态下|
位于输入的串的第一个符号的左侧|
左侧的部分进行规约|
在串能够被接收的理想情况下S |
S
是文法的开始符号
下面图示这个方法
src:https://rainoftime.github.io/teaching/index.html
LR 分析的一般模式是基于栈的 Shift-Reduce|
左侧的部分|
右侧的部分则位于输入流中
LR(0) 语法分析
状态栈与 LR(0) 自动机
对于上面提到的|
左侧的
项
如果处于 $A \rightarrow \alpha \bullet \beta \gamma$ 状态的串读入了 $\beta$
为了方便表示起始状态与终结状态EOF
的概念
应用于 LR(0) 语法分析的 NFA
按以下方法
- 项 $X \rightarrow \bullet \alpha \beta$ 接收 $\alpha$ 后变成 $X \rightarrow \alpha \bullet \beta$
因此。 添加从状态 $X \rightarrow \bullet \alpha \beta$ 到状态 $X \rightarrow \alpha \bullet \beta$ 的转移方式, 条件是接收 $\alpha$, 。 - 如果存在产生式 $X \rightarrow \alpha Y \beta$
$Y \rightarrow \gamma$, 则 $X \rightarrow \gamma \bullet Y \beta$ 可以无条件地, 接收 $\varepsilon$( 转移到 $Y \gamma \gamma$) 。 状态 $X \rightarrow \gamma \bullet Y \beta$ 表示希望看到由 $Y \beta$ 推导出的串( 因此要先看到由 $Y$ 推导出的串, 因此加上 $Y$ 的各个产生式对应的项, )
一个简单的例子如图所示
src:https://rainoftime.github.io/teaching/index.html
然后对于这个 NFA
应用于 LR(0) 语法分析的 DFA
我们可以跳过 NFA 而直接构造 DFA
函数 $\text{Closure}(I)$ 和 $\text{GOTO}(I, X)$ 是两个子程序
前者是期望规约过程下一个读到的非终结符
后者是项 $I$ 接收输入 $X$ 后能够到达的项 $J$ 的 $\text{Closure}(J)$ 集合
构造 DFA 的方法以以下伪代码给出
Initialize T to {Closure({S' -> \bullet S\$})}
Initialize E to empty
repeat
for each state I in T
for each item "A -> \alpha \bullet X \beta" in I
let J be Goto(I, X)
T <- T \cup {J}
E <- E \cup {I ->^X J}
until E and T did not change in this iteration
在一切的开始
对于每一个状态
src:https://rainoftime.github.io/teaching/index.html
语法分析表
根据 DFAs*
表示 shift into state *r*
表示 reduce by rule *accept
表示接收g*
表示 goto state *
在这个网站
https://lr0parser.com/
语法分析
根据语法分析表
// 令 s 是栈顶状态, a 是 w$ 的第一个符号;
while (1) {
// 一开始 s 为 Parsing DFA 的状态 1
if (Action[s, a] == "shift s/") {
将s/压入栈内;
让a为下一个输入符号;
} else if (Action[s, a] == "reduce A->b") {
从栈顶弹出|b|个状态;
令s/是当前栈顶状态,把GOTO[s/, A]入栈;
} else if (Action[s, a] == "Accept") break;
else error();
}
src:https://rainoftime.github.io/teaching/index.html
LR(0) 的局限性
可能会出现同一个状态同时有 shift 和 reduce 两个选择
SLR 语法分析
使用更加严格的归约条件以降低冲突的可能性
由于规约条件更加严格
要求
在 LR(0) 语法分析表的 Action 表项中
src:https://rainoftime.github.io/teaching/index.html
这个想法是类似 LL(1) 的
然而
LR(1) 语法分析
通过添加lookahead 符号以指导归约
LR(1) 的项一般形如
这个项的含义是
对于 LR(1) 的项 $A \rightarrow \alpha \beta \bullet,\ a$ 如果要进行规约
由于添加了 lookahead 符号
src:https://rainoftime.github.io/teaching/index.html
LR(1) 语法分析表的 Reduce Action 表项填写规则是
LALR(1) 语法分析
对于 LR(1) 中的一些状态
LR(1) 状态 $\{(A \rightarrow \alpha \bullet \beta,\ a),(B \rightarrow \gamma \bullet \delta,\ b)\}$ 去掉所有的 lookahead 符号后
通过下面的方法
直到所有状态都有不同的核心
- 选择两个具有相同核心的不同状态
- 合并被选择的状态
创建一个包含所有原来状态中的项的新状态, - 将所有指向原状态的边指向新状态
新状态指向所有原状态的后继状态;
由于这实际上减弱了对规约的要求
错误恢复
Local Error Recovery
通过在文法中添加特殊的 $\text{error}$ 终结符以控制错误恢复的流程
当 LR 语法分析器进入错误状态时
- 从状态栈中弹出元素
如有必要( ) 直到达到对 error 的操作是移进的状态, 。 - 移进 error
。 - 丢弃输入的符号
如有必要( ) 直到达到在当前状态下具有非错误操作的 lookahead 符号, 。 - 恢复正常的语法分析过程
。
src:https://rainoftime.github.io/teaching/index.html
Global Error Recovery
不需要修改文法
TODO
LR(0), SLR, LR(1), LALR(1) 的关系
在规约条件上
在 SLR(1) 中