// 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 }