diff options
Diffstat (limited to 'src/cmd/compile/internal/ir/mknode.go')
-rw-r--r-- | src/cmd/compile/internal/ir/mknode.go | 366 |
1 files changed, 366 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..716e843 --- /dev/null +++ b/src/cmd/compile/internal/ir/mknode.go @@ -0,0 +1,366 @@ +// Copyright 2022 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 + +// Note: this program must be run in this directory. +// go run mknode.go + +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io/fs" + "log" + "os" + "sort" + "strings" +) + +var fset = token.NewFileSet() + +var buf bytes.Buffer + +// concreteNodes contains all concrete types in the package that implement Node +// (except for the mini* types). +var concreteNodes []*ast.TypeSpec + +// interfaceNodes contains all interface types in the package that implement Node. +var interfaceNodes []*ast.TypeSpec + +// mini contains the embeddable mini types (miniNode, miniExpr, and miniStmt). +var mini = map[string]*ast.TypeSpec{} + +// implementsNode reports whether the type t is one which represents a Node +// in the AST. +func implementsNode(t ast.Expr) bool { + id, ok := t.(*ast.Ident) + if !ok { + return false // only named types + } + for _, ts := range interfaceNodes { + if ts.Name.Name == id.Name { + return true + } + } + for _, ts := range concreteNodes { + if ts.Name.Name == id.Name { + return true + } + } + return false +} + +func isMini(t ast.Expr) bool { + id, ok := t.(*ast.Ident) + return ok && mini[id.Name] != nil +} + +func isNamedType(t ast.Expr, name string) bool { + if id, ok := t.(*ast.Ident); ok { + if id.Name == name { + return true + } + } + return false +} + +func main() { + 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"`) + + filter := func(file fs.FileInfo) bool { + return !strings.HasPrefix(file.Name(), "mknode") + } + pkgs, err := parser.ParseDir(fset, ".", filter, 0) + if err != nil { + panic(err) + } + pkg := pkgs["ir"] + + // Find all the mini types. These let us determine which + // concrete types implement Node, so we need to find them first. + for _, f := range pkg.Files { + for _, d := range f.Decls { + g, ok := d.(*ast.GenDecl) + if !ok { + continue + } + for _, s := range g.Specs { + t, ok := s.(*ast.TypeSpec) + if !ok { + continue + } + if strings.HasPrefix(t.Name.Name, "mini") { + mini[t.Name.Name] = t + // Double-check that it is or embeds miniNode. + if t.Name.Name != "miniNode" { + s := t.Type.(*ast.StructType) + if !isNamedType(s.Fields.List[0].Type, "miniNode") { + panic(fmt.Sprintf("can't find miniNode in %s", t.Name.Name)) + } + } + } + } + } + } + + // Find all the declarations of concrete types that implement Node. + for _, f := range pkg.Files { + for _, d := range f.Decls { + g, ok := d.(*ast.GenDecl) + if !ok { + continue + } + for _, s := range g.Specs { + t, ok := s.(*ast.TypeSpec) + if !ok { + continue + } + if strings.HasPrefix(t.Name.Name, "mini") { + // We don't treat the mini types as + // concrete implementations of Node + // (even though they are) because + // we only use them by embedding them. + continue + } + if isConcreteNode(t) { + concreteNodes = append(concreteNodes, t) + } + if isInterfaceNode(t) { + interfaceNodes = append(interfaceNodes, t) + } + } + } + } + // Sort for deterministic output. + sort.Slice(concreteNodes, func(i, j int) bool { + return concreteNodes[i].Name.Name < concreteNodes[j].Name.Name + }) + // Generate code for each concrete type. + for _, t := range concreteNodes { + processType(t) + } + // Add some helpers. + generateHelpers() + + // Format and write output. + out, err := format.Source(buf.Bytes()) + if err != nil { + // write out mangled source so we can see the bug. + out = buf.Bytes() + } + err = os.WriteFile("node_gen.go", out, 0666) + if err != nil { + log.Fatal(err) + } +} + +// isConcreteNode reports whether the type t is a concrete type +// implementing Node. +func isConcreteNode(t *ast.TypeSpec) bool { + s, ok := t.Type.(*ast.StructType) + if !ok { + return false + } + for _, f := range s.Fields.List { + if isMini(f.Type) { + return true + } + } + return false +} + +// isInterfaceNode reports whether the type t is an interface type +// implementing Node (including Node itself). +func isInterfaceNode(t *ast.TypeSpec) bool { + s, ok := t.Type.(*ast.InterfaceType) + if !ok { + return false + } + if t.Name.Name == "Node" { + return true + } + if t.Name.Name == "OrigNode" || t.Name.Name == "InitNode" { + // These we exempt from consideration (fields of + // this type don't need to be walked or copied). + return false + } + + // Look for embedded Node type. + // Note that this doesn't handle multi-level embedding, but + // we have none of that at the moment. + for _, f := range s.Methods.List { + if len(f.Names) != 0 { + continue + } + if isNamedType(f.Type, "Node") { + return true + } + } + return false +} + +func processType(t *ast.TypeSpec) { + name := t.Name.Name + 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. + return + } + + s := t.Type.(*ast.StructType) + fields := s.Fields.List + + // Expand any embedded fields. + for i := 0; i < len(fields); i++ { + f := fields[i] + if len(f.Names) != 0 { + continue // not embedded + } + if isMini(f.Type) { + // Insert the fields of the embedded type into the main type. + // (It would be easier just to append, but inserting in place + // matches the old mknode behavior.) + ss := mini[f.Type.(*ast.Ident).Name].Type.(*ast.StructType) + var f2 []*ast.Field + f2 = append(f2, fields[:i]...) + f2 = append(f2, ss.Fields.List...) + f2 = append(f2, fields[i+1:]...) + fields = f2 + i-- + continue + } else if isNamedType(f.Type, "origNode") { + // Ignore this field + copy(fields[i:], fields[i+1:]) + fields = fields[:len(fields)-1] + i-- + continue + } else { + panic("unknown embedded field " + fmt.Sprintf("%v", f.Type)) + } + } + // Process fields. + var copyBody strings.Builder + var doChildrenBody strings.Builder + var editChildrenBody strings.Builder + var editChildrenWithHiddenBody strings.Builder + for _, f := range fields { + names := f.Names + ft := f.Type + hidden := false + if f.Tag != nil { + tag := f.Tag.Value[1 : len(f.Tag.Value)-1] + if strings.HasPrefix(tag, "mknode:") { + if tag[7:] == "\"-\"" { + if !isNamedType(ft, "Node") { + continue + } + hidden = true + } else { + panic(fmt.Sprintf("unexpected tag value: %s", tag)) + } + } + } + if isNamedType(ft, "Nodes") { + // Nodes == []Node + ft = &ast.ArrayType{Elt: &ast.Ident{Name: "Node"}} + } + isSlice := false + if a, ok := ft.(*ast.ArrayType); ok && a.Len == nil { + isSlice = true + ft = a.Elt + } + isPtr := false + if p, ok := ft.(*ast.StarExpr); ok { + isPtr = true + ft = p.X + } + if !implementsNode(ft) { + continue + } + for _, name := range names { + ptr := "" + if isPtr { + ptr = "*" + } + if isSlice { + fmt.Fprintf(&editChildrenWithHiddenBody, + "edit%ss(n.%s, edit)\n", ft, name) + } else { + fmt.Fprintf(&editChildrenWithHiddenBody, + "if n.%s != nil {\nn.%s = edit(n.%s).(%s%s)\n}\n", name, name, name, ptr, ft) + } + if hidden { + continue + } + if isSlice { + fmt.Fprintf(©Body, "c.%s = copy%ss(c.%s)\n", name, ft, name) + fmt.Fprintf(&doChildrenBody, + "if do%ss(n.%s, do) {\nreturn true\n}\n", ft, name) + fmt.Fprintf(&editChildrenBody, + "edit%ss(n.%s, edit)\n", ft, name) + } else { + fmt.Fprintf(&doChildrenBody, + "if n.%s != nil && do(n.%s) {\nreturn true\n}\n", name, name) + fmt.Fprintf(&editChildrenBody, + "if n.%s != nil {\nn.%s = edit(n.%s).(%s%s)\n}\n", name, name, name, ptr, ft) + } + } + } + fmt.Fprintf(&buf, "func (n *%s) copy() Node {\nc := *n\n", name) + buf.WriteString(copyBody.String()) + fmt.Fprintf(&buf, "return &c\n}\n") + fmt.Fprintf(&buf, "func (n *%s) doChildren(do func(Node) bool) bool {\n", name) + buf.WriteString(doChildrenBody.String()) + fmt.Fprintf(&buf, "return false\n}\n") + fmt.Fprintf(&buf, "func (n *%s) editChildren(edit func(Node) Node) {\n", name) + buf.WriteString(editChildrenBody.String()) + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "func (n *%s) editChildrenWithHidden(edit func(Node) Node) {\n", name) + buf.WriteString(editChildrenWithHiddenBody.String()) + fmt.Fprintf(&buf, "}\n") +} + +func generateHelpers() { + for _, typ := range []string{"CaseClause", "CommClause", "Name", "Node", "Ntype"} { + ptr := "*" + if typ == "Node" || typ == "Ntype" { + ptr = "" // interfaces don't need * + } + fmt.Fprintf(&buf, "\n") + fmt.Fprintf(&buf, "func copy%ss(list []%s%s) []%s%s {\n", typ, ptr, typ, ptr, typ) + fmt.Fprintf(&buf, "if list == nil { return nil }\n") + fmt.Fprintf(&buf, "c := make([]%s%s, len(list))\n", ptr, typ) + fmt.Fprintf(&buf, "copy(c, list)\n") + fmt.Fprintf(&buf, "return c\n") + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "func do%ss(list []%s%s, do func(Node) bool) bool {\n", typ, ptr, typ) + fmt.Fprintf(&buf, "for _, x := range list {\n") + fmt.Fprintf(&buf, "if x != nil && do(x) {\n") + fmt.Fprintf(&buf, "return true\n") + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "return false\n") + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "func edit%ss(list []%s%s, edit func(Node) Node) {\n", typ, ptr, typ) + fmt.Fprintf(&buf, "for i, x := range list {\n") + fmt.Fprintf(&buf, "if x != nil {\n") + fmt.Fprintf(&buf, "list[i] = edit(x).(%s%s)\n", ptr, typ) + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "}\n") + } +} |