golang的源码中含有go fmt,go build等工具,这些工具都会涉及到对golang的源码的文件进行相关分析,因此,可以从这些工具的源代码入手,看看golang的代码是如何解析的,并将代码中的相关信息进行提取。
分析go fmt工具,入口: src/cmd/gofmt. 从main函数入口,通过查看源代码发现,可以通过go/parser的parse函数对文档进行解析,解析后返回的是一个ast.File的实例
type File struct {
Doc *CommentGroup // 文档注释
Package token.Pos // package位置
Name *Ident // package name
Decls []Decl // top-level declarations; or nil
Scope *Scope // package scope (this file only)
Imports []*ImportSpec // imports in this file
Unresolved []*Ident // unresolved identifiers in this file
Comments []*CommentGroup // list of all comments in the source file
}
其中,关键点在Decls,里面包含有const、var定义,结构体、函数等信息。
golang的parser函数从上到下进行解析token包中可以查看到golang的关键字、运算符等相关信息。
decl中包括const、var;type(struct,interface);func
下面是一个从代码中获取相关信息的例子(获取struct和field的注释信息)
func docStruct(fp string) {
fs, _ := parser.ParseFile(token.NewFileSet(), fp, nil, parser.ParseComments)
if fs.Name != nil {
if fs.Name.Obj != nil {
}
}
for _, item := range fs.Decls {
switch item.(type) {
case *ast.BadDecl:
case *ast.GenDecl:
decl := item.(*ast.GenDecl)
for _, subitem := range decl.Specs {
switch decl.Tok {
case token.IMPORT:
case token.CONST:
case token.TYPE:
spec := subitem.(*ast.TypeSpec)
switch spec.Type.(type) {
case *ast.Ident:
case *ast.ArrayType:
case *ast.StructType:
st := spec.Type.(*ast.StructType)
sinfo := StructInfo{
Name: spec.Name.Name,
Fields: StructFieldInfo(st),
}
dir := filepath.Dir(fp)
if strings.HasSuffix(dir, "inner") {
sinfo.Name = "inner_" + sinfo.Name
} else if strings.HasSuffix(dir, "models") {
sinfo.Name = "AE_" + sinfo.Name
} else {
continue
}
filestream.WriteString(fmt.Sprintf("UPDATE tableinfo SET pcomment='%s' WHERE pname='%s';\n", strings.TrimSpace(decl.Doc.Text()), sinfo.Name))
case *ast.StarExpr:
case *ast.ParenExpr:
case *ast.SelectorExpr:
}
case token.VAR:
}
}
case *ast.FuncDecl:
//fmt.Println(item.(*ast.FuncDecl).Doc.Text())
}
}
}
type StructInfo struct {
Name string
Fields []FieldInfo
}
type FieldInfo struct {
Name string
Tag string
Comment string
}
func StructFieldInfo(sinfo *ast.StructType) []FieldInfo {
if sinfo == nil || sinfo.Fields == nil {
return []FieldInfo{}
}
result := make([]FieldInfo, 0)
var info FieldInfo
for _, field := range sinfo.Fields.List {
info = FieldInfo{}
for _, fnames := range field.Names {
info.Name += fnames.Name
}
if field.Tag != nil {
info.Tag = field.Tag.Value
}
if field.Comment != nil {
info.Comment = strings.TrimSpace(field.Comment.Text())
}
result = append(result, info)
}
return result
}
//COMMENT ON COLUMN "private"."info"."Id" IS 'ceshi';