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