├── README.md ├── cmd └── gen │ └── gen.go └── example.go /README.md: -------------------------------------------------------------------------------- 1 | # go-generics 2 | 3 | This is very very experimental 4 | 5 | ## Usage 6 | 7 | ``` 8 | $ gen example.go > out.go 9 | ``` 10 | 11 | ### Input 12 | ```go 13 | package main 14 | 15 | type T generics 16 | 17 | func foo(a, b T) T { 18 | if a < b { 19 | return b 20 | } 21 | return a 22 | } 23 | 24 | type F struct { 25 | } 26 | 27 | func main() { 28 | println(foo[T](1, 2)) 29 | println(foo(1.2, 2)) 30 | println(foo("foo", "bar")) 31 | println(foo("foo", F{})) 32 | println(foo("foo", &F{})) 33 | } 34 | ``` 35 | 36 | ### Output 37 | 38 | ```go 39 | package main 40 | 41 | type T interface{} 42 | 43 | func foo(a, b T) T 44 | 45 | type F struct { 46 | } 47 | 48 | func main() { 49 | println(foo[T](1, 2)) 50 | println(foo_of_float64_int64(1.2, 2)) 51 | println(foo_of_string_string("foo", "bar")) 52 | println(foo_of_string_F("foo", F{})) 53 | println(foo_of_string__F("foo", &F{})) 54 | } 55 | func foo_of_float64_int64(a float64, b int64) T { 56 | if a < b { 57 | return b 58 | } 59 | return a 60 | } 61 | func foo_of_string_string(a string, b string) T { 62 | if a < b { 63 | return b 64 | } 65 | return a 66 | } 67 | func foo_of_string_F(a string, b F) T { 68 | if a < b { 69 | return b 70 | } 71 | return a 72 | } 73 | func foo_of_string__F(a string, b *F) T { 74 | if a < b { 75 | return b 76 | } 77 | return a 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /cmd/gen/gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/ast" 7 | "go/parser" 8 | "go/printer" 9 | "go/token" 10 | "log" 11 | "os" 12 | //"reflect" 13 | "strings" 14 | ) 15 | 16 | func doGenerate(filename string) { 17 | var buf bytes.Buffer 18 | fset := token.NewFileSet() 19 | file, err := parser.ParseFile(fset, filename, nil, parser.ParseComments|parser.SpuriousErrors) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | typeMap := map[string]*ast.Ident{} 25 | funcMap := map[string]*ast.FuncDecl{} 26 | callMap := map[string][]*ast.CallExpr{} 27 | 28 | ast.Inspect(file, func(n ast.Node) bool { 29 | switch x := n.(type) { 30 | case *ast.FuncDecl: 31 | funcMap[x.Name.Name] = x 32 | case *ast.TypeSpec: 33 | if ident, ok := x.Type.(*ast.Ident); ok { 34 | if ident.Name == "generics" { 35 | typeMap[x.Name.Name] = ident 36 | ident.Name = "interface{}" 37 | } 38 | } 39 | case *ast.CallExpr: 40 | if ident, ok := x.Fun.(*ast.Ident); ok { 41 | callMap[ident.Name] = append(callMap[ident.Name], x) 42 | } 43 | } 44 | return true 45 | }) 46 | for _, v := range funcMap { 47 | if v.Type.Results != nil { 48 | found := false 49 | for _, w := range v.Type.Results.List { 50 | if ident, ok := w.Type.(*ast.Ident); ok { 51 | if _, ok := typeMap[ident.Name]; ok { 52 | found = true 53 | break 54 | } 55 | } 56 | } 57 | if found { 58 | for _, call := range callMap[v.Name.Name] { 59 | typeNames := []string{} 60 | for _, arg := range call.Args { 61 | name := "" 62 | switch a := arg.(type) { 63 | case *ast.BasicLit: 64 | name = strings.ToLower(a.Kind.String()) 65 | case *ast.UnaryExpr: 66 | name = "*" + a.X.(*ast.CompositeLit).Type.(*ast.Ident).Name 67 | case *ast.CompositeLit: 68 | name = a.Type.(*ast.Ident).Name 69 | default: 70 | } 71 | if name == "int" { 72 | name = "int64" 73 | } 74 | if name == "float" { 75 | name = "float64" 76 | } 77 | typeNames = append(typeNames, name) 78 | } 79 | typeName := strings.Replace(strings.Join(typeNames, "_"), "*", "_", -1) 80 | if typeName == "" { 81 | continue 82 | } 83 | name := v.Name.Name + "_of_" + typeName 84 | call.Fun.(*ast.Ident).Name = name 85 | 86 | ret := &ast.FieldList{} 87 | for n, p := range v.Type.Params.List { 88 | for m, nm := range p.Names { 89 | f := &ast.Field{} 90 | f.Names = []*ast.Ident{{ 91 | token.NoPos, 92 | nm.Name, 93 | ast.NewObj(ast.Fun, nm.Name), 94 | }} 95 | f.Type = &ast.Ident{ 96 | p.Pos(), 97 | typeNames[n+m], 98 | ast.NewObj(ast.Fun, typeNames[n+m]), 99 | } 100 | ret.List = append(ret.List, f) 101 | } 102 | } 103 | 104 | fd := &ast.FuncDecl{ 105 | Doc: nil, 106 | Recv: nil, 107 | Name: &ast.Ident{ 108 | token.NoPos, 109 | name, 110 | ast.NewObj(ast.Fun, name), 111 | }, 112 | Type: &ast.FuncType{ 113 | Func: token.NoPos, 114 | //Params: &ast.FieldList{0, nil, 0}, 115 | Params: ret, 116 | Results: v.Type.Results, 117 | }, 118 | Body: v.Body, 119 | } 120 | //rtrn := &ast.ReturnStmt{token.NoPos, nil} 121 | //fd.Body.List = append(fd.Body.List, rtrn) 122 | file.Decls = append(file.Decls, fd) 123 | } 124 | v.Body = nil 125 | } 126 | } 127 | } 128 | err = (&printer.Config{ 129 | Mode: printer.UseSpaces | printer.TabIndent, 130 | Tabwidth: 8, 131 | }).Fprint(&buf, fset, file) 132 | if err != nil { 133 | log.Fatal(err) 134 | } 135 | fmt.Println(buf.String()) 136 | } 137 | 138 | func main() { 139 | for _, arg := range os.Args[1:] { 140 | doGenerate(arg) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type T generics 4 | 5 | func foo(a, b T) T { 6 | if a < b { 7 | return b 8 | } 9 | return a 10 | } 11 | 12 | type F struct { 13 | } 14 | 15 | func main() { 16 | println(foo[T](1, 2)) 17 | println(foo(1.2, 2)) 18 | println(foo("foo", "bar")) 19 | println(foo("foo", F{})) 20 | println(foo("foo", &F{})) 21 | } 22 | --------------------------------------------------------------------------------