4.7 更强大的 LR 语法分析器
4.7.5
对于某个产生式 \(B \rightarrow \gamma\cdot\delta\),
- 自发生成:如果 \(GOTO(CLOSURE(\{[A \rightarrow \alpha\cdot\beta, a]\}), X)\) 中包含 \([B \rightarrow \gamma\cdot\delta, b]\),其中 \(b \ne a\),则称 lookahead \(b\) 是对于 \(B \rightarrow \gamma\cdot\delta\) 自发生成的
- 传播:如果 \(GOTO(CLOSURE(\{[A \rightarrow \alpha\cdot\beta, b]\}), X)\) 中包含 \([B \rightarrow \gamma \cdot \delta, b]\),则称 lookahead \(b\) 是由 \(A \rightarrow \alpha\cdot\beta\) 传播到 \(B \rightarrow \gamma\cdot\delta\)
可以看到,\(J\) 中的产生式一定有如下的形式:\(B \rightarrow \gamma\cdot X \delta\)
有了上面关于自发生成和传播的定义,可以很自然地得到确定 lookahead 的算法(详见龙书 4.7.5 算法 4.62)
4.7.6 LR 语法分析表的压缩
-
压缩动作表
- 原理:动作表(ACTION)中通常有很多相同的行
- 第一种做法
- 为每个状态创建指向一维数组的指针,为每个(符号,动作)对列表创建一个一维数组(同时每个终结符分配一个编号),有相同动作的状态指向相同的(符号,动作)对列表
- 以图 4-37(英文版 Figure 4.37)中的动作表为例,图示:
- 类似于 Java 中引用变量和对象的关系:一个引用变量引用一个对象,一个对象可以被多个引用变量所指向(在 Java 中引用变量可以不指向任何对象,即引用变量为 null;同时,一个对象也可以不被任何引用变量所指向。但是在动作表中,状态一定有其对应的动作,动作也一定会有对应的状态集)
- 第二种做法
- 在第一种的基础上再次压缩,将一维数组替换为(终结符,动作)的列表(链表),将最频繁的动作放在列表结尾,符号为“any”,同时将 error 条目替换为规约动作
- 以图 4-37(英文版 Figure 4.37)中的动作表为例,图示:
- 类似于 switch-case 或路由器配置中的 ACL(Access Control List,访问控制列表)
- 以状态 0、4、6、7 对应的动作为例,switch-case 类比:
switch (symbol) { case id: id 对应的动作; break; case (: ( 对应的动作; break; default: any 对应的动作; break; }
-
压缩 GOTO 表
- 与压缩 ACTION 表类似
- 为每个非终结符 A 构造一个数对的列表,(当前状态,下一状态)表示 GOTO[当前状态, A] = 下一状态
- 进一步压缩:将每个报错条目替换为该列表中最常用的非报错条目,当前状态设置为“any”
疑问
4.7.5
- 为什么产生式 \(S\' \rightarrow S\) 一定与 \(\$\) 相关联(attache)?
- 或者说,为什么 \(\$\) 对于 \(S\' \rightarrow S\) 是自发生成的?
参考
[1] 《编译原理》