diff --git a/mapgen/main.go b/mapgen/main.go index b335654..6c78f7b 100644 --- a/mapgen/main.go +++ b/mapgen/main.go @@ -1,15 +1,45 @@ package main import ( + "encoding/json" "fmt" "go/token" + "go/types" + "os" "golang.org/x/tools/go/packages" ) +type TypeSymbol string + +type VarSymbol struct { + Export bool `json:"export"` + VarType TypeSymbol `json:"var_type"` + Name string `json:"name"` +} + +type FunctionSymbol struct { + Export bool `json:"export"` + Name string `json:"name"` + Params map[string]VarSymbol `json:"params"` + ReturnTypes []TypeSymbol `json:"return_types"` +} + +type StructSymbol struct { + Export bool `json:"export"` + Name string `json:"name"` + Fields map[string]VarSymbol `json:"fields"` + Methods map[string]FunctionSymbol `json:"methods"` +} + +type TopLevelSymbolScope struct { + Structs map[string]StructSymbol `json:"structs"` + Functions map[string]FunctionSymbol `json:"functions"` +} + func main() { cfg := &packages.Config{ - Mode: packages.NeedTypes, + Mode: packages.NeedTypes | packages.NeedTypesInfo, } pkgs, err := packages.Load(cfg, "fmt") @@ -17,6 +47,15 @@ func main() { panic(err) } + if packages.PrintErrors(pkgs) > 0 { + os.Exit(1) + } + + result := TopLevelSymbolScope{ + Structs: make(map[string]StructSymbol), + Functions: make(map[string]FunctionSymbol), + } + for _, pkg := range pkgs { scope := pkg.Types.Scope() @@ -26,7 +65,95 @@ func main() { } obj := scope.Lookup(name) - fmt.Println(name, obj.Type()) + + switch obj := obj.(type) { + + case *types.Func: + fn := buildFunction(obj) + result.Functions[name] = fn + + case *types.TypeName: + if strct, ok := buildStruct(obj); ok { + result.Structs[name] = strct + } + } } } + + out, err := json.MarshalIndent(result, "", " ") + if err != nil { + panic(err) + } + + fmt.Println(string(out)) +} + +func buildFunction(fn *types.Func) FunctionSymbol { + sig := fn.Type().(*types.Signature) + + params := make(map[string]VarSymbol) + for i := 0; i < sig.Params().Len(); i++ { + p := sig.Params().At(i) + + name := p.Name() + if name == "" { + name = fmt.Sprintf("param%d", i) + } + + params[name] = VarSymbol{ + Export: true, + Name: name, + VarType: TypeSymbol(p.Type().String()), + } + } + + var returnTypes []TypeSymbol + for i := 0; i < sig.Results().Len(); i++ { + r := sig.Results().At(i) + returnTypes = append(returnTypes, TypeSymbol(r.Type().String())) + } + + return FunctionSymbol{ + Export: token.IsExported(fn.Name()), + Name: fn.Name(), + Params: params, + ReturnTypes: returnTypes, + } +} + +func buildStruct(tn *types.TypeName) (StructSymbol, bool) { + underlying := tn.Type().Underlying() + + strct, ok := underlying.(*types.Struct) + if !ok { + return StructSymbol{}, false + } + + fields := make(map[string]VarSymbol) + + for i := 0; i < strct.NumFields(); i++ { + f := strct.Field(i) + + fields[f.Name()] = VarSymbol{ + Export: token.IsExported(f.Name()), + Name: f.Name(), + VarType: TypeSymbol(f.Type().String()), + } + } + + methods := make(map[string]FunctionSymbol) + methodSet := types.NewMethodSet(tn.Type()) + + for i := 0; i < methodSet.Len(); i++ { + m := methodSet.At(i).Obj().(*types.Func) + + methods[m.Name()] = buildFunction(m) + } + + return StructSymbol{ + Export: token.IsExported(tn.Name()), + Name: tn.Name(), + Fields: fields, + Methods: methods, + }, true }