summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/ir/mknode.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/ir/mknode.go')
-rw-r--r--src/cmd/compile/internal/ir/mknode.go229
1 files changed, 229 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ir/mknode.go b/src/cmd/compile/internal/ir/mknode.go
new file mode 100644
index 0000000..5a0aaad
--- /dev/null
+++ b/src/cmd/compile/internal/ir/mknode.go
@@ -0,0 +1,229 @@
+// 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
+}