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  }