Semantic Analysis:语义分析¶
约 1213 个字 4 张图片 预计阅读时间 4 分钟
程序正确性的很多检查光靠词法语法是做不到的,例如等式左右两边类型不一致,变量未定义等。语义分析就是在词法分析和语法分析的基础上,进一步检查程序的语义是否正确。
语义分析的任务是:
- 通过 AST 来确定程序的一些静态属性,例如
- 作用域、命名可见性
- 变量类型、函数类型、表达式类型
- 将 AST 转换为中间表示(IR)
1. 符号表¶
1.1 概念¶
语义分析阶段实际上就是由环境(或符号表,符号表是环境的一种实现)的维护来驱动的,符号表的工作是将标识符映射到其类型与位置。
- Binding:为一个符号绑定意义,用 \(\mapsto\) 表示。
- Environment:一群 bindings 的集合,例如环境 \(\sigma_0 = \{ x \mapsto \text{int}, y \mapsto \text{bool} \}\)。
1.2 操作¶
先给一个符号表的例子:
可以看到:
- 每进入一个新的作用域(例如函数体),就会创建一个新的符号表。
- 右边的 bindings 会 override 左边的 bindings,也就是 X + Y 和 Y + X 是不同的。
- 当语义分析器到达每个 scope 的末尾时,此作用域的符号表会被 discard。
符号表的操作有:
- Insert:将一个符号插入到符号表中。
- Lookup:查找一个符号在符号表中的绑定。
- BeginScope:开始一个新的作用域,创建一个新的符号表。
- EndScope:结束当前作用域,回退到上一个符号表。
多个符号表
在一些语言中,同一时间可能有多个活跃的 environment,例如 Java 的每个 class 都有自己的一个符号表。
1.3 符号表的实现¶
符号表有两种实现风格:
- Imperative Style:命令式符号表
- 一直在本符号表上修改,遇到新的符号表也直接修改
- 记录创建新符号表后的操作,在 EndScope 时一直 undo 到创建新符号表前的状态
- Functional Style:函数式符号表
- 每次创建新符号表时,都会返回一个新的符号表,而不是修改原来的符号表
- 通过链表来实现,新的符号表指向旧的符号表
1.3.1 命令式符号表¶
- 为了实现快速的查找,命令式符号表通常使用哈希表来存储 bindings。
- 为了实现简易的删除,命令式符号表还会使用带有 external chaining 的哈希表。
- 当新的作用域覆盖原来作用域的某个符号的时候,直接把新的 binding 插入到 chaining 的头部。
- 当 EndScope 时,直接删除 chaining 的头部。
1.3.2 函数式符号表¶
- 可以使用哈希表来实现,但是不是特别搞笑
- 创建新符号表的时候,会 copy 所有的 array,但是共享 old bindings 的哈希表。
- 更常用的是二叉搜索树来实现
- 每个 node 是一个 binding
- 使用 string comparison 来做排序
- 插入:只需复制从 root 到待插入节点的父亲节点
- 查找:平衡情况下是 \(O(\log n)\)
2. Tiger 编译器中的符号¶
- 为了避免查找时候繁琐的 string comparison,可以把每个 string 绑定到一个整数 ID 上。
- 为了支持命令式符号表,引入一个 auxiliary stack 来记录符号表的变化。
3. 类型检查¶
3.1 Tiger 语言中的类型¶
- 基本类型:int, string
- 复合类型:array, record
- 使用
type
关键字类型定义的- nil:
Ty_Nil
- void:
Ty_Void
- 当处理递归定义的类型时,使用
Ty_Name
来表示递归类型,需要为 symbol 有一个 place-holder。
- nil:
3.2 类型相同的定义¶
- Name Equivalence:两个类型的名字相同,当且仅当两个类型拥有相同的类型名,且都是由相同的类型定义所定义的。
- Structural Equivalence:两个类型的结构相同,当且仅当两个类型的结构完全相同。
Tiger 语言中使用 Name Equivalence,具体可以看下面的图:
3.3 命名空间¶
Tiger 有两套分离的命名空间:
- Types
- Functions and Variables
这两套命名空间是分离的,意味着可以有同名的类型和函数/变量。
因此,Tiger 也需要维护两个环境,分别是:
- Type Environment:类型环境,维护类型的定义
- 将 symbol 映射到类型 objects
- Value Environment:值环境,维护变量和函数的定义
- 将变量 symbol 映射到所属类型 objects
- 将函数 symbol 映射到其参数与返回值类型 objects
3.4 类型检查的过程¶
这部分在实验中已经体会到了:
- 类型检查是一个递归的过程,遍历 AST 的每个节点。
- 检查表达式类型
transExp
函数负责检查表达式类型并更新符号表
- 检查声明类型
- Tiger 语言中所有声明都在
let
语句中
- Tiger 语言中所有声明都在