first commit

This commit is contained in:
Young Xu 2023-01-01 16:38:14 +08:00
commit 3c61d439e5
8 changed files with 224 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

11
README.md Normal file
View File

@ -0,0 +1,11 @@
# GOAST
> GoAst 是一个go文件解析器
提供能力:
- 获取所有的变量定义
- 获取所有的常量定义
- 获取所有的函数定义
- 获取所有的结构体定义
- 获取所有的方法定义

19
example.go Normal file
View File

@ -0,0 +1,19 @@
package goast
import (
_ "github.com/stretchr/testify"
)
var a = 1
const d = "world"
func hello() {
}
type c int
func (cc c) name() {
}

12
go.mod Normal file
View File

@ -0,0 +1,12 @@
module gitter.top/xuthus5/goast
go 1.18
require github.com/stretchr/testify v1.8.1
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

18
go.sum Normal file
View File

@ -0,0 +1,18 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

117
goast.go Normal file
View File

@ -0,0 +1,117 @@
package goast
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
)
type Parser struct {
source *os.File
}
type Defines struct {
packageName string
variable map[string]struct{}
constant map[string]struct{}
function map[string]struct{}
method map[string]struct{}
imports map[string]struct{}
types map[string]struct{}
}
// NewParser get new parser
func NewParser(file string) (*Parser, error) {
source, err := os.OpenFile(file, os.O_RDONLY, 0400)
if err != nil {
return nil, err
}
return &Parser{source: source}, nil
}
// Parse parse go file
func (ps *Parser) Parse() (*Defines, error) {
tokenSet := token.NewFileSet()
astFile, err := parser.ParseFile(tokenSet, "", ps.source, 0)
if err != nil {
return nil, err
}
//ast.Print(tokenSet, astFile)
var defines = &Defines{
variable: make(map[string]struct{}),
constant: make(map[string]struct{}),
function: make(map[string]struct{}),
method: make(map[string]struct{}),
imports: make(map[string]struct{}),
types: make(map[string]struct{}),
}
// get package name
defines.packageName = astFile.Name.Name
for _, decl := range astFile.Decls {
switch decl.(type) {
case *ast.GenDecl:
gen := decl.(*ast.GenDecl)
switch gen.Tok {
case token.IMPORT:
_, pkg := getKVFromImportSpec(gen.Specs[0].(*ast.ImportSpec))
defines.imports[pkg] = struct{}{}
case token.VAR:
key, _ := getKVFromValueSpec(gen.Specs[0].(*ast.ValueSpec))
defines.variable[key] = struct{}{}
case token.CONST:
key, _ := getKVFromValueSpec(gen.Specs[0].(*ast.ValueSpec))
defines.constant[key] = struct{}{}
case token.TYPE:
key, _ := getKVFromTypeSpec(gen.Specs[0].(*ast.TypeSpec))
defines.types[key] = struct{}{}
}
case *ast.FuncDecl:
funcDecl := decl.(*ast.FuncDecl)
if funcDecl.Recv == nil {
defines.function[funcDecl.Name.Name] = struct{}{}
} else {
methodType, _ := getMethodNameFromReceiveField(funcDecl.Recv)
name := fmt.Sprintf("%s.%s", methodType, funcDecl.Name.Name)
defines.method[name] = struct{}{}
}
}
}
return defines, nil
}
func (d *Defines) ExistVariable(name string) bool {
_, exist := d.variable[name]
return exist
}
func (d *Defines) ExistConstant(name string) bool {
_, exist := d.constant[name]
return exist
}
func (d *Defines) ExistFunction(name string) bool {
_, exist := d.function[name]
return exist
}
func (d *Defines) ExistMethod(name string) bool {
_, exist := d.method[name]
return exist
}
func (d *Defines) ExistImport(name string) bool {
_, exist := d.imports[name]
return exist
}
func (d *Defines) ExistType(name string) bool {
_, exist := d.types[name]
return exist
}

15
goast_test.go Normal file
View File

@ -0,0 +1,15 @@
package goast
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)
func TestParser_Parse(t *testing.T) {
parser, err := NewParser("./example.go")
assert.NoError(t, err)
defines, err := parser.Parse()
assert.NoError(t, err)
fmt.Printf("%+v", defines)
}

31
util.go Normal file
View File

@ -0,0 +1,31 @@
package goast
import "go/ast"
// getKVFromImportSpec get import name and package path from *ast.ImportSpec
func getKVFromImportSpec(impt *ast.ImportSpec) (string, string) {
name := impt.Name.Name
pkg := impt.Path.Value
return name, pkg
}
// getKVFromValueSpec get const/variable name and value from *ast.ValueSpec
func getKVFromValueSpec(spec *ast.ValueSpec) (string, string) {
name := spec.Names[0].Name
value := spec.Values[0].(*ast.BasicLit).Value
return name, value
}
// getKVFromTypeSpec get type name and base type from *ast.TypeSpec
func getKVFromTypeSpec(spec *ast.TypeSpec) (string, string) {
name := spec.Name.Name
value := spec.Type.(*ast.Ident).Name
return name, value
}
// getMethodNameFromReceiveField get method type and receiver name from Recv field
func getMethodNameFromReceiveField(recv *ast.FieldList) (string, string) {
methodType := recv.List[0].Type.(*ast.Ident).Name
receiver := recv.List[0].Names[0].Name
return methodType, receiver
}