Golang 的语法分析主要流程由 cmd/compile/internal/gc/noder.go 文件完成,这一步是在词法分析之后,会将 Go 源文件转成 AST。先来看来表示AST的基本数据结构(仅列举 Node 与 Type 类型,其他的类型可参见源码)。
// Node表示AST中的结点,是一个基础结构,用于将整个源代码串联起来
type Node struct {
// *Node与Nodes字段用于构造树结构
Left *Node // Left/Right字段可用于保存赋值语句的左值与右值等
Right *Node
Ninit Nodes // Ninit可用于保存if/for等语句的初始化语句
Nbody Nodes // Nbody可用于保存函数体
List Nodes // List可保存函数调用参数,map, slice等结构初始化值
Rlist Nodes // Rlist可保存多重赋值的右值列表
// most nodes
Type *types.Type // 类型
Orig *Node // original form, for printing, and tracking copies of ONAMEs
// func
Func *Func // 若Node表示函数,则Func指定函数的定义
// ONAME, OTYPE, OPACK, OLABEL, some OLITERAL
Name *Name
Sym *types.Sym // 源码中的变量名,类型名,函数名等
E interface{}
Xoffset int64 //偏移
Pos src.XPos
flags bitset32
Esc uint16 // EscXXX
Op Op // Node的类型,Op值决定其他字含义
aux uint8 // 可用于保存内置函数make,len等的枚举值
}
// Type表示Go源码中的类型
type Type struct {
// Extra 包括特定类型的数据结构。具体的结构根据Etype的值判定,例如TMAP, TSLICE等。
Extra interface{}
// Width is the width of this Type in bytes.
Width int64 // valid if Align > 0
methods Fields // struct的方法
allMethods Fields // struct的方法和内嵌的struct的方法
Nod *Node
Orig *Type
Cache struct {
ptr *Type // *T, or nil
slice *Type // []T, or nil
}
Sym *Sym // 类型名
Vargen int32
Etype EType // 类型的枚举值,决定其它字段的含义
Align uint8
flags bitset8
}
Node 是基本的结点结构,用于表示整棵语法树。Type 表示 Go 中的类型。还有其它的结构没有列出来,具体的可以见源码。下面举几个例子。
1 包导入
import "fmt"
AST:
Pack 即 package,表示此棵语法树为包语法树。fmt 是要导入的包。Pkg 中另外一种结构,包含 fmt 包的基本信息。
2 类型定义
type Person struct{
ID int64
Name string
Age uint8
}
AST:

结构体定义相对复杂,但也并不难理解。ODCLTYPE 表示声明一个类型定义,OTYPE 代表一棵具体的类型语法树。
3 定义变量:
var number1 int32 = 100
AST:

变量定义的 AST 比较简单,但有两棵语法树,左边是声明,右边是初始化。ODCL 结构定义声明变量 number1,OAS 给变量 number1 赋值100。
4 函数定义
func add(a int32, b int32) (c int32){
...
}
AST:

函数定义的结构比较复杂,但并不难理解。函数类型包括入参与出参。入参由 List 保存,出参由 Rlist 保存。图中 OTFUNC 的 Left 子节点代表函数的接收者(本例中为空)。
再看下语法解析的流程。整棵语法树的构建由 noder.go:decls 函数开始,然后递归地进行。
func (p *noder) decls(decls []syntax.Decl) (l []*Node) {
var cs constState
for _, decl := range decls {
p.setlineno(decl)
switch decl := decl.(type) {
case *syntax.ImportDecl: // 包导入
p.importDecl(decl)
case *syntax.VarDecl: // 变量定义
l = append(l, p.varDecl(decl)...)
case *syntax.ConstDecl: // 常量定义
l = append(l, p.constDecl(decl, &cs)...)
case *syntax.TypeDecl: // 类型定义
l = append(l, p.typeDecl(decl))
case *syntax.FuncDecl: // 函数定义
l = append(l, p.funcDecl(decl))
default:
panic("unhandled Decl")
}
}
return
}
decls 函数由一个大的 for 循环组成。在循环中判定声明的类型,然后调用对应的函数进行处理。变量,常量,类型以及函数的声明和定义所生成的 Node 结构最终会被追加到 xtop。xtop 代表的就是最终生成的语法树。
func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) *Node {
switch stmt := stmt.(type) {
case *syntax.EmptyStmt: // 空
return nil
case *syntax.LabeledStmt: // label
return p.labeledStmt(stmt, fallOK)
case *syntax.BlockStmt: // 块语句
return ...
case *syntax.ExprStmt: // 表达式语句
return p.wrapname(stmt, p.expr(stmt.X))
case *syntax.SendStmt: // send
return p.nod(stmt, OSEND, p.expr(stmt.Chan), p.expr(stmt.Value))
case *syntax.DeclStmt: // 声明语句(类型,变量等的定义)
return liststmt(p.decls(stmt.DeclList))
case *syntax.AssignStmt: // 赋值语句
return ...
case *syntax.BranchStmt: //
return ...
case *syntax.CallStmt: // 函数调用,包括强制类型转换,例如int64(100)
return ...
case *syntax.ReturnStmt: // return
return ...
case *syntax.IfStmt: // if 语句
return p.ifStmt(stmt)
case *syntax.ForStmt: // for 语句
return p.forStmt(stmt)
case *syntax.SwitchStmt: // switch 语句
return p.switchStmt(stmt)
case *syntax.SelectStmt: // select 语句
return p.selectStmt(stmt)
}
panic("unhandled Stmt")
}
stmtFall 函数主要处理语句,例如赋值,函数调用,if/for/switch 等。它由一个不算很大的switch 组成。
func (p *noder) expr(expr syntax.Expr) *Node {
switch expr := expr.(type) {
case nil, *syntax.BadExpr:
return nil
case *syntax.Name: // 变量名,函数名等
return p.mkname(expr)
case *syntax.BasicLit: // 字面量,例如数字: 123,字符串:"abcdefg"
return nodlit(p.basicLit(expr))
case *syntax.CompositeLit: // map, slice, struct字面量
return ...
case *syntax.KeyValueExpr: // key: value键值对
// use position of expr.Key rather than of expr (which has position of ':')
return p.nod(expr.Key, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
case *syntax.FuncLit: // 函数字面量,例如 add = func (a int, b int) int {return a + b}
return p.funcLit(expr)
case *syntax.ParenExpr: // 括号表达式
return p.nod(expr, OPAREN, p.expr(expr.X), nil)
case *syntax.SelectorExpr: // 类似于a.b的表达式
return ...
case *syntax.IndexExpr: // 类似于a[b]的表达式
return p.nod(expr, OINDEX, p.expr(expr.X), p.expr(expr.Index))
case *syntax.SliceExpr: // slice表达式,类似于a[begin:end:cap]
return ...
case *syntax.AssertExpr: // 类型断言
return p.nod(expr, ODOTTYPE, p.expr(expr.X), p.typeExpr(expr.Type))
case *syntax.Operation: // 一元和二元表达式
return ...
case *syntax.CallExpr: // 函数调用表达式
return ...
case *syntax.ArrayType: // 数组类型
return ...
case *syntax.SliceType: // slice类型定义
return p.nod(expr, OTARRAY, nil, p.typeExpr(expr.Elem))
case *syntax.DotsType: // ...
return p.nod(expr, ODDD, p.typeExpr(expr.Elem), nil)
case *syntax.StructType: // struct 类型定义
return p.structType(expr)
case *syntax.InterfaceType: // 接口类型定义
return p.interfaceType(expr)
case *syntax.FuncType: // 函数定义
return p.signature(nil, expr)
case *syntax.MapType: // map 定义
return p.nod(expr, OTMAP, p.typeExpr(expr.Key), p.typeExpr(expr.Value))
case *syntax.ChanType: // chan 定义
n := p.nod(expr, OTCHAN, p.typeExpr(expr.Elem), nil)
n.SetTChanDir(p.chanDir(expr.Dir))
return n
case *syntax.TypeSwitchGuard: // type switch
return ...
}
panic("unhandled Expr")
}
expr 函数也是由一个不算很长的 switch 语句组成。若对 Go 语法比较熟悉的话,decls, stmtFall, expr 等函数的处理流程还是比较好理解的,按照声明,语句,表达式逐步处理。生成的 xtop 语法树为下一步的类型检查做好了准备。