| // Copyright 2020 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| //go:build ignore | |
| // +build ignore | |
| package main | |
| import ( | |
| "bytes" | |
| "fmt" | |
| "go/format" | |
| "go/types" | |
| "io/ioutil" | |
| "log" | |
| "reflect" | |
| "sort" | |
| "strings" | |
| "golang.org/x/tools/go/packages" | |
| ) | |
| var irPkg *types.Package | |
| var buf bytes.Buffer | |
| func main() { | |
| cfg := &packages.Config{ | |
| Mode: packages.NeedSyntax | packages.NeedTypes, | |
| } | |
| pkgs, err := packages.Load(cfg, "cmd/compile/internal/ir") | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| irPkg = pkgs[0].Types | |
| fmt.Fprintln(&buf, "// Code generated by mknode.go. DO NOT EDIT.") | |
| fmt.Fprintln(&buf) | |
| fmt.Fprintln(&buf, "package ir") | |
| fmt.Fprintln(&buf) | |
| fmt.Fprintln(&buf, `import "fmt"`) | |
| scope := irPkg.Scope() | |
| for _, name := range scope.Names() { | |
| if strings.HasPrefix(name, "mini") { | |
| continue | |
| } | |
| obj, ok := scope.Lookup(name).(*types.TypeName) | |
| if !ok { | |
| continue | |
| } | |
| typ := obj.Type().(*types.Named) | |
| if !implementsNode(types.NewPointer(typ)) { | |
| continue | |
| } | |
| fmt.Fprintf(&buf, "\n") | |
| fmt.Fprintf(&buf, "func (n *%s) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }\n", name) | |
| switch name { | |
| case "Name", "Func": | |
| // Too specialized to automate. | |
| continue | |
| } | |
| forNodeFields(typ, | |
| "func (n *%[1]s) copy() Node { c := *n\n", | |
| "", | |
| "c.%[1]s = copy%[2]s(c.%[1]s)", | |
| "return &c }\n") | |
| forNodeFields(typ, | |
| "func (n *%[1]s) doChildren(do func(Node) bool) bool {\n", | |
| "if n.%[1]s != nil && do(n.%[1]s) { return true }", | |
| "if do%[2]s(n.%[1]s, do) { return true }", | |
| "return false }\n") | |
| forNodeFields(typ, | |
| "func (n *%[1]s) editChildren(edit func(Node) Node) {\n", | |
| "if n.%[1]s != nil { n.%[1]s = edit(n.%[1]s).(%[2]s) }", | |
| "edit%[2]s(n.%[1]s, edit)", | |
| "}\n") | |
| } | |
| makeHelpers() | |
| out, err := format.Source(buf.Bytes()) | |
| if err != nil { | |
| // write out mangled source so we can see the bug. | |
| out = buf.Bytes() | |
| } | |
| err = ioutil.WriteFile("node_gen.go", out, 0666) | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| } | |
| // needHelper maps needed slice helpers from their base name to their | |
| // respective slice-element type. | |
| var needHelper = map[string]string{} | |
| func makeHelpers() { | |
| var names []string | |
| for name := range needHelper { | |
| names = append(names, name) | |
| } | |
| sort.Strings(names) | |
| for _, name := range names { | |
| fmt.Fprintf(&buf, sliceHelperTmpl, name, needHelper[name]) | |
| } | |
| } | |
| const sliceHelperTmpl = ` | |
| func copy%[1]s(list []%[2]s) []%[2]s { | |
| if list == nil { | |
| return nil | |
| } | |
| c := make([]%[2]s, len(list)) | |
| copy(c, list) | |
| return c | |
| } | |
| func do%[1]s(list []%[2]s, do func(Node) bool) bool { | |
| for _, x := range list { | |
| if x != nil && do(x) { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| func edit%[1]s(list []%[2]s, edit func(Node) Node) { | |
| for i, x := range list { | |
| if x != nil { | |
| list[i] = edit(x).(%[2]s) | |
| } | |
| } | |
| } | |
| ` | |
| func forNodeFields(named *types.Named, prologue, singleTmpl, sliceTmpl, epilogue string) { | |
| fmt.Fprintf(&buf, prologue, named.Obj().Name()) | |
| anyField(named.Underlying().(*types.Struct), func(f *types.Var) bool { | |
| if f.Embedded() { | |
| return false | |
| } | |
| name, typ := f.Name(), f.Type() | |
| slice, _ := typ.Underlying().(*types.Slice) | |
| if slice != nil { | |
| typ = slice.Elem() | |
| } | |
| tmpl, what := singleTmpl, types.TypeString(typ, types.RelativeTo(irPkg)) | |
| if implementsNode(typ) { | |
| if slice != nil { | |
| helper := strings.TrimPrefix(what, "*") + "s" | |
| needHelper[helper] = what | |
| tmpl, what = sliceTmpl, helper | |
| } | |
| } else if what == "*Field" { | |
| // Special case for *Field. | |
| tmpl = sliceTmpl | |
| if slice != nil { | |
| what = "Fields" | |
| } else { | |
| what = "Field" | |
| } | |
| } else { | |
| return false | |
| } | |
| if tmpl == "" { | |
| return false | |
| } | |
| // Allow template to not use all arguments without | |
| // upsetting fmt.Printf. | |
| s := fmt.Sprintf(tmpl+"\x00 %[1]s %[2]s", name, what) | |
| fmt.Fprintln(&buf, s[:strings.LastIndex(s, "\x00")]) | |
| return false | |
| }) | |
| fmt.Fprintf(&buf, epilogue) | |
| } | |
| func implementsNode(typ types.Type) bool { | |
| if _, ok := typ.Underlying().(*types.Interface); ok { | |
| // TODO(mdempsky): Check the interface implements Node. | |
| // Worst case, node_gen.go will fail to compile if we're wrong. | |
| return true | |
| } | |
| if ptr, ok := typ.(*types.Pointer); ok { | |
| if str, ok := ptr.Elem().Underlying().(*types.Struct); ok { | |
| return anyField(str, func(f *types.Var) bool { | |
| return f.Embedded() && f.Name() == "miniNode" | |
| }) | |
| } | |
| } | |
| return false | |
| } | |
| func anyField(typ *types.Struct, pred func(f *types.Var) bool) bool { | |
| for i, n := 0, typ.NumFields(); i < n; i++ { | |
| if value, ok := reflect.StructTag(typ.Tag(i)).Lookup("mknode"); ok { | |
| if value != "-" { | |
| panic(fmt.Sprintf("unexpected tag value: %q", value)) | |
| } | |
| continue | |
| } | |
| f := typ.Field(i) | |
| if pred(f) { | |
| return true | |
| } | |
| if f.Embedded() { | |
| if typ, ok := f.Type().Underlying().(*types.Struct); ok { | |
| if anyField(typ, pred) { | |
| return true | |
| } | |
| } | |
| } | |
| } | |
| return false | |
| } |