├── LICENSE ├── README.md ├── degen ├── api.go ├── conversions.go ├── degen.go └── inst.go ├── examples ├── chanutils │ ├── chanutils.go │ └── out.go ├── list │ ├── list.go │ └── out.go ├── mathutils │ ├── mathutils.go │ └── out.go ├── priorityqueue │ ├── out.go │ └── priorityqueue.go ├── sliceutils │ ├── out.go │ └── sliceutils.go └── syncmap │ ├── out.go │ └── syncmap.go ├── go.mod ├── go ├── ast │ ├── ast.go │ ├── commentmap.go │ ├── filter.go │ ├── import.go │ ├── print.go │ ├── resolve.go │ ├── scope.go │ └── walk.go ├── build │ ├── build.go │ ├── doc.go │ ├── read.go │ ├── syslist.go │ └── zcgo.go ├── constant │ └── value.go ├── doc │ ├── Makefile │ ├── comment.go │ ├── doc.go │ ├── example.go │ ├── exports.go │ ├── filter.go │ ├── headscan.go │ ├── reader.go │ └── synopsis.go ├── format │ ├── format.go │ └── internal.go ├── importer │ └── importer.go ├── internal │ ├── gccgoimporter │ │ ├── ar.go │ │ ├── gccgoinstallation.go │ │ ├── gccgoinstallation_test.go │ │ ├── importer.go │ │ ├── importer_test.go │ │ ├── parser.go │ │ ├── parser_test.go │ │ └── testdata │ │ │ ├── aliases.go │ │ │ ├── aliases.gox │ │ │ ├── complexnums.go │ │ │ ├── complexnums.gox │ │ │ ├── conversions.go │ │ │ ├── conversions.gox │ │ │ ├── escapeinfo.go │ │ │ ├── escapeinfo.gox │ │ │ ├── imports.go │ │ │ ├── imports.gox │ │ │ ├── issue27856.go │ │ │ ├── issue27856.gox │ │ │ ├── issue29198.go │ │ │ ├── issue29198.gox │ │ │ ├── libimportsar.a │ │ │ ├── nointerface.go │ │ │ ├── nointerface.gox │ │ │ ├── pointer.go │ │ │ ├── pointer.gox │ │ │ ├── time.gox │ │ │ ├── unicode.gox │ │ │ └── v1reflect.gox │ ├── gcimporter │ │ ├── bimport.go │ │ ├── exportdata.go │ │ ├── gcimporter.go │ │ ├── gcimporter_test.go │ │ ├── iimport.go │ │ └── testdata │ │ │ ├── a.go │ │ │ ├── b.go │ │ │ ├── exports.go │ │ │ ├── issue15920.go │ │ │ ├── issue20046.go │ │ │ ├── issue25301.go │ │ │ ├── issue25596.go │ │ │ ├── p.go │ │ │ └── versions │ │ │ ├── test.go │ │ │ ├── test_go1.11_0i.a │ │ │ ├── test_go1.11_6b.a │ │ │ ├── test_go1.11_999b.a │ │ │ ├── test_go1.11_999i.a │ │ │ ├── test_go1.7_0.a │ │ │ ├── test_go1.7_1.a │ │ │ ├── test_go1.8_4.a │ │ │ └── test_go1.8_5.a │ ├── srcimporter │ │ ├── srcimporter.go │ │ ├── srcimporter_test.go │ │ └── testdata │ │ │ ├── issue20855 │ │ │ └── issue20855.go │ │ │ ├── issue23092 │ │ │ └── issue23092.go │ │ │ └── issue24392 │ │ │ └── issue24392.go │ └── xcoff │ │ ├── ar.go │ │ ├── ar_test.go │ │ ├── file.go │ │ ├── file_test.go │ │ ├── testdata │ │ ├── bigar-empty │ │ ├── bigar-ppc64 │ │ ├── gcc-ppc32-aix-dwarf2-exec │ │ ├── gcc-ppc64-aix-dwarf2-exec │ │ ├── hello.c │ │ ├── printbye.c │ │ └── printhello.c │ │ └── xcoff.go ├── parser │ ├── interface.go │ └── parser.go ├── printer │ ├── nodes.go │ └── printer.go ├── scanner │ ├── errors.go │ └── scanner.go ├── token │ ├── position.go │ ├── serialize.go │ └── token.go └── types │ ├── api.go │ ├── assignments.go │ ├── builtins.go │ ├── call.go │ ├── check.go │ ├── conversions.go │ ├── decl.go │ ├── errors.go │ ├── eval.go │ ├── expr.go │ ├── exprstring.go │ ├── gotype.go │ ├── initorder.go │ ├── labels.go │ ├── lookup.go │ ├── methodset.go │ ├── object.go │ ├── objset.go │ ├── operand.go │ ├── ordering.go │ ├── package.go │ ├── predicates.go │ ├── resolver.go │ ├── return.go │ ├── scope.go │ ├── selection.go │ ├── sizes.go │ ├── stmt.go │ ├── type.go │ ├── typestring.go │ ├── typexpr.go │ └── universe.go └── main.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Michal Štrba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /degen/api.go: -------------------------------------------------------------------------------- 1 | package degen 2 | 3 | import ( 4 | "fmt" 5 | "github.com/faiface/generics/go/ast" 6 | "github.com/faiface/generics/go/importer" 7 | "github.com/faiface/generics/go/token" 8 | "github.com/faiface/generics/go/types" 9 | ) 10 | 11 | func Degen(fset *token.FileSet, input *ast.File, debug bool) (output *ast.File, changed bool) { 12 | typesCfg := &types.Config{ 13 | Importer: importer.Default(), 14 | } 15 | info := &types.Info{ 16 | Types: make(map[ast.Expr]types.TypeAndValue), 17 | Defs: make(map[*ast.Ident]types.Object), 18 | Uses: make(map[*ast.Ident]types.Object), 19 | GenericCalls: make(map[*ast.CallExpr]*types.GenericCall), 20 | GenericInstances: make(map[*ast.CallExpr]*types.GenericInstance), 21 | } 22 | _, err := typesCfg.Check("", fset, []*ast.File{input}, info) 23 | if err != nil && debug { 24 | fmt.Println(err) 25 | } 26 | 27 | output = &ast.File{ 28 | Name: input.Name, 29 | Imports: input.Imports, 30 | } 31 | 32 | cfg := &config{ 33 | info: info, 34 | instantiated: make(map[string]bool), 35 | input: input, 36 | output: output, 37 | } 38 | 39 | for _, decl := range input.Decls { 40 | switch decl := decl.(type) { 41 | case *ast.FuncDecl: 42 | if decl.Recv.NumFields() == 0 { 43 | cfg.instantiated[decl.Name.Name] = true 44 | } 45 | case *ast.GenDecl: 46 | for _, spec := range decl.Specs { 47 | switch spec := spec.(type) { 48 | case *ast.TypeSpec: 49 | cfg.instantiated[spec.Name.Name] = true 50 | } 51 | } 52 | } 53 | } 54 | 55 | for _, decl := range input.Decls { 56 | switch decl := decl.(type) { 57 | case *ast.FuncDecl: 58 | if len(decl.TypeParams) > 0 { 59 | output.Decls = append(output.Decls, decl) 60 | continue 61 | } 62 | changed = degenFuncDecl(cfg, decl) || changed 63 | 64 | case *ast.GenDecl: 65 | if decl.Tok != token.TYPE { 66 | output.Decls = append(output.Decls, decl) 67 | continue 68 | } 69 | 70 | for _, spec := range decl.Specs { 71 | spec := spec.(*ast.TypeSpec) 72 | 73 | if len(spec.Params) > 0 { 74 | output.Decls = append(output.Decls, decl) 75 | continue 76 | } 77 | 78 | changed = degenTypeSpec(cfg, spec) || changed 79 | } 80 | 81 | default: 82 | output.Decls = append(output.Decls, decl) 83 | } 84 | } 85 | 86 | return output, changed 87 | } 88 | 89 | type config struct { 90 | info *types.Info 91 | instantiated map[string]bool 92 | input *ast.File 93 | output *ast.File 94 | } 95 | -------------------------------------------------------------------------------- /degen/conversions.go: -------------------------------------------------------------------------------- 1 | package degen 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "github.com/faiface/generics/go/ast" 7 | "github.com/faiface/generics/go/token" 8 | "github.com/faiface/generics/go/types" 9 | ) 10 | 11 | func tupleToFieldList(t *types.Tuple) *ast.FieldList { 12 | var fields ast.FieldList 13 | for i := 0; i < t.Len(); i++ { 14 | v := t.At(i) 15 | field := &ast.Field{ 16 | Type: typeToExpr(v.Type()), 17 | } 18 | if v.Name() != "" { 19 | field.Names = []*ast.Ident{{Name: v.Name()}} 20 | } 21 | fields.List = append(fields.List, field) 22 | } 23 | return &fields 24 | } 25 | 26 | func typeToExpr(t types.Type) ast.Expr { 27 | switch t := t.(type) { 28 | case nil: 29 | return &ast.BadExpr{} 30 | 31 | case *types.Basic: 32 | return &ast.Ident{ 33 | Name: t.Name(), 34 | } 35 | 36 | case *types.Array: 37 | return &ast.ArrayType{ 38 | Len: &ast.BasicLit{ 39 | Kind: token.INT, 40 | Value: fmt.Sprint(t.Len()), 41 | }, 42 | Elt: typeToExpr(t.Elem()), 43 | } 44 | 45 | case *types.Slice: 46 | return &ast.ArrayType{ 47 | Elt: typeToExpr(t.Elem()), 48 | } 49 | 50 | case *types.Struct: 51 | var fields ast.FieldList 52 | for i := 0; i < t.NumFields(); i++ { 53 | v := t.Field(i) 54 | field := &ast.Field{ 55 | Type: typeToExpr(v.Type()), 56 | } 57 | if v.Name() != "" { 58 | field.Names = []*ast.Ident{{Name: v.Name()}} 59 | } 60 | fields.List = append(fields.List, field) 61 | } 62 | return &ast.StructType{ 63 | Fields: &fields, 64 | } 65 | 66 | case *types.Pointer: 67 | return &ast.StarExpr{ 68 | X: typeToExpr(t.Elem()), 69 | } 70 | 71 | case *types.Tuple: 72 | return &ast.BadExpr{} 73 | 74 | case *types.Signature: 75 | if len(t.TypeParams()) > 0 { 76 | return &ast.BadExpr{} 77 | } 78 | return &ast.FuncType{ 79 | Params: tupleToFieldList(t.Params()), 80 | Results: tupleToFieldList(t.Results()), 81 | } 82 | 83 | case *types.Interface: 84 | var methods ast.FieldList 85 | for i := 0; i < t.NumMethods(); i++ { 86 | meth := t.Method(i) 87 | methods.List = append(methods.List, &ast.Field{ 88 | Names: []*ast.Ident{{Name: meth.Name()}}, 89 | Type: typeToExpr(meth.Type()), 90 | }) 91 | } 92 | return &ast.InterfaceType{ 93 | Methods: &methods, 94 | } 95 | 96 | case *types.Map: 97 | return &ast.MapType{ 98 | Key: typeToExpr(t.Key()), 99 | Value: typeToExpr(t.Elem()), 100 | } 101 | 102 | case *types.Chan: 103 | dir := map[types.ChanDir]ast.ChanDir{ 104 | types.SendRecv: ast.SEND | ast.RECV, 105 | types.SendOnly: ast.SEND, 106 | types.RecvOnly: ast.RECV, 107 | } 108 | return &ast.ChanType{ 109 | Dir: dir[t.Dir()], 110 | Value: typeToExpr(t.Elem()), 111 | } 112 | 113 | case *types.Named: 114 | return &ast.Ident{ 115 | Name: t.Obj().Name(), 116 | } 117 | 118 | case *types.TypeParam: 119 | return &ast.BadExpr{} 120 | 121 | default: 122 | return &ast.BadExpr{} 123 | } 124 | } 125 | 126 | func writeType(w io.Writer, t types.Type) { 127 | switch t := t.(type) { 128 | case nil: 129 | fmt.Fprintf(w, "bad") 130 | 131 | case *types.Basic: 132 | fmt.Fprintf(w, "%s", t.Name()) 133 | 134 | case *types.Array: 135 | fmt.Fprintf(w, "array_%d_", t.Len()) 136 | writeType(w, t.Elem()) 137 | 138 | case *types.Slice: 139 | fmt.Fprintf(w, "slice_") 140 | writeType(w, t.Elem()) 141 | 142 | case *types.Struct: 143 | fmt.Fprint(w, "struct_") 144 | for i := 0; i < t.NumFields(); i++ { 145 | field := t.Field(i) 146 | fmt.Fprintf(w, "%s_", field.Name()) 147 | writeType(w, field.Type()) 148 | fmt.Fprintf(w, "_") 149 | } 150 | fmt.Fprintf(w, "end") 151 | 152 | case *types.Pointer: 153 | fmt.Fprintf(w, "ptr_") 154 | writeType(w, t.Elem()) 155 | 156 | case *types.Tuple: 157 | fmt.Fprintf(w, "bad") 158 | 159 | case *types.Signature: 160 | if len(t.TypeParams()) > 0 { 161 | fmt.Fprintf(w, "bad") 162 | return 163 | } 164 | fmt.Fprint(w, "func_") 165 | for i := 0; i < t.Params().Len(); i++ { 166 | param := t.Params().At(i) 167 | writeType(w, param.Type()) 168 | fmt.Fprintf(w, "_") 169 | } 170 | fmt.Fprint(w, "to_") 171 | for i := 0; i < t.Results().Len(); i++ { 172 | result := t.Results().At(i) 173 | writeType(w, result.Type()) 174 | fmt.Fprintf(w, "_") 175 | } 176 | fmt.Fprintf(w, "end") 177 | 178 | case *types.Interface: 179 | fmt.Fprintf(w, "interface_") 180 | for i := 0; i < t.NumMethods(); i++ { 181 | meth := t.Method(i) 182 | fmt.Fprintf(w, "%s_", meth.Name()) 183 | writeType(w, meth.Type()) 184 | fmt.Fprintf(w, "_") 185 | } 186 | fmt.Fprintf(w, "end") 187 | 188 | case *types.Map: 189 | fmt.Fprintf(w, "map_") 190 | writeType(w, t.Key()) 191 | fmt.Fprintf(w, "_") 192 | writeType(w, t.Elem()) 193 | 194 | case *types.Chan: 195 | dir := map[types.ChanDir]string{ 196 | types.SendRecv: "both", 197 | types.SendOnly: "send", 198 | types.RecvOnly: "recv", 199 | } 200 | fmt.Fprintf(w, "chan_%s_", dir[t.Dir()]) 201 | writeType(w, t.Elem()) 202 | 203 | case *types.Named: 204 | fmt.Fprintf(w, "%s", t.Obj().Name()) 205 | 206 | case *types.TypeParam: 207 | fmt.Fprintf(w, "bad") 208 | 209 | default: 210 | fmt.Fprintf(w, "bad") 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /examples/chanutils/chanutils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Elems returns a channel that streams all the supplied elements one by one. 6 | func Elems(a ...type T) <-chan T { 7 | ch := make(chan T) 8 | go func() { 9 | for _, x := range a { 10 | ch <- x 11 | } 12 | close(ch) 13 | }() 14 | return ch 15 | } 16 | 17 | // Pipe redirects all the data from one channel to another one. 18 | func Pipe(from <-chan type T, to chan<- T) { 19 | for x := range from { 20 | to <- x 21 | } 22 | close(to) 23 | } 24 | 25 | // Map transforms a channel of type T into a channel of type U by transforming each of the 26 | // values sent on the channel using the supplied function. 27 | func Map(in <-chan type T, f func(T) type U) <-chan U { 28 | out := make(chan U) 29 | go func() { 30 | for x := range in { 31 | out <- f(x) 32 | } 33 | close(out) 34 | }() 35 | return out 36 | } 37 | 38 | // Merge merges all the supplied channels into a single channel. Any value sent by any of 39 | // the original channels will appear on the merged channel. The returned channel gets closed when 40 | // all of the supplied channels get closed. 41 | func Merge(chans ...<-chan type T) <-chan T { 42 | merged := make(chan T) 43 | done := make(chan bool) 44 | 45 | for _, ch := range chans { 46 | ch := ch 47 | go func() { 48 | for x := range ch { 49 | merged <- x 50 | } 51 | done <- true 52 | }() 53 | } 54 | 55 | go func() { 56 | for range chans { 57 | <-done 58 | } 59 | close(merged) 60 | }() 61 | 62 | return merged 63 | } 64 | 65 | func main() { 66 | letters := Elems("A", "B", "C", "D", "E") 67 | numbers := Elems(1, 2, 3, 4, 5) 68 | 69 | everything := Merge(letters, Map(numbers, func(x int) string { 70 | return fmt.Sprint(x) 71 | })) 72 | 73 | for s := range everything { 74 | fmt.Println(s) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /examples/chanutils/out.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func Elems_string(a ...string) <-chan string { 6 | ch := make(chan string) 7 | go func() { 8 | for _, x := range a { 9 | ch <- x 10 | } 11 | 12 | close(ch) 13 | }() 14 | return ch 15 | } 16 | 17 | func Elems_int(a ...int) <-chan int { 18 | ch := make(chan int) 19 | go func() { 20 | for _, x := range a { 21 | ch <- x 22 | } 23 | 24 | close(ch) 25 | }() 26 | return ch 27 | } 28 | 29 | func Map_int_string(in <-chan int, f func(int) string) <-chan string { 30 | out := make(chan string) 31 | go func() { 32 | for x := range in { 33 | out <- f(x) 34 | } 35 | 36 | close(out) 37 | }() 38 | return out 39 | } 40 | 41 | func Merge_string(chans ...<-chan string) <-chan string { 42 | merged := make(chan string) 43 | done := make(chan bool) 44 | for _, ch := range chans { 45 | ch := ch 46 | go func() { 47 | for x := range ch { 48 | merged <- x 49 | } 50 | 51 | done <- true 52 | }() 53 | } 54 | go func() { 55 | for range chans { 56 | <-done 57 | } 58 | 59 | close(merged) 60 | }() 61 | return merged 62 | } 63 | func main() { 64 | letters := Elems_string("A", "B", "C", "D", "E") 65 | numbers := Elems_int(1, 2, 3, 4, 5) 66 | 67 | everything := Merge_string(letters, Map_int_string(numbers, func(x int) string { return fmt.Sprint(x) })) 68 | for s := range everything { 69 | fmt.Println(s) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/list/list.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | // List is a generic singly-linked list. Just like in LISP. 9 | type List(type T) struct { 10 | First T 11 | Rest *List(T) 12 | } 13 | 14 | // Empty returns an empty list of type T. 15 | func Empty(type T) *List(T) { 16 | return nil 17 | } 18 | 19 | // Prepend returns a new list with x prepended before l. 20 | func (l *List(type T)) Prepend(x T) *List(T) { 21 | return &List(T){ 22 | First: x, 23 | Rest: l, 24 | } 25 | } 26 | 27 | // Elems constructs a linked list containing the given elements. 28 | func Elems(xs ...type T) *List(T) { 29 | list := Empty(T) 30 | for i := len(xs)-1; i >= 0; i-- { 31 | list = list.Prepend(xs[i]) 32 | } 33 | return list 34 | } 35 | 36 | // Empty returns whether l is an empty list. 37 | func (l *List(type T)) Empty() bool { 38 | return l == nil 39 | } 40 | 41 | // Slice collects all elements from the list into a slice. 42 | func (l *List(type T)) Slice() []T { 43 | var elems []T 44 | for !l.Empty() { 45 | elems = append(elems, l.First) 46 | l = l.Rest 47 | } 48 | return elems 49 | } 50 | 51 | // Map returns a new list where each element from the original list is transformed by f. 52 | func Map(l *List(type T), f func(T) type U) *List(U) { 53 | if l.Empty() { 54 | return Empty(U) 55 | } 56 | return Map(l.Rest, f).Prepend(f(l.First)) 57 | } 58 | 59 | func main() { 60 | list1 := Elems(1.0, 4.0, 9.0, 16.0) 61 | list2 := Map(list1, math.Sqrt) 62 | fmt.Println(list2.Slice()) 63 | } 64 | -------------------------------------------------------------------------------- /examples/list/out.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type List_float64 struct { 9 | First float64 10 | Rest *List_float64 11 | } 12 | 13 | func (l *List_float64) Prepend(x float64) *List_float64 { 14 | return &List_float64{First: x, 15 | Rest: l} 16 | } 17 | func (l *List_float64) Empty() bool { 18 | return l == nil 19 | } 20 | func (l *List_float64) Slice() []float64 { 21 | var elems []float64 22 | for !l.Empty() { 23 | elems = append(elems, l.First) 24 | l = l.Rest 25 | } 26 | return elems 27 | } 28 | func Empty_float64() *List_float64 { 29 | return nil 30 | } 31 | 32 | func Elems_float64(xs ...float64) *List_float64 { 33 | list := Empty_float64() 34 | for i := len(xs) - 1; i >= 0; i-- { 35 | list = list.Prepend(xs[i]) 36 | } 37 | return list 38 | } 39 | 40 | func Map_float64_float64(l *List_float64, f func(float64) float64) *List_float64 { 41 | if l.Empty() { 42 | return Empty_float64() 43 | } 44 | return Map_float64_float64(l.Rest, f).Prepend(f(l.First)) 45 | } 46 | func main() { 47 | list1 := Elems_float64(1.0, 4.0, 9.0, 16.0) 48 | list2 := Map_float64_float64(list1, math.Sqrt) 49 | fmt.Println(list2.Slice()) 50 | } 51 | -------------------------------------------------------------------------------- /examples/mathutils/mathutils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Min returns the smaller one of the two. 6 | func Min(x, y type T ord) T { 7 | if x < y { 8 | return x 9 | } 10 | return y 11 | } 12 | 13 | // Max returns the bigger one of the two. 14 | func Max(x, y type T ord) T { 15 | if x > y { 16 | return x 17 | } 18 | return y 19 | } 20 | 21 | // Sum returns the sum of the numbers. 22 | func Sum(nums ...type T num) T { 23 | result := T(0) 24 | for _, x := range nums { 25 | result += x 26 | } 27 | return result 28 | } 29 | 30 | // Product returns the product of the numbers. 31 | func Product(nums ...type T num) T { 32 | result := T(1) 33 | for _, x := range nums { 34 | result *= x 35 | } 36 | return result 37 | } 38 | 39 | func main() { 40 | fmt.Println(Min(7, 9)) 41 | fmt.Println(Min(int32(10), 93)) 42 | fmt.Println(Max(3.14, 31.4)) 43 | fmt.Println(Max("A", "B")) 44 | 45 | fmt.Println(Sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) 46 | fmt.Println(Product(1, 2, 3, 4, 5)) 47 | 48 | bytes := ([]byte)("Hello, world!") 49 | fmt.Println(Sum(bytes...)) 50 | } 51 | -------------------------------------------------------------------------------- /examples/mathutils/out.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func Min_int(x, y int) int { 6 | if x < y { 7 | return x 8 | } 9 | return y 10 | } 11 | 12 | func Min_int32(x, y int32) int32 { 13 | if x < y { 14 | return x 15 | } 16 | return y 17 | } 18 | 19 | func Max_float64(x, y float64) float64 { 20 | if x > y { 21 | return x 22 | } 23 | return y 24 | } 25 | 26 | func Max_string(x, y string) string { 27 | if x > y { 28 | return x 29 | } 30 | return y 31 | } 32 | 33 | func Sum_int(nums ...int) int { 34 | result := int(0) 35 | for _, x := range nums { 36 | result += x 37 | } 38 | return result 39 | } 40 | 41 | func Product_int(nums ...int) int { 42 | result := int(1) 43 | for _, x := range nums { 44 | result *= x 45 | } 46 | return result 47 | } 48 | 49 | func Sum_byte(nums ...byte) byte { 50 | result := byte(0) 51 | for _, x := range nums { 52 | result += x 53 | } 54 | return result 55 | } 56 | func main() { 57 | fmt.Println(Min_int(7, 9)) 58 | fmt.Println(Min_int32(int32(10), 93)) 59 | fmt.Println(Max_float64(3.14, 31.4)) 60 | fmt.Println(Max_string("A", "B")) 61 | 62 | fmt.Println(Sum_int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) 63 | fmt.Println(Product_int(1, 2, 3, 4, 5)) 64 | 65 | bytes := ([]byte)("Hello, world!") 66 | fmt.Println(Sum_byte(bytes...)) 67 | } 68 | -------------------------------------------------------------------------------- /examples/priorityqueue/out.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Person struct { 6 | name string 7 | age int 8 | } 9 | 10 | func (p Person) String() string { 11 | return fmt.Sprintf("%s is %d years old", p.name, p.age) 12 | } 13 | func (p Person) Name() string { 14 | return p.name 15 | } 16 | func (p Person) Age() int { 17 | return p.age 18 | } 19 | func Min_int() func(int, int) bool { 20 | return func(x, y int) bool { return x < y } 21 | } 22 | 23 | type Heap_int struct { 24 | elems []int 25 | less func(int, int) bool 26 | } 27 | 28 | func (h *Heap_int) Size() int { 29 | return len(h.elems) 30 | } 31 | 32 | func (h *Heap_int) Push(x int) { 33 | h.elems = append(h.elems, x) 34 | j := len(h.elems) - 1 35 | for { 36 | 37 | i := (j - 1) / 2 38 | if i == j || !h.less(h.elems[j], h.elems[i]) { 39 | break 40 | } 41 | 42 | h.elems[i], h.elems[j] = h.elems[j], h.elems[i] 43 | j = i 44 | } 45 | } 46 | func (h *Heap_int) Top() (top int, ok bool) { 47 | if len(h.elems) == 0 { 48 | ok = false 49 | return 50 | } 51 | return h.elems[0], true 52 | } 53 | func (h *Heap_int) Pop() (top int, ok bool) { 54 | if len(h.elems) == 0 { 55 | ok = false 56 | return 57 | } 58 | 59 | n := len(h.elems) - 1 60 | h.elems[0], h.elems[n] = h.elems[n], h.elems[0] 61 | i := 0 62 | for { 63 | 64 | j1 := 2*i + 1 65 | if j1 >= n || j1 < 0 { 66 | break 67 | } 68 | 69 | j := j1 70 | if j2 := j1 + 1; j2 < n && h.less(h.elems[j2], h.elems[j1]) { 71 | j = j2 72 | } 73 | if !h.less(h.elems[j], h.elems[i]) { 74 | break 75 | } 76 | 77 | h.elems[i], h.elems[j] = h.elems[j], h.elems[i] 78 | i = j 79 | } 80 | 81 | top = h.elems[n] 82 | h.elems = h.elems[:n] 83 | return top, true 84 | } 85 | 86 | func NewHeap_int(less func(x, y int) bool) *Heap_int { return &Heap_int{less: less} } 87 | 88 | func MaxBy_int_Person(by func(Person) int) func(Person, Person) bool { 89 | return func(x, y Person) bool { return by(x) > by(y) } 90 | } 91 | 92 | type Heap_Person struct { 93 | elems []Person 94 | less func(Person, Person) bool 95 | } 96 | 97 | func (h *Heap_Person) Size() int { 98 | return len(h.elems) 99 | } 100 | 101 | func (h *Heap_Person) Push(x Person) { 102 | h.elems = append(h.elems, x) 103 | j := len(h.elems) - 1 104 | for { 105 | 106 | i := (j - 1) / 2 107 | if i == j || !h.less(h.elems[j], h.elems[i]) { 108 | break 109 | } 110 | 111 | h.elems[i], h.elems[j] = h.elems[j], h.elems[i] 112 | j = i 113 | } 114 | } 115 | func (h *Heap_Person) Top() (top Person, ok bool) { 116 | if len(h.elems) == 0 { 117 | ok = false 118 | return 119 | } 120 | return h.elems[0], true 121 | } 122 | func (h *Heap_Person) Pop() (top Person, ok bool) { 123 | if len(h.elems) == 0 { 124 | ok = false 125 | return 126 | } 127 | 128 | n := len(h.elems) - 1 129 | h.elems[0], h.elems[n] = h.elems[n], h.elems[0] 130 | i := 0 131 | for { 132 | 133 | j1 := 2*i + 1 134 | if j1 >= n || j1 < 0 { 135 | break 136 | } 137 | 138 | j := j1 139 | if j2 := j1 + 1; j2 < n && h.less(h.elems[j2], h.elems[j1]) { 140 | j = j2 141 | } 142 | if !h.less(h.elems[j], h.elems[i]) { 143 | break 144 | } 145 | 146 | h.elems[i], h.elems[j] = h.elems[j], h.elems[i] 147 | i = j 148 | } 149 | 150 | top = h.elems[n] 151 | h.elems = h.elems[:n] 152 | return top, true 153 | } 154 | 155 | func NewHeap_Person(less func(x, y Person) bool) *Heap_Person { return &Heap_Person{less: less} } 156 | func main() { 157 | numbers := NewHeap_int(Min_int()) 158 | for x := 10; x >= 1; x-- { 159 | numbers.Push(x) 160 | } 161 | for { 162 | 163 | x, ok := numbers.Pop() 164 | if !ok { 165 | break 166 | } 167 | 168 | fmt.Print(x, " ") 169 | } 170 | 171 | fmt.Println() 172 | 173 | fmt.Println() 174 | 175 | people := NewHeap_Person(MaxBy_int_Person(Person.Age)) 176 | for _, person := range []Person{{"Michal", 23}, {"Viktória", 20}, {"Jano", 21}, {"Martin", 18}} { 177 | 178 | people.Push(person) 179 | } 180 | for { 181 | 182 | p, ok := people.Pop() 183 | if !ok { 184 | break 185 | } 186 | 187 | fmt.Println(p) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /examples/priorityqueue/priorityqueue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Heap is a container that lets you add elements into it and provides 6 | // access to the minimal (or maximal) element. 7 | type Heap(type T) struct { 8 | elems []T 9 | less func(T, T) bool 10 | } 11 | 12 | // NewHeap constructs an empty heap container with the specified comparator. 13 | // 14 | // For the usual cases, a comparator can be created using one of the Min, Max, MinBy, and MaxBy functions: 15 | // 16 | // NewHeap(Min(int)) // a minimum heap of ints 17 | // NewHeap(Max(string)) // a maximum heap of strings 18 | // NewHeap(MinBy(Person.Age)) // a minimum heap of Person values that sorts by the results of the Age method 19 | func NewHeap(less func(x, y type T) bool) *Heap(T) { 20 | return &Heap(T){ 21 | less: less, 22 | } 23 | } 24 | 25 | // The following four functions help create comparators. The same thing could (and probably should) 26 | // be used for sorting, because it's very practical. 27 | 28 | // Min returns a comparator for an orderable type T that compares using <. 29 | func Min(type T ord) func(T, T) bool { 30 | return func(x, y T) bool { 31 | return x < y 32 | } 33 | } 34 | 35 | // Max returns a comparator for an oderable type T that compares using >. 36 | func Max(type T ord) func(T, T) bool { 37 | return func(x, y T) bool { 38 | return x > y 39 | } 40 | } 41 | 42 | // MinBy returns a comparator that compares using < based on the results of the provided function. 43 | func MinBy(by func(type T) type O ord) func(T, T) bool { 44 | return func(x, y T) bool { 45 | return by(x) < by(y) 46 | } 47 | } 48 | 49 | // MaxBy returns a comparator that compares using > based on the results of the provided function. 50 | func MaxBy(by func(type T) type O ord) func(T, T) bool { 51 | return func(x, y T) bool { 52 | return by(x) > by(y) 53 | } 54 | } 55 | 56 | // Size returns the number of elements currently in the heap. 57 | func (h *Heap(type T)) Size() int { 58 | return len(h.elems) 59 | } 60 | 61 | // Push adds an element to the heap. 62 | func (h *Heap(type T)) Push(x T) { 63 | h.elems = append(h.elems, x) 64 | j := len(h.elems) - 1 65 | for { 66 | i := (j - 1) / 2 // parent 67 | if i == j || !h.less(h.elems[j], h.elems[i]) { 68 | break 69 | } 70 | h.elems[i], h.elems[j] = h.elems[j], h.elems[i] 71 | j = i 72 | } 73 | } 74 | 75 | // Top returns the current minimal (or maximal) element. 76 | // 77 | // Returns false if the heap is empty. 78 | func (h *Heap(type T)) Top() (top T, ok bool) { 79 | if len(h.elems) == 0 { 80 | ok = false 81 | return 82 | } 83 | return h.elems[0], true 84 | } 85 | 86 | // Pop returns the current minimal (or maximal) element and removes it from the heap. 87 | // 88 | // Returns false if the heap is empty. 89 | func (h *Heap(type T)) Pop() (top T, ok bool) { 90 | if len(h.elems) == 0 { 91 | ok = false 92 | return 93 | } 94 | n := len(h.elems) - 1 95 | h.elems[0], h.elems[n] = h.elems[n], h.elems[0] 96 | i := 0 97 | for { 98 | j1 := 2*i + 1 99 | if j1 >= n || j1 < 0 { // j1 < 0 after int overflow 100 | break 101 | } 102 | j := j1 // left child 103 | if j2 := j1 + 1; j2 < n && h.less(h.elems[j2], h.elems[j1]) { 104 | j = j2 // = 2*i + 2 // right child 105 | } 106 | if !h.less(h.elems[j], h.elems[i]) { 107 | break 108 | } 109 | h.elems[i], h.elems[j] = h.elems[j], h.elems[i] 110 | i = j 111 | } 112 | top = h.elems[n] 113 | h.elems = h.elems[:n] 114 | return top, true 115 | } 116 | 117 | type Person struct { 118 | name string 119 | age int 120 | } 121 | 122 | func (p Person) String() string { 123 | return fmt.Sprintf("%s is %d years old", p.name, p.age) 124 | } 125 | 126 | func (p Person) Name() string { 127 | return p.name 128 | } 129 | 130 | func (p Person) Age() int { 131 | return p.age 132 | } 133 | 134 | func main() { 135 | numbers := NewHeap(Min(int)) 136 | for x := 10; x >= 1; x-- { 137 | numbers.Push(x) 138 | } 139 | for { 140 | x, ok := numbers.Pop() 141 | if !ok { 142 | break 143 | } 144 | fmt.Print(x, " ") 145 | } 146 | fmt.Println() 147 | 148 | fmt.Println() 149 | 150 | people := NewHeap(MaxBy(Person.Age)) 151 | for _, person := range []Person{ 152 | {"Michal", 23}, 153 | {"Viktória", 20}, 154 | {"Jano", 21}, 155 | {"Martin", 18}, 156 | } { 157 | people.Push(person) 158 | } 159 | for { 160 | p, ok := people.Pop() 161 | if !ok { 162 | break 163 | } 164 | fmt.Println(p) 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /examples/sliceutils/out.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | type Person struct { 9 | name string 10 | age int 11 | } 12 | 13 | func (p Person) Name() string { 14 | return p.name 15 | } 16 | func (p Person) Age() int { 17 | return p.age 18 | } 19 | 20 | func SortBy_int_Person(a []Person, by func(Person) int) { 21 | sort.Slice(a, func(i, j int) bool { return by(a[i]) < by(a[j]) }) 22 | } 23 | func Map_Person_string(a []Person, f func(Person) string) []string { 24 | result := make([]string, len(a)) 25 | for i := range a { 26 | result[i] = f(a[i]) 27 | } 28 | return result 29 | } 30 | 31 | func Map_Person_int(a []Person, f func(Person) int) []int { 32 | result := make([]int, len(a)) 33 | for i := range a { 34 | result[i] = f(a[i]) 35 | } 36 | return result 37 | } 38 | 39 | func Map_int_string(a []int, f func(int) string) []string { 40 | result := make([]string, len(a)) 41 | for i := range a { 42 | result[i] = f(a[i]) 43 | } 44 | return result 45 | } 46 | 47 | func Concat_string(slices ...[]string) []string { 48 | total := 0 49 | for i := range slices { 50 | total += len(slices[i]) 51 | } 52 | 53 | result := make([]string, 0, total) 54 | for i := range slices { 55 | result = append(result, slices[i]...) 56 | } 57 | return result 58 | } 59 | 60 | func Reverse_string(a []string) { 61 | for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 { 62 | a[i], a[j] = a[j], a[i] 63 | } 64 | } 65 | func main() { 66 | people := []Person{{"Michal", 23}, {"Viktória", 20}, {"Jano", 21}, {"Martin", 18}} 67 | SortBy_int_Person(people, Person.Age) 68 | fmt.Println(people) 69 | 70 | names := Map_Person_string(people, Person.Name) 71 | ages := Map_Person_int(people, Person.Age) 72 | ageStrings := Map_int_string(ages, func(a int) string { return fmt.Sprint(a) }) 73 | 74 | everything := Concat_string(names, ageStrings) 75 | Reverse_string(everything) 76 | fmt.Println(everything) 77 | } 78 | -------------------------------------------------------------------------------- /examples/sliceutils/sliceutils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | // Reverse reverses any slice in place. 9 | func Reverse(a []type T) { 10 | for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 { 11 | a[i], a[j] = a[j], a[i] 12 | } 13 | } 14 | 15 | // Concat returns a concatenation of multiple slices of the same type. 16 | func Concat(slices ...[]type T) []T { 17 | total := 0 18 | for i := range slices { 19 | total += len(slices[i]) 20 | } 21 | result := make([]T, 0, total) 22 | for i := range slices { 23 | result = append(result, slices[i]...) 24 | } 25 | return result 26 | } 27 | 28 | // Map returns a new slice where each element from the original slice is transformed by f. 29 | func Map(a []type T, f func(T) type U) []U { 30 | result := make([]U, len(a)) 31 | for i := range a { 32 | result[i] = f(a[i]) 33 | } 34 | return result 35 | } 36 | 37 | // Interfaces converts a slice of an arbitrary type to a slice of empty interfaces. 38 | func Interfaces(a []type T) []interface{} { 39 | ifaces := make([]interface{}, len(a)) 40 | for i := range a { 41 | ifaces[i] = a[i] 42 | } 43 | return ifaces 44 | } 45 | 46 | // Sort sorts a slice of an arbitrary orderable type. 47 | func Sort(a []type T ord) { 48 | sort.Slice(a, func(i, j int) bool { 49 | return a[i] < a[j] 50 | }) 51 | } 52 | 53 | // SortBy sorts a slice by a key. For example: 54 | // 55 | // SortBy(people, (*Person).Age) 56 | func SortBy(a []type T, by func(T) type O ord) { 57 | sort.Slice(a, func(i, j int) bool { 58 | return by(a[i]) < by(a[j]) 59 | }) 60 | } 61 | 62 | // SortWith sorts a slice using a custom comparator. 63 | func SortWith(a []type T, with func(T, T) bool) { 64 | sort.Slice(a, func(i, j int) bool { 65 | return with(a[i], a[j]) 66 | }) 67 | } 68 | 69 | type Person struct { 70 | name string 71 | age int 72 | } 73 | 74 | func (p Person) Name() string { 75 | return p.name 76 | } 77 | 78 | func (p Person) Age() int { 79 | return p.age 80 | } 81 | 82 | func main() { 83 | people := []Person{ 84 | {"Michal", 23}, 85 | {"Viktória", 20}, 86 | {"Jano", 21}, 87 | {"Martin", 18}, 88 | } 89 | SortBy(people, Person.Age) 90 | fmt.Println(people) 91 | 92 | names := Map(people, Person.Name) 93 | ages := Map(people, Person.Age) 94 | ageStrings := Map(ages, func(a int) string { 95 | return fmt.Sprint(a) 96 | }) 97 | 98 | everything := Concat(names, ageStrings) 99 | Reverse(everything) 100 | fmt.Println(everything) 101 | } 102 | -------------------------------------------------------------------------------- /examples/syncmap/out.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type SyncMap_string_bool struct { 10 | mu sync.Mutex 11 | m map[string]bool 12 | } 13 | 14 | func (sm *SyncMap_string_bool) Delete(key string) { sm.mu.Lock(); delete(sm.m, key); sm.mu.Unlock() } 15 | 16 | func (sm *SyncMap_string_bool) Load(key string) (value bool, ok bool) { 17 | sm.mu.Lock() 18 | value, ok = sm.m[key] 19 | sm.mu.Unlock() 20 | return 21 | } 22 | 23 | func (sm *SyncMap_string_bool) LoadOrStore(key string, value bool) (actual bool, loaded bool) { 24 | sm.mu.Lock() 25 | actual, loaded = sm.m[key] 26 | if !loaded { 27 | sm.m[key] = value 28 | actual = value 29 | } 30 | 31 | sm.mu.Unlock() 32 | return 33 | } 34 | 35 | func (sm *SyncMap_string_bool) Range(f func(key string, value bool) bool) { 36 | sm.mu.Lock() 37 | for k, v := range sm.m { 38 | if !f(k, v) { 39 | break 40 | } 41 | } 42 | 43 | sm.mu.Unlock() 44 | } 45 | 46 | func (sm *SyncMap_string_bool) Store(key string, value bool) { 47 | sm.mu.Lock() 48 | sm.m[key] = value 49 | sm.mu.Unlock() 50 | } 51 | func MakeSyncMap_string_bool() *SyncMap_string_bool { 52 | return &SyncMap_string_bool{m: make(map[string]bool)} 53 | } 54 | 55 | func MarkAll_string(done chan<- bool, sm *SyncMap_string_bool, values ...string) { 56 | for _, val := range values { 57 | time.Sleep(time.Second / 10) 58 | sm.Store(val, true) 59 | } 60 | 61 | done <- true 62 | } 63 | func main() { 64 | marked := MakeSyncMap_string_bool() 65 | done := make(chan bool) 66 | go MarkAll_string(done, marked, "A", "B", "C", "D") 67 | go MarkAll_string(done, marked, "E", "F", "G", "H") 68 | go MarkAll_string(done, marked, "I", "J", "K", "L") 69 | for i := 0; i < 3; i++ { 70 | <-done 71 | } 72 | 73 | marked.Range(func(key string, value bool) bool { fmt.Println(key); return true }) 74 | } 75 | -------------------------------------------------------------------------------- /examples/syncmap/syncmap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // SyncMap is a generic hash-map usable from multiple goroutines simultaneously. 10 | // 11 | // The API imitates the API of sync.Map. 12 | // 13 | // This is a dummy implementation to demonstrate the typing capabilities. This is not 14 | // an example of an efficient implementation of a SyncMap. 15 | type SyncMap(type K eq, type V) struct { 16 | mu sync.Mutex 17 | m map[K]V 18 | } 19 | 20 | // MakeSyncMap creates a new, empty SyncMap. 21 | // 22 | // I know it's better to make the zero value useful, this is just to better demonstrate 23 | // the unnamed type parmeters syntax. 24 | func MakeSyncMap(type K eq, type V) *SyncMap(K, V) { 25 | return &SyncMap(K, V){ 26 | m: make(map[K]V), 27 | } 28 | } 29 | 30 | // Delete deletes the value for a key. 31 | func (sm *SyncMap(type K eq, type V)) Delete(key K) { 32 | sm.mu.Lock() 33 | delete(sm.m, key) 34 | sm.mu.Unlock() 35 | } 36 | 37 | // Load returns the value stored in the map for a key, or nil if no value is present. 38 | // The ok result indicates whether value was found in the map. 39 | func (sm *SyncMap(type K eq, type V)) Load(key K) (value V, ok bool) { 40 | sm.mu.Lock() 41 | value, ok = sm.m[key] 42 | sm.mu.Unlock() 43 | return 44 | } 45 | 46 | // LoadOrStore returns the existing value for the key if present. 47 | // Otherwise, it stores and returns the given value. 48 | // The loaded result is true if the value was loaded, false if stored. 49 | func (sm *SyncMap(type K eq, type V)) LoadOrStore(key K, value V) (actual V, loaded bool) { 50 | sm.mu.Lock() 51 | actual, loaded = sm.m[key] 52 | if !loaded { 53 | sm.m[key] = value 54 | actual = value 55 | } 56 | sm.mu.Unlock() 57 | return 58 | } 59 | 60 | // Range calls f sequentially for each key and value present in the map. 61 | // If f returns false, range stops the iteration. 62 | func (sm *SyncMap(type K eq, type V)) Range(f func(key K, value V) bool) { 63 | sm.mu.Lock() 64 | for k, v := range sm.m { 65 | if !f(k, v) { 66 | break 67 | } 68 | } 69 | sm.mu.Unlock() 70 | } 71 | 72 | // Store sets the value for a key. 73 | func (sm *SyncMap(type K eq, type V)) Store(key K, value V) { 74 | sm.mu.Lock() 75 | sm.m[key] = value 76 | sm.mu.Unlock() 77 | } 78 | 79 | func MarkAll(done chan<- bool, sm *SyncMap(type T eq, bool), values ...T) { 80 | for _, val := range values { 81 | time.Sleep(time.Second / 10) 82 | sm.Store(val, true) 83 | } 84 | done <- true 85 | } 86 | 87 | func main() { 88 | marked := MakeSyncMap(string, bool) 89 | done := make(chan bool) 90 | 91 | go MarkAll(done, marked, "A", "B", "C", "D") 92 | go MarkAll(done, marked, "E", "F", "G", "H") 93 | go MarkAll(done, marked, "I", "J", "K", "L") 94 | 95 | for i := 0; i < 3; i++ { 96 | <-done 97 | } 98 | 99 | marked.Range(func(key string, value bool) bool { 100 | fmt.Println(key) 101 | return true 102 | }) 103 | } 104 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/faiface/generics 2 | -------------------------------------------------------------------------------- /go/ast/import.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ast 6 | 7 | import ( 8 | "github.com/faiface/generics/go/token" 9 | "sort" 10 | "strconv" 11 | ) 12 | 13 | // SortImports sorts runs of consecutive import lines in import blocks in f. 14 | // It also removes duplicate imports when it is possible to do so without data loss. 15 | func SortImports(fset *token.FileSet, f *File) { 16 | for _, d := range f.Decls { 17 | d, ok := d.(*GenDecl) 18 | if !ok || d.Tok != token.IMPORT { 19 | // Not an import declaration, so we're done. 20 | // Imports are always first. 21 | break 22 | } 23 | 24 | if !d.Lparen.IsValid() { 25 | // Not a block: sorted by default. 26 | continue 27 | } 28 | 29 | // Identify and sort runs of specs on successive lines. 30 | i := 0 31 | specs := d.Specs[:0] 32 | for j, s := range d.Specs { 33 | if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line { 34 | // j begins a new run. End this one. 35 | specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...) 36 | i = j 37 | } 38 | } 39 | specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...) 40 | d.Specs = specs 41 | 42 | // Deduping can leave a blank line before the rparen; clean that up. 43 | if len(d.Specs) > 0 { 44 | lastSpec := d.Specs[len(d.Specs)-1] 45 | lastLine := fset.Position(lastSpec.Pos()).Line 46 | rParenLine := fset.Position(d.Rparen).Line 47 | for rParenLine > lastLine+1 { 48 | rParenLine-- 49 | fset.File(d.Rparen).MergeLine(rParenLine) 50 | } 51 | } 52 | } 53 | } 54 | 55 | func importPath(s Spec) string { 56 | t, err := strconv.Unquote(s.(*ImportSpec).Path.Value) 57 | if err == nil { 58 | return t 59 | } 60 | return "" 61 | } 62 | 63 | func importName(s Spec) string { 64 | n := s.(*ImportSpec).Name 65 | if n == nil { 66 | return "" 67 | } 68 | return n.Name 69 | } 70 | 71 | func importComment(s Spec) string { 72 | c := s.(*ImportSpec).Comment 73 | if c == nil { 74 | return "" 75 | } 76 | return c.Text() 77 | } 78 | 79 | // collapse indicates whether prev may be removed, leaving only next. 80 | func collapse(prev, next Spec) bool { 81 | if importPath(next) != importPath(prev) || importName(next) != importName(prev) { 82 | return false 83 | } 84 | return prev.(*ImportSpec).Comment == nil 85 | } 86 | 87 | type posSpan struct { 88 | Start token.Pos 89 | End token.Pos 90 | } 91 | 92 | func sortSpecs(fset *token.FileSet, f *File, specs []Spec) []Spec { 93 | // Can't short-circuit here even if specs are already sorted, 94 | // since they might yet need deduplication. 95 | // A lone import, however, may be safely ignored. 96 | if len(specs) <= 1 { 97 | return specs 98 | } 99 | 100 | // Record positions for specs. 101 | pos := make([]posSpan, len(specs)) 102 | for i, s := range specs { 103 | pos[i] = posSpan{s.Pos(), s.End()} 104 | } 105 | 106 | // Identify comments in this range. 107 | // Any comment from pos[0].Start to the final line counts. 108 | lastLine := fset.Position(pos[len(pos)-1].End).Line 109 | cstart := len(f.Comments) 110 | cend := len(f.Comments) 111 | for i, g := range f.Comments { 112 | if g.Pos() < pos[0].Start { 113 | continue 114 | } 115 | if i < cstart { 116 | cstart = i 117 | } 118 | if fset.Position(g.End()).Line > lastLine { 119 | cend = i 120 | break 121 | } 122 | } 123 | comments := f.Comments[cstart:cend] 124 | 125 | // Assign each comment to the import spec preceding it. 126 | importComments := map[*ImportSpec][]*CommentGroup{} 127 | specIndex := 0 128 | for _, g := range comments { 129 | for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() { 130 | specIndex++ 131 | } 132 | s := specs[specIndex].(*ImportSpec) 133 | importComments[s] = append(importComments[s], g) 134 | } 135 | 136 | // Sort the import specs by import path. 137 | // Remove duplicates, when possible without data loss. 138 | // Reassign the import paths to have the same position sequence. 139 | // Reassign each comment to abut the end of its spec. 140 | // Sort the comments by new position. 141 | sort.Slice(specs, func(i, j int) bool { 142 | ipath := importPath(specs[i]) 143 | jpath := importPath(specs[j]) 144 | if ipath != jpath { 145 | return ipath < jpath 146 | } 147 | iname := importName(specs[i]) 148 | jname := importName(specs[j]) 149 | if iname != jname { 150 | return iname < jname 151 | } 152 | return importComment(specs[i]) < importComment(specs[j]) 153 | }) 154 | 155 | // Dedup. Thanks to our sorting, we can just consider 156 | // adjacent pairs of imports. 157 | deduped := specs[:0] 158 | for i, s := range specs { 159 | if i == len(specs)-1 || !collapse(s, specs[i+1]) { 160 | deduped = append(deduped, s) 161 | } else { 162 | p := s.Pos() 163 | fset.File(p).MergeLine(fset.Position(p).Line) 164 | } 165 | } 166 | specs = deduped 167 | 168 | // Fix up comment positions 169 | for i, s := range specs { 170 | s := s.(*ImportSpec) 171 | if s.Name != nil { 172 | s.Name.NamePos = pos[i].Start 173 | } 174 | s.Path.ValuePos = pos[i].Start 175 | s.EndPos = pos[i].End 176 | for _, g := range importComments[s] { 177 | for _, c := range g.List { 178 | c.Slash = pos[i].End 179 | } 180 | } 181 | } 182 | 183 | sort.Slice(comments, func(i, j int) bool { 184 | return comments[i].Pos() < comments[j].Pos() 185 | }) 186 | 187 | return specs 188 | } 189 | -------------------------------------------------------------------------------- /go/ast/resolve.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This file implements NewPackage. 6 | 7 | package ast 8 | 9 | import ( 10 | "fmt" 11 | "github.com/faiface/generics/go/scanner" 12 | "github.com/faiface/generics/go/token" 13 | "strconv" 14 | ) 15 | 16 | type pkgBuilder struct { 17 | fset *token.FileSet 18 | errors scanner.ErrorList 19 | } 20 | 21 | func (p *pkgBuilder) error(pos token.Pos, msg string) { 22 | p.errors.Add(p.fset.Position(pos), msg) 23 | } 24 | 25 | func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) { 26 | p.error(pos, fmt.Sprintf(format, args...)) 27 | } 28 | 29 | func (p *pkgBuilder) declare(scope, altScope *Scope, obj *Object) { 30 | alt := scope.Insert(obj) 31 | if alt == nil && altScope != nil { 32 | // see if there is a conflicting declaration in altScope 33 | alt = altScope.Lookup(obj.Name) 34 | } 35 | if alt != nil { 36 | prevDecl := "" 37 | if pos := alt.Pos(); pos.IsValid() { 38 | prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.fset.Position(pos)) 39 | } 40 | p.error(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl)) 41 | } 42 | } 43 | 44 | func resolve(scope *Scope, ident *Ident) bool { 45 | for ; scope != nil; scope = scope.Outer { 46 | if obj := scope.Lookup(ident.Name); obj != nil { 47 | ident.Obj = obj 48 | return true 49 | } 50 | } 51 | return false 52 | } 53 | 54 | // An Importer resolves import paths to package Objects. 55 | // The imports map records the packages already imported, 56 | // indexed by package id (canonical import path). 57 | // An Importer must determine the canonical import path and 58 | // check the map to see if it is already present in the imports map. 59 | // If so, the Importer can return the map entry. Otherwise, the 60 | // Importer should load the package data for the given path into 61 | // a new *Object (pkg), record pkg in the imports map, and then 62 | // return pkg. 63 | type Importer func(imports map[string]*Object, path string) (pkg *Object, err error) 64 | 65 | // NewPackage creates a new Package node from a set of File nodes. It resolves 66 | // unresolved identifiers across files and updates each file's Unresolved list 67 | // accordingly. If a non-nil importer and universe scope are provided, they are 68 | // used to resolve identifiers not declared in any of the package files. Any 69 | // remaining unresolved identifiers are reported as undeclared. If the files 70 | // belong to different packages, one package name is selected and files with 71 | // different package names are reported and then ignored. 72 | // The result is a package node and a scanner.ErrorList if there were errors. 73 | // 74 | func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, error) { 75 | var p pkgBuilder 76 | p.fset = fset 77 | 78 | // complete package scope 79 | pkgName := "" 80 | pkgScope := NewScope(universe) 81 | for _, file := range files { 82 | // package names must match 83 | switch name := file.Name.Name; { 84 | case pkgName == "": 85 | pkgName = name 86 | case name != pkgName: 87 | p.errorf(file.Package, "package %s; expected %s", name, pkgName) 88 | continue // ignore this file 89 | } 90 | 91 | // collect top-level file objects in package scope 92 | for _, obj := range file.Scope.Objects { 93 | p.declare(pkgScope, nil, obj) 94 | } 95 | } 96 | 97 | // package global mapping of imported package ids to package objects 98 | imports := make(map[string]*Object) 99 | 100 | // complete file scopes with imports and resolve identifiers 101 | for _, file := range files { 102 | // ignore file if it belongs to a different package 103 | // (error has already been reported) 104 | if file.Name.Name != pkgName { 105 | continue 106 | } 107 | 108 | // build file scope by processing all imports 109 | importErrors := false 110 | fileScope := NewScope(pkgScope) 111 | for _, spec := range file.Imports { 112 | if importer == nil { 113 | importErrors = true 114 | continue 115 | } 116 | path, _ := strconv.Unquote(spec.Path.Value) 117 | pkg, err := importer(imports, path) 118 | if err != nil { 119 | p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err) 120 | importErrors = true 121 | continue 122 | } 123 | // TODO(gri) If a local package name != "." is provided, 124 | // global identifier resolution could proceed even if the 125 | // import failed. Consider adjusting the logic here a bit. 126 | 127 | // local name overrides imported package name 128 | name := pkg.Name 129 | if spec.Name != nil { 130 | name = spec.Name.Name 131 | } 132 | 133 | // add import to file scope 134 | if name == "." { 135 | // merge imported scope with file scope 136 | for _, obj := range pkg.Data.(*Scope).Objects { 137 | p.declare(fileScope, pkgScope, obj) 138 | } 139 | } else if name != "_" { 140 | // declare imported package object in file scope 141 | // (do not re-use pkg in the file scope but create 142 | // a new object instead; the Decl field is different 143 | // for different files) 144 | obj := NewObj(Pkg, name) 145 | obj.Decl = spec 146 | obj.Data = pkg.Data 147 | p.declare(fileScope, pkgScope, obj) 148 | } 149 | } 150 | 151 | // resolve identifiers 152 | if importErrors { 153 | // don't use the universe scope without correct imports 154 | // (objects in the universe may be shadowed by imports; 155 | // with missing imports, identifiers might get resolved 156 | // incorrectly to universe objects) 157 | pkgScope.Outer = nil 158 | } 159 | i := 0 160 | for _, ident := range file.Unresolved { 161 | if !resolve(fileScope, ident) { 162 | p.errorf(ident.Pos(), "undeclared name: %s", ident.Name) 163 | file.Unresolved[i] = ident 164 | i++ 165 | } 166 | 167 | } 168 | file.Unresolved = file.Unresolved[0:i] 169 | pkgScope.Outer = universe // reset universe scope 170 | } 171 | 172 | p.errors.Sort() 173 | return &Package{pkgName, pkgScope, imports, files}, p.errors.Err() 174 | } 175 | -------------------------------------------------------------------------------- /go/ast/scope.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This file implements scopes and the objects they contain. 6 | 7 | package ast 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "github.com/faiface/generics/go/token" 13 | ) 14 | 15 | // A Scope maintains the set of named language entities declared 16 | // in the scope and a link to the immediately surrounding (outer) 17 | // scope. 18 | // 19 | type Scope struct { 20 | Outer *Scope 21 | Objects map[string]*Object 22 | } 23 | 24 | // NewScope creates a new scope nested in the outer scope. 25 | func NewScope(outer *Scope) *Scope { 26 | const n = 4 // initial scope capacity 27 | return &Scope{outer, make(map[string]*Object, n)} 28 | } 29 | 30 | // Lookup returns the object with the given name if it is 31 | // found in scope s, otherwise it returns nil. Outer scopes 32 | // are ignored. 33 | // 34 | func (s *Scope) Lookup(name string) *Object { 35 | return s.Objects[name] 36 | } 37 | 38 | // Insert attempts to insert a named object obj into the scope s. 39 | // If the scope already contains an object alt with the same name, 40 | // Insert leaves the scope unchanged and returns alt. Otherwise 41 | // it inserts obj and returns nil. 42 | // 43 | func (s *Scope) Insert(obj *Object) (alt *Object) { 44 | if alt = s.Objects[obj.Name]; alt == nil { 45 | s.Objects[obj.Name] = obj 46 | } 47 | return 48 | } 49 | 50 | // Debugging support 51 | func (s *Scope) String() string { 52 | var buf bytes.Buffer 53 | fmt.Fprintf(&buf, "scope %p {", s) 54 | if s != nil && len(s.Objects) > 0 { 55 | fmt.Fprintln(&buf) 56 | for _, obj := range s.Objects { 57 | fmt.Fprintf(&buf, "\t%s %s\n", obj.Kind, obj.Name) 58 | } 59 | } 60 | fmt.Fprintf(&buf, "}\n") 61 | return buf.String() 62 | } 63 | 64 | // ---------------------------------------------------------------------------- 65 | // Objects 66 | 67 | // An Object describes a named language entity such as a package, 68 | // constant, type, variable, function (incl. methods), or label. 69 | // 70 | // The Data fields contains object-specific data: 71 | // 72 | // Kind Data type Data value 73 | // Pkg *Scope package scope 74 | // Con int iota for the respective declaration 75 | // 76 | type Object struct { 77 | Kind ObjKind 78 | Name string // declared name 79 | Decl interface{} // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil 80 | Data interface{} // object-specific data; or nil 81 | Type interface{} // placeholder for type information; may be nil 82 | } 83 | 84 | // NewObj creates a new object of a given kind and name. 85 | func NewObj(kind ObjKind, name string) *Object { 86 | return &Object{Kind: kind, Name: name} 87 | } 88 | 89 | // Pos computes the source position of the declaration of an object name. 90 | // The result may be an invalid position if it cannot be computed 91 | // (obj.Decl may be nil or not correct). 92 | func (obj *Object) Pos() token.Pos { 93 | name := obj.Name 94 | switch d := obj.Decl.(type) { 95 | case *Field: 96 | for _, n := range d.Names { 97 | if n.Name == name { 98 | return n.Pos() 99 | } 100 | } 101 | case *ImportSpec: 102 | if d.Name != nil && d.Name.Name == name { 103 | return d.Name.Pos() 104 | } 105 | return d.Path.Pos() 106 | case *ValueSpec: 107 | for _, n := range d.Names { 108 | if n.Name == name { 109 | return n.Pos() 110 | } 111 | } 112 | case *TypeSpec: 113 | if d.Name.Name == name { 114 | return d.Name.Pos() 115 | } 116 | case *FuncDecl: 117 | if d.Name.Name == name { 118 | return d.Name.Pos() 119 | } 120 | case *LabeledStmt: 121 | if d.Label.Name == name { 122 | return d.Label.Pos() 123 | } 124 | case *AssignStmt: 125 | for _, x := range d.Lhs { 126 | if ident, isIdent := x.(*Ident); isIdent && ident.Name == name { 127 | return ident.Pos() 128 | } 129 | } 130 | case *Scope: 131 | // predeclared object - nothing to do for now 132 | } 133 | return token.NoPos 134 | } 135 | 136 | // ObjKind describes what an object represents. 137 | type ObjKind int 138 | 139 | // The list of possible Object kinds. 140 | const ( 141 | Bad ObjKind = iota // for error handling 142 | Pkg // package 143 | Con // constant 144 | Typ // type 145 | Var // variable 146 | Fun // function or method 147 | Lbl // label 148 | ) 149 | 150 | var objKindStrings = [...]string{ 151 | Bad: "bad", 152 | Pkg: "package", 153 | Con: "const", 154 | Typ: "type", 155 | Var: "var", 156 | Fun: "func", 157 | Lbl: "label", 158 | } 159 | 160 | func (kind ObjKind) String() string { return objKindStrings[kind] } 161 | -------------------------------------------------------------------------------- /go/build/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package build gathers information about Go packages. 6 | // 7 | // Go Path 8 | // 9 | // The Go path is a list of directory trees containing Go source code. 10 | // It is consulted to resolve imports that cannot be found in the standard 11 | // Go tree. The default path is the value of the GOPATH environment 12 | // variable, interpreted as a path list appropriate to the operating system 13 | // (on Unix, the variable is a colon-separated string; 14 | // on Windows, a semicolon-separated string; 15 | // on Plan 9, a list). 16 | // 17 | // Each directory listed in the Go path must have a prescribed structure: 18 | // 19 | // The src/ directory holds source code. The path below 'src' determines 20 | // the import path or executable name. 21 | // 22 | // The pkg/ directory holds installed package objects. 23 | // As in the Go tree, each target operating system and 24 | // architecture pair has its own subdirectory of pkg 25 | // (pkg/GOOS_GOARCH). 26 | // 27 | // If DIR is a directory listed in the Go path, a package with 28 | // source in DIR/src/foo/bar can be imported as "foo/bar" and 29 | // has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a" 30 | // (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a"). 31 | // 32 | // The bin/ directory holds compiled commands. 33 | // Each command is named for its source directory, but only 34 | // using the final element, not the entire path. That is, the 35 | // command with source in DIR/src/foo/quux is installed into 36 | // DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped 37 | // so that you can add DIR/bin to your PATH to get at the 38 | // installed commands. 39 | // 40 | // Here's an example directory layout: 41 | // 42 | // GOPATH=/home/user/gocode 43 | // 44 | // /home/user/gocode/ 45 | // src/ 46 | // foo/ 47 | // bar/ (go code in package bar) 48 | // x.go 49 | // quux/ (go code in package main) 50 | // y.go 51 | // bin/ 52 | // quux (installed command) 53 | // pkg/ 54 | // linux_amd64/ 55 | // foo/ 56 | // bar.a (installed package object) 57 | // 58 | // Build Constraints 59 | // 60 | // A build constraint, also known as a build tag, is a line comment that begins 61 | // 62 | // // +build 63 | // 64 | // that lists the conditions under which a file should be included in the package. 65 | // Constraints may appear in any kind of source file (not just Go), but 66 | // they must appear near the top of the file, preceded 67 | // only by blank lines and other line comments. These rules mean that in Go 68 | // files a build constraint must appear before the package clause. 69 | // 70 | // To distinguish build constraints from package documentation, a series of 71 | // build constraints must be followed by a blank line. 72 | // 73 | // A build constraint is evaluated as the OR of space-separated options; 74 | // each option evaluates as the AND of its comma-separated terms; 75 | // and each term is an alphanumeric word or, preceded by !, its negation. 76 | // That is, the build constraint: 77 | // 78 | // // +build linux,386 darwin,!cgo 79 | // 80 | // corresponds to the boolean formula: 81 | // 82 | // (linux AND 386) OR (darwin AND (NOT cgo)) 83 | // 84 | // A file may have multiple build constraints. The overall constraint is the AND 85 | // of the individual constraints. That is, the build constraints: 86 | // 87 | // // +build linux darwin 88 | // // +build 386 89 | // 90 | // corresponds to the boolean formula: 91 | // 92 | // (linux OR darwin) AND 386 93 | // 94 | // During a particular build, the following words are satisfied: 95 | // 96 | // - the target operating system, as spelled by runtime.GOOS 97 | // - the target architecture, as spelled by runtime.GOARCH 98 | // - the compiler being used, either "gc" or "gccgo" 99 | // - "cgo", if ctxt.CgoEnabled is true 100 | // - "go1.1", from Go version 1.1 onward 101 | // - "go1.2", from Go version 1.2 onward 102 | // - "go1.3", from Go version 1.3 onward 103 | // - "go1.4", from Go version 1.4 onward 104 | // - "go1.5", from Go version 1.5 onward 105 | // - "go1.6", from Go version 1.6 onward 106 | // - "go1.7", from Go version 1.7 onward 107 | // - "go1.8", from Go version 1.8 onward 108 | // - "go1.9", from Go version 1.9 onward 109 | // - "go1.10", from Go version 1.10 onward 110 | // - any additional words listed in ctxt.BuildTags 111 | // 112 | // If a file's name, after stripping the extension and a possible _test suffix, 113 | // matches any of the following patterns: 114 | // *_GOOS 115 | // *_GOARCH 116 | // *_GOOS_GOARCH 117 | // (example: source_windows_amd64.go) where GOOS and GOARCH represent 118 | // any known operating system and architecture values respectively, then 119 | // the file is considered to have an implicit build constraint requiring 120 | // those terms (in addition to any explicit constraints in the file). 121 | // 122 | // To keep a file from being considered for the build: 123 | // 124 | // // +build ignore 125 | // 126 | // (any other unsatisfied word will work as well, but ``ignore'' is conventional.) 127 | // 128 | // To build a file only when using cgo, and only on Linux and OS X: 129 | // 130 | // // +build linux,cgo darwin,cgo 131 | // 132 | // Such a file is usually paired with another file implementing the 133 | // default functionality for other systems, which in this case would 134 | // carry the constraint: 135 | // 136 | // // +build !linux,!darwin !cgo 137 | // 138 | // Naming a file dns_windows.go will cause it to be included only when 139 | // building the package for Windows; similarly, math_386.s will be included 140 | // only when building the package for 32-bit x86. 141 | // 142 | // Using GOOS=android matches build tags and files as for GOOS=linux 143 | // in addition to android tags and files. 144 | // 145 | // Binary-Only Packages 146 | // 147 | // It is possible to distribute packages in binary form without including the 148 | // source code used for compiling the package. To do this, the package must 149 | // be distributed with a source file not excluded by build constraints and 150 | // containing a "//go:binary-only-package" comment. 151 | // Like a build constraint, this comment must appear near the top of the file, 152 | // preceded only by blank lines and other line comments and with a blank line 153 | // following the comment, to separate it from the package documentation. 154 | // Unlike build constraints, this comment is only recognized in non-test 155 | // Go source files. 156 | // 157 | // The minimal source code for a binary-only package is therefore: 158 | // 159 | // //go:binary-only-package 160 | // 161 | // package mypkg 162 | // 163 | // The source code may include additional Go code. That code is never compiled 164 | // but will be processed by tools like godoc and might be useful as end-user 165 | // documentation. 166 | // 167 | package build 168 | -------------------------------------------------------------------------------- /go/build/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package build 6 | 7 | import ( 8 | "bufio" 9 | "errors" 10 | "io" 11 | "unicode/utf8" 12 | ) 13 | 14 | type importReader struct { 15 | b *bufio.Reader 16 | buf []byte 17 | peek byte 18 | err error 19 | eof bool 20 | nerr int 21 | } 22 | 23 | func isIdent(c byte) bool { 24 | return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf 25 | } 26 | 27 | var ( 28 | errSyntax = errors.New("syntax error") 29 | errNUL = errors.New("unexpected NUL in input") 30 | ) 31 | 32 | // syntaxError records a syntax error, but only if an I/O error has not already been recorded. 33 | func (r *importReader) syntaxError() { 34 | if r.err == nil { 35 | r.err = errSyntax 36 | } 37 | } 38 | 39 | // readByte reads the next byte from the input, saves it in buf, and returns it. 40 | // If an error occurs, readByte records the error in r.err and returns 0. 41 | func (r *importReader) readByte() byte { 42 | c, err := r.b.ReadByte() 43 | if err == nil { 44 | r.buf = append(r.buf, c) 45 | if c == 0 { 46 | err = errNUL 47 | } 48 | } 49 | if err != nil { 50 | if err == io.EOF { 51 | r.eof = true 52 | } else if r.err == nil { 53 | r.err = err 54 | } 55 | c = 0 56 | } 57 | return c 58 | } 59 | 60 | // peekByte returns the next byte from the input reader but does not advance beyond it. 61 | // If skipSpace is set, peekByte skips leading spaces and comments. 62 | func (r *importReader) peekByte(skipSpace bool) byte { 63 | if r.err != nil { 64 | if r.nerr++; r.nerr > 10000 { 65 | panic("github.com/faiface/generics/go/build: import reader looping") 66 | } 67 | return 0 68 | } 69 | 70 | // Use r.peek as first input byte. 71 | // Don't just return r.peek here: it might have been left by peekByte(false) 72 | // and this might be peekByte(true). 73 | c := r.peek 74 | if c == 0 { 75 | c = r.readByte() 76 | } 77 | for r.err == nil && !r.eof { 78 | if skipSpace { 79 | // For the purposes of this reader, semicolons are never necessary to 80 | // understand the input and are treated as spaces. 81 | switch c { 82 | case ' ', '\f', '\t', '\r', '\n', ';': 83 | c = r.readByte() 84 | continue 85 | 86 | case '/': 87 | c = r.readByte() 88 | if c == '/' { 89 | for c != '\n' && r.err == nil && !r.eof { 90 | c = r.readByte() 91 | } 92 | } else if c == '*' { 93 | var c1 byte 94 | for (c != '*' || c1 != '/') && r.err == nil { 95 | if r.eof { 96 | r.syntaxError() 97 | } 98 | c, c1 = c1, r.readByte() 99 | } 100 | } else { 101 | r.syntaxError() 102 | } 103 | c = r.readByte() 104 | continue 105 | } 106 | } 107 | break 108 | } 109 | r.peek = c 110 | return r.peek 111 | } 112 | 113 | // nextByte is like peekByte but advances beyond the returned byte. 114 | func (r *importReader) nextByte(skipSpace bool) byte { 115 | c := r.peekByte(skipSpace) 116 | r.peek = 0 117 | return c 118 | } 119 | 120 | // readKeyword reads the given keyword from the input. 121 | // If the keyword is not present, readKeyword records a syntax error. 122 | func (r *importReader) readKeyword(kw string) { 123 | r.peekByte(true) 124 | for i := 0; i < len(kw); i++ { 125 | if r.nextByte(false) != kw[i] { 126 | r.syntaxError() 127 | return 128 | } 129 | } 130 | if isIdent(r.peekByte(false)) { 131 | r.syntaxError() 132 | } 133 | } 134 | 135 | // readIdent reads an identifier from the input. 136 | // If an identifier is not present, readIdent records a syntax error. 137 | func (r *importReader) readIdent() { 138 | c := r.peekByte(true) 139 | if !isIdent(c) { 140 | r.syntaxError() 141 | return 142 | } 143 | for isIdent(r.peekByte(false)) { 144 | r.peek = 0 145 | } 146 | } 147 | 148 | // readString reads a quoted string literal from the input. 149 | // If an identifier is not present, readString records a syntax error. 150 | func (r *importReader) readString(save *[]string) { 151 | switch r.nextByte(true) { 152 | case '`': 153 | start := len(r.buf) - 1 154 | for r.err == nil { 155 | if r.nextByte(false) == '`' { 156 | if save != nil { 157 | *save = append(*save, string(r.buf[start:])) 158 | } 159 | break 160 | } 161 | if r.eof { 162 | r.syntaxError() 163 | } 164 | } 165 | case '"': 166 | start := len(r.buf) - 1 167 | for r.err == nil { 168 | c := r.nextByte(false) 169 | if c == '"' { 170 | if save != nil { 171 | *save = append(*save, string(r.buf[start:])) 172 | } 173 | break 174 | } 175 | if r.eof || c == '\n' { 176 | r.syntaxError() 177 | } 178 | if c == '\\' { 179 | r.nextByte(false) 180 | } 181 | } 182 | default: 183 | r.syntaxError() 184 | } 185 | } 186 | 187 | // readImport reads an import clause - optional identifier followed by quoted string - 188 | // from the input. 189 | func (r *importReader) readImport(imports *[]string) { 190 | c := r.peekByte(true) 191 | if c == '.' { 192 | r.peek = 0 193 | } else if isIdent(c) { 194 | r.readIdent() 195 | } 196 | r.readString(imports) 197 | } 198 | 199 | // readComments is like ioutil.ReadAll, except that it only reads the leading 200 | // block of comments in the file. 201 | func readComments(f io.Reader) ([]byte, error) { 202 | r := &importReader{b: bufio.NewReader(f)} 203 | r.peekByte(true) 204 | if r.err == nil && !r.eof { 205 | // Didn't reach EOF, so must have found a non-space byte. Remove it. 206 | r.buf = r.buf[:len(r.buf)-1] 207 | } 208 | return r.buf, r.err 209 | } 210 | 211 | // readImports is like ioutil.ReadAll, except that it expects a Go file as input 212 | // and stops reading the input once the imports have completed. 213 | func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) { 214 | r := &importReader{b: bufio.NewReader(f)} 215 | 216 | r.readKeyword("package") 217 | r.readIdent() 218 | for r.peekByte(true) == 'i' { 219 | r.readKeyword("import") 220 | if r.peekByte(true) == '(' { 221 | r.nextByte(false) 222 | for r.peekByte(true) != ')' && r.err == nil { 223 | r.readImport(imports) 224 | } 225 | r.nextByte(false) 226 | } else { 227 | r.readImport(imports) 228 | } 229 | } 230 | 231 | // If we stopped successfully before EOF, we read a byte that told us we were done. 232 | // Return all but that last byte, which would cause a syntax error if we let it through. 233 | if r.err == nil && !r.eof { 234 | return r.buf[:len(r.buf)-1], nil 235 | } 236 | 237 | // If we stopped for a syntax error, consume the whole file so that 238 | // we are sure we don't change the errors that go/parser returns. 239 | if r.err == errSyntax && !reportSyntaxError { 240 | r.err = nil 241 | for r.err == nil && !r.eof { 242 | r.readByte() 243 | } 244 | } 245 | 246 | return r.buf, r.err 247 | } 248 | -------------------------------------------------------------------------------- /go/build/syslist.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package build 6 | 7 | const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows zos " 8 | const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc s390 s390x sparc sparc64 " 9 | -------------------------------------------------------------------------------- /go/build/zcgo.go: -------------------------------------------------------------------------------- 1 | // Code generated by go tool dist; DO NOT EDIT. 2 | 3 | package build 4 | 5 | const defaultCGO_ENABLED = "" 6 | 7 | var cgoEnabled = map[string]bool{ 8 | "android/386": true, 9 | "android/amd64": true, 10 | "android/arm": true, 11 | "android/arm64": true, 12 | "darwin/386": true, 13 | "darwin/amd64": true, 14 | "darwin/arm": true, 15 | "darwin/arm64": true, 16 | "dragonfly/amd64": true, 17 | "freebsd/386": true, 18 | "freebsd/amd64": true, 19 | "linux/386": true, 20 | "linux/amd64": true, 21 | "linux/arm": true, 22 | "linux/arm64": true, 23 | "linux/mips": true, 24 | "linux/mips64": true, 25 | "linux/mips64le": true, 26 | "linux/mipsle": true, 27 | "linux/ppc64le": true, 28 | "linux/s390x": true, 29 | "netbsd/386": true, 30 | "netbsd/amd64": true, 31 | "netbsd/arm": true, 32 | "openbsd/386": true, 33 | "openbsd/amd64": true, 34 | "solaris/amd64": true, 35 | "windows/386": true, 36 | "windows/amd64": true, 37 | } 38 | -------------------------------------------------------------------------------- /go/doc/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2009 The Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # Script to test heading detection heuristic 6 | headscan: headscan.go 7 | go build headscan.go 8 | -------------------------------------------------------------------------------- /go/doc/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package doc extracts source code documentation from a Go AST. 6 | package doc 7 | 8 | import ( 9 | "github.com/faiface/generics/go/ast" 10 | "github.com/faiface/generics/go/token" 11 | ) 12 | 13 | // Package is the documentation for an entire package. 14 | type Package struct { 15 | Doc string 16 | Name string 17 | ImportPath string 18 | Imports []string 19 | Filenames []string 20 | Notes map[string][]*Note 21 | 22 | // Deprecated: For backward compatibility Bugs is still populated, 23 | // but all new code should use Notes instead. 24 | Bugs []string 25 | 26 | // declarations 27 | Consts []*Value 28 | Types []*Type 29 | Vars []*Value 30 | Funcs []*Func 31 | } 32 | 33 | // Value is the documentation for a (possibly grouped) var or const declaration. 34 | type Value struct { 35 | Doc string 36 | Names []string // var or const names in declaration order 37 | Decl *ast.GenDecl 38 | 39 | order int 40 | } 41 | 42 | // Type is the documentation for a type declaration. 43 | type Type struct { 44 | Doc string 45 | Name string 46 | Decl *ast.GenDecl 47 | 48 | // associated declarations 49 | Consts []*Value // sorted list of constants of (mostly) this type 50 | Vars []*Value // sorted list of variables of (mostly) this type 51 | Funcs []*Func // sorted list of functions returning this type 52 | Methods []*Func // sorted list of methods (including embedded ones) of this type 53 | } 54 | 55 | // Func is the documentation for a func declaration. 56 | type Func struct { 57 | Doc string 58 | Name string 59 | Decl *ast.FuncDecl 60 | 61 | // methods 62 | // (for functions, these fields have the respective zero value) 63 | Recv string // actual receiver "T" or "*T" 64 | Orig string // original receiver "T" or "*T" 65 | Level int // embedding level; 0 means not embedded 66 | } 67 | 68 | // A Note represents a marked comment starting with "MARKER(uid): note body". 69 | // Any note with a marker of 2 or more upper case [A-Z] letters and a uid of 70 | // at least one character is recognized. The ":" following the uid is optional. 71 | // Notes are collected in the Package.Notes map indexed by the notes marker. 72 | type Note struct { 73 | Pos, End token.Pos // position range of the comment containing the marker 74 | UID string // uid found with the marker 75 | Body string // note body text 76 | } 77 | 78 | // Mode values control the operation of New. 79 | type Mode int 80 | 81 | const ( 82 | // extract documentation for all package-level declarations, 83 | // not just exported ones 84 | AllDecls Mode = 1 << iota 85 | 86 | // show all embedded methods, not just the ones of 87 | // invisible (unexported) anonymous fields 88 | AllMethods 89 | ) 90 | 91 | // New computes the package documentation for the given package AST. 92 | // New takes ownership of the AST pkg and may edit or overwrite it. 93 | // 94 | func New(pkg *ast.Package, importPath string, mode Mode) *Package { 95 | var r reader 96 | r.readPackage(pkg, mode) 97 | r.computeMethodSets() 98 | r.cleanupTypes() 99 | return &Package{ 100 | Doc: r.doc, 101 | Name: pkg.Name, 102 | ImportPath: importPath, 103 | Imports: sortedKeys(r.imports), 104 | Filenames: r.filenames, 105 | Notes: r.notes, 106 | Bugs: noteBodies(r.notes["BUG"]), 107 | Consts: sortedValues(r.values, token.CONST), 108 | Types: sortedTypes(r.types, mode&AllMethods != 0), 109 | Vars: sortedValues(r.values, token.VAR), 110 | Funcs: sortedFuncs(r.funcs, true), 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /go/doc/filter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package doc 6 | 7 | import "github.com/faiface/generics/go/ast" 8 | 9 | type Filter func(string) bool 10 | 11 | func matchFields(fields *ast.FieldList, f Filter) bool { 12 | if fields != nil { 13 | for _, field := range fields.List { 14 | for _, name := range field.Names { 15 | if f(name.Name) { 16 | return true 17 | } 18 | } 19 | } 20 | } 21 | return false 22 | } 23 | 24 | func matchDecl(d *ast.GenDecl, f Filter) bool { 25 | for _, d := range d.Specs { 26 | switch v := d.(type) { 27 | case *ast.ValueSpec: 28 | for _, name := range v.Names { 29 | if f(name.Name) { 30 | return true 31 | } 32 | } 33 | case *ast.TypeSpec: 34 | if f(v.Name.Name) { 35 | return true 36 | } 37 | switch t := v.Type.(type) { 38 | case *ast.StructType: 39 | if matchFields(t.Fields, f) { 40 | return true 41 | } 42 | case *ast.InterfaceType: 43 | if matchFields(t.Methods, f) { 44 | return true 45 | } 46 | } 47 | } 48 | } 49 | return false 50 | } 51 | 52 | func filterValues(a []*Value, f Filter) []*Value { 53 | w := 0 54 | for _, vd := range a { 55 | if matchDecl(vd.Decl, f) { 56 | a[w] = vd 57 | w++ 58 | } 59 | } 60 | return a[0:w] 61 | } 62 | 63 | func filterFuncs(a []*Func, f Filter) []*Func { 64 | w := 0 65 | for _, fd := range a { 66 | if f(fd.Name) { 67 | a[w] = fd 68 | w++ 69 | } 70 | } 71 | return a[0:w] 72 | } 73 | 74 | func filterTypes(a []*Type, f Filter) []*Type { 75 | w := 0 76 | for _, td := range a { 77 | n := 0 // number of matches 78 | if matchDecl(td.Decl, f) { 79 | n = 1 80 | } else { 81 | // type name doesn't match, but we may have matching consts, vars, factories or methods 82 | td.Consts = filterValues(td.Consts, f) 83 | td.Vars = filterValues(td.Vars, f) 84 | td.Funcs = filterFuncs(td.Funcs, f) 85 | td.Methods = filterFuncs(td.Methods, f) 86 | n += len(td.Consts) + len(td.Vars) + len(td.Funcs) + len(td.Methods) 87 | } 88 | if n > 0 { 89 | a[w] = td 90 | w++ 91 | } 92 | } 93 | return a[0:w] 94 | } 95 | 96 | // Filter eliminates documentation for names that don't pass through the filter f. 97 | // TODO(gri): Recognize "Type.Method" as a name. 98 | // 99 | func (p *Package) Filter(f Filter) { 100 | p.Consts = filterValues(p.Consts, f) 101 | p.Vars = filterValues(p.Vars, f) 102 | p.Types = filterTypes(p.Types, f) 103 | p.Funcs = filterFuncs(p.Funcs, f) 104 | p.Doc = "" // don't show top-level package doc 105 | } 106 | -------------------------------------------------------------------------------- /go/doc/headscan.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build ignore 6 | 7 | /* 8 | The headscan command extracts comment headings from package files; 9 | it is used to detect false positives which may require an adjustment 10 | to the comment formatting heuristics in comment.go. 11 | 12 | Usage: headscan [-root root_directory] 13 | 14 | By default, the $GOROOT/src directory is scanned. 15 | */ 16 | package main 17 | 18 | import ( 19 | "bytes" 20 | "flag" 21 | "fmt" 22 | "github.com/faiface/generics/go/doc" 23 | "github.com/faiface/generics/go/parser" 24 | "github.com/faiface/generics/go/token" 25 | "os" 26 | "path/filepath" 27 | "regexp" 28 | "runtime" 29 | "strings" 30 | ) 31 | 32 | var ( 33 | root = flag.String("root", filepath.Join(runtime.GOROOT(), "src"), "root of filesystem tree to scan") 34 | verbose = flag.Bool("v", false, "verbose mode") 35 | ) 36 | 37 | // ToHTML in comment.go assigns a (possibly blank) ID to each heading 38 | var html_h = regexp.MustCompile(`