AST 语法树说明
在Go语言中,AST(Abstract Syntax Tree)即抽象语法树。它是一种用于表示源代码结构的数据结构,通过对源代码的语法分析,可以生成一棵AST,用于表示源代码的语法结构。
在Go语言中,AST是通过Go语言的内置包go/ast
来实现的。该包提供了一系列类型和函数,可以用于生成和操作AST。
例如,下面是一段Go语言源代码:
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}
我们可以通过go/ast
包中的函数来生成这段代码的AST。
例如,可以使用parser.ParseFile
函数来解析一个源文件,并返回该文件的AST:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
// 解析源文件
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "example.go", nil, 0)
if err != nil {
panic(err)
}
// 遍历AST
ast.Inspect(f, func(n ast.Node) bool {
// 处理节点
fmt.Println(n)
return true
})
}
局部输出
&{<nil> 1 main [0xc0000a20c0 0xc000092420] scope 0xc000010400 {
func main
}
[0xc000092210 0xc000092240 0xc000092270 0xc0000922a0] [token parser nil nil panic ast ast bool fmt true] []}
main
<nil>
&{<nil> 15 import 22 [0xc000092210 0xc000092240 0xc000092270 0xc0000922a0] 78}
&{<nil> <nil> 0xc0000aa020 <nil> 0}
&{28 STRING "fmt"}
上面的代码中,我们使用了parser.ParseFile
函数来解析一个源文件,并返回该文件的AST。然后,我们使用ast.Inspect
函数来遍历AST,并处理每个AST节点。在这个例子中,我们打印了每个节点的信息。
通过生成AST,我们可以对源代码的语法结构进行分析和操作。例如,可以通过遍历AST来寻找特定的节点,从而实现代码的查找、替换和重构等功能。
示例
解析结构体
- 创建名为src.go的文件,内容如下。稍后就来解析内容中的结构体
// package ast
package ast /* the name is ast */
import "fmt"
// StcA结构体说明
// descrpiton StcA
type StcA struct {
FA string `json:"fa" form:"fa"` //测试字段A
Fb string `json:"fb" form:"fb"`
}
type StcB struct {
A, B, C int // associated with a, b, c
// associated with x, y
x, y float64 // float values
z complex128 // complex value
}
- 创建测试文件main.go
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"strings"
"testing"
)
func main() {
src, _ := ioutil.ReadFile("src.go")
f := token.NewFileSet()
p, err := parser.ParseFile(f, "src.go", src, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
for _, v := range p.Decls {
var (
structComment string
structName string
fieldNumber int
)
if stc, ok := v.(*ast.GenDecl); ok && stc.Tok == token.TYPE {
//fmt.Println(stc.Tok) //import、type、struct...
if stc.Doc != nil {
structComment = strings.TrimRight(stc.Doc.Text(), "\n")
}
for _, spec := range stc.Specs {
if tp, ok := spec.(*ast.TypeSpec); ok {
structName = tp.Name.Name
fmt.Println("结构体名称:", structName)
fmt.Println("结构体注释:", structComment)
if stp, ok := tp.Type.(*ast.StructType); ok {
if !stp.Struct.IsValid() {
continue
}
fieldNumber = stp.Fields.NumFields()
fmt.Println("字段数:", fieldNumber)
for _, field := range stp.Fields.List {
var (
fieldName string
fieldType string
fieldTag string
fieldTagKind string
fieldComment string
)
//获取字段名
if len(field.Names) == 1 {
fieldName = field.Names[0].Name //等同于field.Names[0].String()) //获取名称方法2
} else if len(field.Names) > 1 {
for _, name := range field.Names {
fieldName = fieldName + name.String() + ","
}
}
if field.Tag != nil {
fieldTag = field.Tag.Value
fieldTagKind = field.Tag.Kind.String()
}
if field.Comment != nil {
fieldComment = strings.TrimRight(field.Comment.Text(), "\n")
}
if ft, ok := field.Type.(*ast.Ident); ok {
fieldType = ft.Name
}
fmt.Println("\t字段名:", fieldName)
fmt.Println("\t字段类型:", fieldType)
fmt.Println("\t标签:", fieldTag, "标签类型", fieldTagKind)
fmt.Println("\t字段注释:", fieldComment)
fmt.Println("\t----------")
}
}
}
}
}
}
}
输出:
结构体名称: StcA
结构体注释: StcA结构体说明
descrpiton StcA
字段数: 2
字段名: FA
字段类型: string
标签: `json:"fa" form:"fa"` 标签类型 STRING
字段注释: 测试字段A
----------
字段名: Fb
字段类型: string
标签: `json:"fb" form:"fb"` 标签类型 STRING
字段注释:
----------
结构体名称: StcB
结构体注释:
字段数: 6
字段名: A,B,C,
字段类型: int
标签: 标签类型
字段注释: associated with a, b, c
----------
字段名: x,y,
字段类型: float64
标签: 标签类型
字段注释: float values
----------
字段名: z
字段类型: complex128
标签: 标签类型
字段注释: complex value
----------