Chapter 4: Abstract Syntax
Parser 只判断句子是否属于某个语言,而编译器需要建立 abstract syntax tree,进行语义分析,生成 IR
4.1 Semantic Action
Semantic Action in Recursive Descent
- 在递归下降的 parser 里,语义分析是 parser 函数的返回值或副作用(或者兼有)
- 语义值的类型可以定义(union)
- 消除左递归后,只有右边的操作数,把左操作数传给右边
- 具体实现:保持一个 semantics values 的栈,reduce 时 pop 出相应的值
4.2 Abstract Parse Trees
上一节中,直接在 Yacc parser 里写语义分析难 read & maintain,只能按 parse 的顺序分析
最好先生成 parser tree,语义分析时遍历,将二者分开
解耦,可以先使用再声明
Parse Tree
Concrete parse tree (syntax):
- one leaf for each token
- one internal node for each grammar rule
- 问题:有些节点不需要,太依赖语法了,耦合度高
Abstract syntax(不是用来 parsing 的)
- 包含短语结构
Position
- 有的编译器词法、语法、语义分析同时进行,记录 current pos
- 有的编译器(Yacc)分三轮进行,需要记录中间符号的位置
Chapter 5: Semantic Analysis
语法分析的局限性:无法检测下面的错误
Semantic Analysis(语法分析)的作用
- 变量使用需要先声明
- 变量的类型是否正确(表达式、函数调用)
- 生成 IR
Symbol Tables
Symbol Tables 也被称为 environment
- Binding - 符号与类型进行绑定 e.g. a -> int
- environment - binding 的集合
- 进入新的作用域,新的同名变量会覆盖之前的
- 离开作用域后,binding 会结束
符号表需要的操作接口
- insert
- lookup
- beginScope
- endScope
不同语言的多个符号表(如多个类)
Implement
命令式
- 修改旧的环境
- 维护一个全局 stack,在离开环境时 undo
- 需要高效查找 - hash
- 轻松删除 - hash table with external chaining(插到链表头)
函数式
- 开个新的 environment
- 全部复制太耗时耗空间
- 用二分搜索树实现(字符串比较)
- 进入新环境只需要复制根节点
字符串比较耗时,转换成 symbol(int 值比较)
Type Checking
Recursive Declarations
相互调用的情况:f call g,g call f
- First pass - gathers information about the header
- Second pass - processes the bodies