gorgonia.org/gorgonia@v0.9.17/cmd/genapi/generatemonads.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "go/ast" 7 "go/parser" 8 "go/token" 9 "log" 10 "path" 11 "path/filepath" 12 "strings" 13 ) 14 15 type nametypePair struct { 16 name string 17 *ast.FuncType 18 } 19 20 func functions(decls []ast.Decl) (signatures []nametypePair) { 21 for _, decl := range decls { 22 switch d := decl.(type) { 23 case *ast.FuncDecl: 24 signatures = append(signatures, nametypePair{d.Name.Name, d.Type}) 25 default: 26 } 27 } 28 return 29 } 30 31 type strRepr struct { 32 name string 33 inTypes []string 34 retTypes []string 35 36 printName bool 37 } 38 39 func (s strRepr) String() string { 40 buf := new(bytes.Buffer) 41 buf.Write([]byte("func ")) 42 if s.printName { 43 buf.Write([]byte(s.name)) 44 } 45 46 buf.Write([]byte("(")) 47 for i, v := range s.inTypes { 48 buf.Write([]byte(v)) 49 if i < len(s.inTypes)-1 { 50 buf.Write([]byte(", ")) 51 } 52 } 53 buf.Write([]byte(") (")) 54 for i, v := range s.retTypes { 55 buf.Write([]byte(v)) 56 if i < len(s.retTypes)-1 { 57 buf.Write([]byte(", ")) 58 } 59 } 60 buf.Write([]byte(")")) 61 return buf.String() 62 } 63 64 func processSig(pair nametypePair) strRepr { 65 a := pair.FuncType 66 var inTypes, retTypes []string 67 if a.Params == nil { 68 goto next 69 } 70 for _, field := range a.Params.List { 71 names := len(field.Names) 72 typ := parseTypeExpr(field.Type) 73 if names == 0 { 74 inTypes = append(inTypes, typ) 75 continue 76 } 77 for i := 0; i < names; i++ { 78 inTypes = append(inTypes, typ) 79 } 80 } 81 next: 82 if a.Results == nil { 83 return strRepr{pair.name, inTypes, retTypes, true} 84 } 85 for _, field := range a.Results.List { 86 names := len(field.Names) 87 typ := parseTypeExpr(field.Type) 88 if names == 0 { 89 retTypes = append(retTypes, typ) 90 continue 91 } 92 for i := 0; i < names; i++ { 93 retTypes = append(retTypes, typ) 94 } 95 } 96 return strRepr{pair.name, inTypes, retTypes, true} 97 } 98 99 func parseTypeExpr(expr ast.Expr) string { 100 switch e := expr.(type) { 101 case *ast.Ident: 102 return e.Name 103 case *ast.StarExpr: 104 x := parseTypeExpr(e.X) 105 return "*" + x 106 case *ast.SelectorExpr: 107 return parseTypeExpr(e.X) + "." + e.Sel.Name 108 case *ast.Ellipsis: 109 return "..." + parseTypeExpr(e.Elt) 110 case *ast.ArrayType: 111 return "[]" + parseTypeExpr(e.Elt) 112 default: 113 return fmt.Sprintf("%T", expr) 114 } 115 } 116 117 func filterSigs(xs []strRepr, fn func(strRepr) bool) (retVal []strRepr) { 118 for _, x := range xs { 119 if fn(x) { 120 retVal = append(retVal, x) 121 } 122 } 123 return 124 } 125 126 func functionSignatures() { 127 files := path.Join(gorgonialoc, "*.go") 128 matches, err := filepath.Glob(files) 129 130 if err != nil { 131 log.Fatal(err) 132 } 133 fset := token.NewFileSet() 134 135 var allFns []strRepr 136 for _, f := range matches { 137 file, err := parser.ParseFile(fset, f, nil, parser.AllErrors) 138 if err != nil { 139 log.Fatal(err) 140 141 } 142 143 fns := functions(file.Decls) 144 for _, fn := range fns { 145 sig := processSig(fn) 146 sig.printName = false 147 if strings.Title(sig.name) == sig.name { 148 allFns = append(allFns, sig) 149 } 150 } 151 } 152 f := func(a strRepr) bool { 153 want := []string{"Nodes", "error"} 154 if len(a.retTypes) != len(want) { 155 return false 156 } 157 for i, v := range a.retTypes { 158 if v != want[i] { 159 return false 160 } 161 } 162 return true 163 } 164 165 signatures := make(map[string]int) 166 interesting := filterSigs(allFns, f) 167 for _, v := range interesting { 168 v.printName = true 169 signatures[fmt.Sprintf("%v", v)]++ 170 } 171 172 for k, v := range signatures { 173 fmt.Printf("%v\t%d\n", k, v) 174 } 175 }