3B.3 数据结构
语法树相关的数据结构都在文件 $GCROOT/compile/internal/syntax/nodes.go
中,每个源文件对应一个如下结构体的实例:
该结构是syntax.Parse()
函数的返回结果,代表着一个源文件的 AST。其中重要的是 DeclList
, 其包含了该源文件中所有的声明,声明(Declaration)是 Go 源文件顶级作用域中的唯一合法结构,声明在其内部递归地包含其他结构体。
该文件中定义的各种数据结构与 Go 语言规范 中所定义的语言结构是一一对应的,在源代码中,很多数据结构的声明上面都有对应的文法。所有的数据结构可以归为如下几类:
3.3.1 声明(Declaration)
声明就是在程序里面说“我要创建一个 X, 他的名字是 Y”,其包含两件事情:一个是命名,一个是绑定。例如 Go 中的 var name string
就是一个变量声明,其在程序中取了一个名字:name, 然后将其与 string 这种类型绑定起来,翻译成前边的语言就是:我要创建一个字符串,他的名字是 name。注意 name := "Golang"
不仅包含了声明,而且包含了赋值语句(Statement),只是声明中的类型信息是隐式的。
Go Spec 中对声明的阐述为:
A declaration binds a non-blank identifier to a constant, type, variable, function, label, or package. Every identifier in a program must be declared. No identifier may be declared twice in the same block, and no identifier may be declared in both the file and package block.
由此可见,声明其实就是创建程序组件并为其命名的操作,有了这些程序组件作为积木,我们才能搭建出整个程序大厦。在 Go 语言源文件中,合法的顶级声明包括:常量、类型、变量、函数、方法 以及 package, 下面都是合法的声明:
在 nodes.go 中,每一种声明都有对应的数据结构,作为示例,我们在这里仅仅看一下函数声明的结构:
可以发现该结构体就是对文法的一种直接映射,其每个属性也代表了一个函数或者方法的某个代码块。
结构体 FuncDecl 可以表示函数、方法两类声明,实际上 Go 中函数与方法没有本质区别,方法与函数的区别在于方法的第一个参数默认绑定到调用的实例上,这与 Java 的 this
关键字是一样的。因为 Go 中函数是一类公民(First Class Citizen),所以我们甚至可以将方法赋值给变量,如下代码体现了方法与函数的等价性质:
3.3.2 表达式(Expression)
表达式代表的是可以对其进行求值运算的代码块,例如算术运算、函数调用、访问数组或者哈希表等。
Go Spec 中对表达式的阐述为:
An expression specifies the computation of a value by applying operators and functions to operands.
也就是说表达式代表的是一个计算,该计算过程通过将某种运算符(operator)应用到操作数(operand)、或者将函数应用到其参数上来完成。常见的运算符包括算术运算符、逻辑运算符、位运算符等,我们对使用这类运算符做“计算”的认知是很自然的,例如表达式 i + j
, 运算符(Operator)是 +
, 操作数(Operand)是 i
与 j
. 但对于编译器而言,运算符的范围更加宽泛,例如赋值语句的右边只能是一个表达式,形如 a := b
中的 b
必须有值返回才能将其赋给 a
. 例如如下代码:
赋值语句右侧以关键字 struct
开始,所以对于编译器而言 struct
就是一个运算符,其操作数就是紧跟着的 {}
所包围的代码块,编译器根据该运算符“计算”出了一个类型值,然后为该类型值初始化了一个实例赋值给 language
.
nodes.go 中定义的表达式类型比较多,包括如下种类:
字面量 所有的字面量都通过一个表达式来表示:
数据访问 例如访问数组元素、访问 map、访问 struct 的字段等。形如 t[i] 通过下标访问数据的表达式定义如下:
类型定义 所有的类型定义都是表达式,这里我们仅仅看一下 struct 类型的表达式结构:
类型表达式在类型检查时会用来构建具体的类型对象
这里只是简单列举了几类表达式,所有的表达式定义详见源文件。
3.3.3 语句(Statement)
如果说表达式是程序的计算单元的话,那么语句就是程序的控制单元。
Go Spec 中对语句的阐述为:
Statements control execution.
例如 for 控制循环,if…else… 控制分支选项等。我们看一下 if 语句的结构:
其他语句对应的结构体定义详见源文件。
3.3.4 其他
除了表示程序块的数据结构,该文件内还定义了一些辅助结构,包括:
针对不同类型的几个接口,例如
Node
,Decl
,Expr
,Stmt
等Group
通过括号
()
括起来的一组声明都指向同一个 Group 实例。例如如下代码中所有的常量声明都会指向一个 Group 实例:
最后更新于