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:

包导入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

变量定义的 AST 比较简单,但有两棵语法树,左边是声明,右边是初始化。ODCL 结构定义声明变量 number1,OAS 给变量 number1 赋值100。

4 函数定义

func add(a int32, b int32) (c int32){
    ...
}

AST:

函数定义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 语法树为下一步的类型检查做好了准备。