github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/sql/sem/tree/evalgen/eval_gen.go (about) 1 // Copyright 2022 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 // Command evalgen is used to generate interfaces and "visitor" methods for 12 // expr and op evaluation. 13 // 14 // Generated files can be regenerated with either of the follow commands: 15 // 16 // ./dev generate go 17 // go generate ./pkg/sql/sem/tree 18 package main 19 20 import ( 21 "flag" 22 "fmt" 23 "go/ast" 24 "go/parser" 25 "go/token" 26 "os" 27 "path/filepath" 28 29 "github.com/cockroachdb/errors" 30 "golang.org/x/tools/go/ast/inspector" 31 ) 32 33 const ( 34 exprEvalFileName = "eval_expr_generated.go" 35 opEvalFileName = "eval_op_generated.go" 36 ) 37 38 var generated = stringSet{ 39 exprEvalFileName: {}, 40 opEvalFileName: {}, 41 } 42 43 func main() { 44 out := flag.String("out", ".", "path to write files") 45 flag.Parse() 46 fileSet := token.NewFileSet() 47 var files []*ast.File 48 byName := map[string]*ast.File{} 49 for _, arg := range flag.Args() { 50 expanded, err := filepath.Glob(arg) 51 if err != nil { 52 panic(fmt.Sprintf("failed to expand %s: %v", arg, err)) 53 } 54 for _, fp := range expanded { 55 name := filepath.Base(fp) 56 if _, exists := byName[name]; generated.contains(name) || exists { 57 continue 58 } 59 f, err := parser.ParseFile(fileSet, fp, nil, 0) 60 if err != nil { 61 panic(fmt.Sprintf("failed to parse %s: %v", arg, err)) 62 } 63 files = append(files, f) 64 byName[name] = f 65 } 66 } 67 if err := generateExprEval( 68 filepath.Join(*out, exprEvalFileName), files, 69 ); err != nil { 70 panic(err) 71 } 72 if err := generateOpsFile( 73 filepath.Join(*out, opEvalFileName), files, byName, 74 ); err != nil { 75 panic(err) 76 } 77 } 78 79 func writeFile(name string, fn func(f *os.File) error) error { 80 f, err := os.Create(name) 81 if err != nil { 82 return err 83 } 84 defer func() { 85 closeErr := f.Close() 86 if err == nil { 87 err = closeErr 88 } 89 if err != nil { 90 _ = os.Remove(name) 91 } 92 }() 93 return fn(f) 94 } 95 96 const header = `// Copyright 2022 The Cockroach Authors. 97 // 98 // Use of this software is governed by the Business Source License 99 // included in the file licenses/BSL.txt. 100 // 101 // As of the Change Date specified in that file, in accordance with 102 // the Business Source License, use of this software will be governed 103 // by the Apache License, Version 2.0, included in the file 104 // licenses/APL.txt. 105 106 // Code generated by eval_gen.go. DO NOT EDIT. 107 // Regenerate this file with either of the following commands: 108 // 109 // ./dev generate go 110 // go generate ./pkg/sql/sem/tree 111 // 112 // If you use the dev command and you have added a new tree expression, like 113 // tree.XYZ in a new file, you may get the confusing error: undefined: XYZ. 114 // Run './dev generate bazel' to fix this. 115 package tree 116 117 import "context" 118 ` 119 120 func findTypesWithMethod(ins *inspector.Inspector, filter func(decl *ast.FuncDecl) bool) stringSet { 121 ss := stringSet{} 122 ins.Preorder([]ast.Node{(*ast.FuncDecl)(nil)}, func(node ast.Node) { 123 fd := node.(*ast.FuncDecl) 124 if fd.Recv == nil || !filter(fd) { 125 return 126 } 127 ss.add(extractMethodReceiverName(fd)) 128 }) 129 return ss 130 } 131 132 func extractMethodReceiver(fd *ast.FuncDecl) (name string, isPtr bool) { 133 switch n := fd.Recv.List[0].Type.(type) { 134 case *ast.StarExpr: 135 return n.X.(*ast.Ident).Name, true 136 case *ast.Ident: 137 return n.Name, false 138 default: 139 panic(errors.Errorf("unexpected receiver type node %T", n)) 140 } 141 } 142 143 func extractMethodReceiverName(fd *ast.FuncDecl) (name string) { 144 name, _ = extractMethodReceiver(fd) 145 return name 146 } 147 148 func findStructTypesWithEmbeddedType( 149 ins *inspector.Inspector, filter func(name string) bool, embeddedStruct string, 150 ) stringSet { 151 hasEmbedded := stringSet{} 152 ins.Preorder([]ast.Node{ 153 (*ast.TypeSpec)(nil), 154 }, func(node ast.Node) { 155 ts := node.(*ast.TypeSpec) 156 if !filter(ts.Name.Name) { 157 return 158 } 159 st, ok := ts.Type.(*ast.StructType) 160 if !ok { 161 return 162 } 163 for _, f := range st.Fields.List { 164 if len(f.Names) > 0 { 165 continue 166 } 167 id, ok := f.Type.(*ast.Ident) 168 if !ok { 169 continue 170 } 171 if id.Name == embeddedStruct { 172 hasEmbedded.add(ts.Name.Name) 173 return 174 } 175 } 176 }) 177 return hasEmbedded 178 }