空语句块
func main() {fset := token.NewFileSet()f, err := parser.ParseFile(fset, "hello.go", src, parser.AllErrors)if err != nil {log.Fatal(err)return}ast.Print(nil, f.Decls[0].(*ast.FuncDecl).Body)}const src = `package pkgnamefunc main() {}`---------------------------output-------------------------0 *ast.BlockStmt {1 . Lbrace: 292 . Rbrace: 303 }
函数的声明由ast.FuncDecl结构体定义,其中的Body成员是ast.BlockStmt类型。ast.BlockStmt类型的定义如下:
type Stmt interface {Node// contains filtered or unexported methods}type BlockStmt struct {Lbrace token.Pos // position of "{"List []StmtRbrace token.Pos // position of "}"}
因为由大括弧定义的语句块也是一种合法的语句,因此我们可以在函数体再定义任意个空的语句块:
func main() {{}{}}
再次分析函数体的语法树,可以得到以下的结果:
0 *ast.BlockStmt {1 . Lbrace: 292 . List: []ast.Stmt (len = 2) {3 . . 0: *ast.BlockStmt {4 . . . Lbrace: 325 . . . Rbrace: 336 . . }7 . . 1: *ast.BlockStmt {8 . . . Lbrace: 369 . . . Rbrace: 3710 . . }11 . }12 . Rbrace: 3913 }
其中List部分有两个新定义的语句块,每个语句块依然是ast.BlockStmt类型。
返回语句
func main() {return 42, err}
当然,按照Go语言规范main函数是没有返回值的,因此return语句也不能有返回值。不过我们目前还处在语法树解析阶段,并不会检查返回语句和函数的返回值类型是否匹配,这种类型匹配检查要在语法树构建之后才会进行。
main函数体的语法树结果如下:
0 *ast.BlockStmt {1 . Lbrace: 292 . List: []ast.Stmt (len = 1) {3 . . 0: *ast.ReturnStmt {4 . . . Return: 325 . . . Results: []ast.Expr (len = 2) {6 . . . . 0: *ast.BasicLit {7 . . . . . ValuePos: 398 . . . . . Kind: INT9 . . . . . Value: "42"10 . . . . }11 . . . . 1: *ast.Ident {12 . . . . . NamePos: 4313 . . . . . Name: "err"14 . . . . }15 . . . }16 . . }17 . }18 . Rbrace: 4719 }
返回语句由ast.ReturnStmt类型表示,其中Results成员对应返回值列表,这里分别是基础的数值常量42和标识符err。ast.ReturnStmt类型定义如下:
type ReturnStmt struct {Return token.Pos // position of "return" keywordResults []Expr // result expressions; or nil}
其中Return成员表示return关键字的位置,Results成员对应一个表达式列表,如果为nil表示没有返回值。
声明语句
func main() {var a int}
语法树解析输出如下:
0 *ast.BlockStmt {1 . Lbrace: 292 . List: []ast.Stmt (len = 1) {3 . . 0: *ast.DeclStmt {4 . . . Decl: *ast.GenDecl {5 . . . . TokPos: 326 . . . . Tok: var7 . . . . Lparen: 08 . . . . Specs: []ast.Spec (len = 1) {9 . . . . . 0: *ast.ValueSpec {10 . . . . . . Names: []*ast.Ident (len = 1) {11 . . . . . . . 0: *ast.Ident {12 . . . . . . . . NamePos: 3613 . . . . . . . . Name: "a"14 . . . . . . . . Obj: *ast.Object {...}20 . . . . . . . }21 . . . . . . }22 . . . . . . Type: *ast.Ident {23 . . . . . . . NamePos: 3824 . . . . . . . Name: "int"25 . . . . . . }26 . . . . . }27 . . . . }28 . . . . Rparen: 029 . . . }30 . . }31 . }32 . Rbrace: 4233 }
声明的变量在ast.DeclStmt结构体中表示,结构体定义如下:
type DeclStmt struct {Decl Decl // *GenDecl with CONST, TYPE, or VAR token}
短声明和多赋值语句
func main() {a, b := 1, 2}
输出的语法树结果如下:
0 *ast.BlockStmt {1 . Lbrace: 292 . List: []ast.Stmt (len = 1) {3 . . 0: *ast.AssignStmt {4 . . . Lhs: []ast.Expr (len = 2) {5 . . . . 0: *ast.Ident {6 . . . . . NamePos: 327 . . . . . Name: "a"8 . . . . . Obj: *ast.Object {9 . . . . . . Kind: var10 . . . . . . Name: "a"11 . . . . . . Decl: *(obj @ 3)12 . . . . . }13 . . . . }14 . . . . 1: *ast.Ident {15 . . . . . NamePos: 3516 . . . . . Name: "b"17 . . . . . Obj: *ast.Object {18 . . . . . . Kind: var19 . . . . . . Name: "b"20 . . . . . . Decl: *(obj @ 3)21 . . . . . }22 . . . . }23 . . . }24 . . . TokPos: 3725 . . . Tok: :=26 . . . Rhs: []ast.Expr (len = 2) {27 . . . . 0: *ast.BasicLit {28 . . . . . ValuePos: 4029 . . . . . Kind: INT30 . . . . . Value: "1"31 . . . . }32 . . . . 1: *ast.BasicLit {33 . . . . . ValuePos: 4334 . . . . . Kind: INT35 . . . . . Value: "2"36 . . . . }37 . . . }38 . . }39 . }40 . Rbrace: 4541 }
短声明和多赋值语句都通过ast.AssignStmt结构体表达,其定义如下:
type AssignStmt struct {Lhs []ExprTokPos token.Pos // position of TokTok token.Token // assignment token, DEFINERhs []Expr}
其中Lhs表示左边的表达式或标识符列表,而Rhs表示右边的表达式列表。短声明和多赋值语句是通过Tok来进行区分。
if/else分支语句
func main() {if true {} else {}}
输出的语法树如下:
0 *ast.BlockStmt {1 . Lbrace: 292 . List: []ast.Stmt (len = 1) {3 . . 0: *ast.IfStmt {4 . . . If: 325 . . . Cond: *ast.Ident {6 . . . . NamePos: 357 . . . . Name: "true"8 . . . }9 . . . Body: *ast.BlockStmt {10 . . . . Lbrace: 4011 . . . . Rbrace: 4112 . . . }13 . . . Else: *ast.BlockStmt {14 . . . . Lbrace: 4815 . . . . Rbrace: 4916 . . . }17 . . }18 . }19 . Rbrace: 5120 }
if由ast.IfStmt结构体表示,其中的Cond为分支的条件表达式,Body为分支的主体语句块,Else为补充的语句块。ast.IfStmt结构体完整定义如下:
type IfStmt struct {If token.Pos // position of "if" keywordInit Stmt // initialization statement; or nilCond Expr // conditionBody *BlockStmtElse Stmt // else branch; or nil}
for循环
for语法对应下面四种
for {}for true {}for i := 0; true; i++ {}for i, v := range m {}
以上四个循环语句可以再次归纳为以下两种:
for x; y; z {}for x, y := range z {}
因此我们先分析经典风格的for x; y; z {}循环:
func main() {for x; y; z {}}
其语法树结构如下:
0 *ast.BlockStmt {1 . Lbrace: 292 . List: []ast.Stmt (len = 1) {3 . . 0: *ast.ForStmt {4 . . . For: 325 . . . Init: *ast.ExprStmt {6 . . . . X: *ast.Ident {7 . . . . . NamePos: 368 . . . . . Name: "x"9 . . . . }10 . . . }11 . . . Cond: *ast.Ident {12 . . . . NamePos: 3913 . . . . Name: "y"14 . . . }15 . . . Post: *ast.ExprStmt {16 . . . . X: *ast.Ident {17 . . . . . NamePos: 4218 . . . . . Name: "z"19 . . . . }20 . . . }21 . . . Body: *ast.BlockStmt {22 . . . . Lbrace: 4423 . . . . Rbrace: 4524 . . . }25 . . }26 . }27 . Rbrace: 4728 }
ast.ForStmt结构体表示经典的for循环,其中Init、Cond、Post和Body分别对应初始化语句、条件语句、迭代语句和循环体语句。ast.ForStmt结构体的定义如下:
type ForStmt struct {For token.Pos // position of "for" keywordInit Stmt // initialization statement; or nilCond Expr // condition; or nilPost Stmt // post iteration statement; or nilBody *BlockStmt}
在了解了经典风格的循环之后,我们再来看看最简单的for range循环:
func main() {for range ch {}}
我们省略来循环中的Key和Value部分。其语法树如下:
0 *ast.BlockStmt {1 . Lbrace: 292 . List: []ast.Stmt (len = 1) {3 . . 0: *ast.RangeStmt {4 . . . For: 325 . . . TokPos: 06 . . . Tok: ILLEGAL7 . . . X: *ast.Ident {8 . . . . NamePos: 429 . . . . Name: "ch"10 . . . }11 . . . Body: *ast.BlockStmt {12 . . . . Lbrace: 4513 . . . . Rbrace: 4614 . . . }15 . . }16 . }17 . Rbrace: 4818 }
for range循环的语法树由ast.RangeStmt结构表示,其完整定义如下:
type RangeStmt struct {For token.Pos // position of "for" keywordKey, Value Expr // Key, Value may be nilTokPos token.Pos // position of Tok; invalid if Key == nilTok token.Token // ILLEGAL if Key == nil, ASSIGN, DEFINEX Expr // value to range overBody *BlockStmt}
其中Key和Value对应循环时的迭代位置和值,X成员是生成要循环对象的表达式(可能是数组、切片、map和管道等),Body表示循环体语句块。另外,Tok成员可以区别Key和Value是多赋值语句还是短变量声明语句。
类型断言
func main() {x.(int)}
对x做类型断言,如果成功则返回x里面存储的int类型的值,如果失败则抛出异常。生成的语法树如下:
0 *ast.BlockStmt {1 . Lbrace: 292 . List: []ast.Stmt (len = 1) {3 . . 0: *ast.ExprStmt {4 . . . X: *ast.TypeAssertExpr {5 . . . . X: *ast.Ident {6 . . . . . NamePos: 327 . . . . . Name: "x"8 . . . . }9 . . . . Lparen: 3410 . . . . Type: *ast.Ident {11 . . . . . NamePos: 3512 . . . . . Name: "int"13 . . . . }14 . . . . Rparen: 3815 . . . }16 . . }17 . }18 . Rbrace: 4019 }
需要注意语法树的结构:首先是ast.ExprStmt结构体表示的表达式语句,其中的X成员才是对应类型断言表达式。类型断言由ast.TypeAssertExpr结构体表示,其定义如下:
type TypeAssertExpr struct {X Expr // expressionLparen token.Pos // position of "("Type Expr // asserted type; nil means type switch X.(type)Rparen token.Pos // position of ")"}
其中X成员是类型断言的主体表达式(产生一个接口值),Type成员是类型的表达式。如果Type为nil,则表示对应x.(type)形式的断言,否则是类型switch中使用的形式。
go和defer语句
go和defer语句在语法树中分别以ast.GoStmt和ast.DeferStmt结构定义:
type GoStmt struct {Go token.Pos // position of "go" keywordCall *CallExpr}type DeferStmt struct {Defer token.Pos // position of "defer" keywordCall *CallExpr}
func main() {go hello("光谷码农")}
其对应的语法树结果:
0 *ast.BlockStmt {1 . Lbrace: 292 . List: []ast.Stmt (len = 1) {3 . . 0: *ast.GoStmt {4 . . . Go: 325 . . . Call: *ast.CallExpr {6 . . . . Fun: *ast.Ident {7 . . . . . NamePos: 358 . . . . . Name: "hello"9 . . . . }10 . . . . Lparen: 4011 . . . . Args: []ast.Expr (len = 1) {12 . . . . . 0: *ast.BasicLit {13 . . . . . . ValuePos: 4114 . . . . . . Kind: STRING15 . . . . . . Value: "\"光谷码农\""16 . . . . . }17 . . . . }18 . . . . Ellipsis: 019 . . . . Rparen: 5520 . . . }21 . . }22 . }23 . Rbrace: 5724 }
