github.com/goccy/go-json@v0.10.3-0.20240509105655-5e2ae3f23c1d/internal/cmd/generator/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/format"
     7  	"go/parser"
     8  	"go/printer"
     9  	"go/token"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	"text/template"
    15  )
    16  
    17  type opType struct {
    18  	Op   string
    19  	Code string
    20  }
    21  
    22  func createOpType(op, code string) opType {
    23  	return opType{
    24  		Op:   op,
    25  		Code: code,
    26  	}
    27  }
    28  
    29  func _main() error {
    30  	tmpl, err := template.New("").Parse(`// Code generated by internal/cmd/generator. DO NOT EDIT!
    31  package encoder
    32  
    33  import (
    34    "strings"
    35  )
    36  
    37  type CodeType int
    38  
    39  const (
    40  {{- range $index, $type := .CodeTypes }}
    41    Code{{ $type }} CodeType = {{ $index }}
    42  {{- end }}
    43  )
    44  
    45  var opTypeStrings = [{{ .OpLen }}]string{
    46  {{- range $type := .OpTypes }}
    47      "{{ $type.Op }}",
    48  {{- end }}
    49  }
    50  
    51  type OpType uint16
    52  
    53  const (
    54  {{- range $index, $type := .OpTypes }}
    55    Op{{ $type.Op }} OpType = {{ $index }}
    56  {{- end }}
    57  )
    58  
    59  func (t OpType) String() string {
    60    if int(t) >= {{ .OpLen }} {
    61      return ""
    62    }
    63    return opTypeStrings[int(t)]
    64  }
    65  
    66  func (t OpType) CodeType() CodeType {
    67    if strings.Contains(t.String(), "Struct") {
    68      if strings.Contains(t.String(), "End") {
    69        return CodeStructEnd
    70      }
    71      return CodeStructField
    72    }
    73    switch t {
    74    case OpArray, OpArrayPtr:
    75      return CodeArrayHead
    76    case OpArrayElem:
    77      return CodeArrayElem
    78    case OpSlice, OpSlicePtr:
    79      return CodeSliceHead
    80    case OpSliceElem:
    81      return CodeSliceElem
    82    case OpMap, OpMapPtr:
    83      return CodeMapHead
    84    case OpMapKey:
    85      return CodeMapKey
    86    case OpMapValue:
    87      return CodeMapValue
    88    case OpMapEnd:
    89      return CodeMapEnd
    90    }
    91  
    92    return CodeOp
    93  }
    94  
    95  func (t OpType) HeadToPtrHead() OpType {
    96    if strings.Index(t.String(), "PtrHead") > 0 {
    97      return t
    98    }
    99  
   100    idx := strings.Index(t.String(), "Head")
   101    if idx == -1 {
   102      return t
   103    }
   104    suffix := "PtrHead"+t.String()[idx+len("Head"):]
   105  
   106    const toPtrOffset = 2
   107    if strings.Contains(OpType(int(t) + toPtrOffset).String(), suffix) {
   108      return OpType(int(t) + toPtrOffset)
   109    }
   110    return t
   111  }
   112  
   113  func (t OpType) HeadToOmitEmptyHead() OpType {
   114    const toOmitEmptyOffset = 1
   115    if strings.Contains(OpType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
   116      return OpType(int(t) + toOmitEmptyOffset)
   117    }
   118  
   119    return t
   120  }
   121  
   122  func (t OpType) PtrHeadToHead() OpType {
   123    idx := strings.Index(t.String(), "PtrHead")
   124    if idx == -1 {
   125      return t
   126    }
   127    suffix := t.String()[idx+len("Ptr"):]
   128  
   129    const toPtrOffset = 2
   130    if strings.Contains(OpType(int(t) - toPtrOffset).String(), suffix) {
   131      return OpType(int(t) - toPtrOffset)
   132    }
   133    return t
   134  }
   135  
   136  func (t OpType) FieldToEnd() OpType {
   137    idx := strings.Index(t.String(), "Field")
   138    if idx == -1 {
   139      return t
   140    }
   141    suffix := t.String()[idx+len("Field"):]
   142    if suffix == "" || suffix == "OmitEmpty" {
   143      return t
   144    }
   145    const toEndOffset = 2
   146    if strings.Contains(OpType(int(t) + toEndOffset).String(), "End"+suffix) {
   147      return OpType(int(t) + toEndOffset)
   148    }
   149    return t
   150  }
   151  
   152  func (t OpType) FieldToOmitEmptyField() OpType {
   153    const toOmitEmptyOffset = 1
   154    if strings.Contains(OpType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
   155      return OpType(int(t) + toOmitEmptyOffset)
   156    }
   157    return t
   158  }
   159  `)
   160  	if err != nil {
   161  		return err
   162  	}
   163  	codeTypes := []string{
   164  		"Op",
   165  		"ArrayHead",
   166  		"ArrayElem",
   167  		"SliceHead",
   168  		"SliceElem",
   169  		"MapHead",
   170  		"MapKey",
   171  		"MapValue",
   172  		"MapEnd",
   173  		"Recursive",
   174  		"StructField",
   175  		"StructEnd",
   176  	}
   177  	primitiveTypes := []string{
   178  		"int", "uint", "float32", "float64", "bool", "string", "bytes", "number",
   179  		"array", "map", "slice", "struct", "MarshalJSON", "MarshalText",
   180  		"intString", "uintString", "float32String", "float64String", "boolString", "stringString", "numberString",
   181  		"intPtr", "uintPtr", "float32Ptr", "float64Ptr", "boolPtr", "stringPtr", "bytesPtr", "numberPtr",
   182  		"arrayPtr", "mapPtr", "slicePtr", "marshalJSONPtr", "marshalTextPtr", "interfacePtr",
   183  		"intPtrString", "uintPtrString", "float32PtrString", "float64PtrString", "boolPtrString", "stringPtrString", "numberPtrString",
   184  	}
   185  	primitiveTypesUpper := []string{}
   186  	for _, typ := range primitiveTypes {
   187  		primitiveTypesUpper = append(primitiveTypesUpper, strings.ToUpper(string(typ[0]))+typ[1:])
   188  	}
   189  	opTypes := []opType{
   190  		createOpType("End", "Op"),
   191  		createOpType("Interface", "Op"),
   192  		createOpType("Ptr", "Op"),
   193  		createOpType("SliceElem", "SliceElem"),
   194  		createOpType("SliceEnd", "Op"),
   195  		createOpType("ArrayElem", "ArrayElem"),
   196  		createOpType("ArrayEnd", "Op"),
   197  		createOpType("MapKey", "MapKey"),
   198  		createOpType("MapValue", "MapValue"),
   199  		createOpType("MapEnd", "Op"),
   200  		createOpType("Recursive", "Op"),
   201  		createOpType("RecursivePtr", "Op"),
   202  		createOpType("RecursiveEnd", "Op"),
   203  		createOpType("InterfaceEnd", "Op"),
   204  	}
   205  	for _, typ := range primitiveTypesUpper {
   206  		typ := typ
   207  		opTypes = append(opTypes, createOpType(typ, "Op"))
   208  	}
   209  	for _, typ := range append(primitiveTypesUpper, "") {
   210  		for _, ptrOrNot := range []string{"", "Ptr"} {
   211  			for _, opt := range []string{"", "OmitEmpty"} {
   212  				ptrOrNot := ptrOrNot
   213  				opt := opt
   214  				typ := typ
   215  
   216  				op := fmt.Sprintf(
   217  					"Struct%sHead%s%s",
   218  					ptrOrNot,
   219  					opt,
   220  					typ,
   221  				)
   222  				opTypes = append(opTypes, opType{
   223  					Op:   op,
   224  					Code: "StructField",
   225  				})
   226  			}
   227  		}
   228  	}
   229  	for _, typ := range append(primitiveTypesUpper, "") {
   230  		for _, opt := range []string{"", "OmitEmpty"} {
   231  			opt := opt
   232  			typ := typ
   233  
   234  			op := fmt.Sprintf(
   235  				"StructField%s%s",
   236  				opt,
   237  				typ,
   238  			)
   239  			opTypes = append(opTypes, opType{
   240  				Op:   op,
   241  				Code: "StructField",
   242  			})
   243  		}
   244  		for _, opt := range []string{"", "OmitEmpty"} {
   245  			opt := opt
   246  			typ := typ
   247  
   248  			op := fmt.Sprintf(
   249  				"StructEnd%s%s",
   250  				opt,
   251  				typ,
   252  			)
   253  			opTypes = append(opTypes, opType{
   254  				Op:   op,
   255  				Code: "StructEnd",
   256  			})
   257  		}
   258  	}
   259  	var b bytes.Buffer
   260  	if err := tmpl.Execute(&b, struct {
   261  		CodeTypes []string
   262  		OpTypes   []opType
   263  		OpLen     int
   264  	}{
   265  		CodeTypes: codeTypes,
   266  		OpTypes:   opTypes,
   267  		OpLen:     len(opTypes),
   268  	}); err != nil {
   269  		return err
   270  	}
   271  	path := filepath.Join(repoRoot(), "internal", "encoder", "optype.go")
   272  	buf, err := format.Source(b.Bytes())
   273  	if err != nil {
   274  		return err
   275  	}
   276  	return os.WriteFile(path, buf, 0644)
   277  }
   278  
   279  func generateVM() error {
   280  	file, err := os.ReadFile("vm.go.tmpl")
   281  	if err != nil {
   282  		return err
   283  	}
   284  	fset := token.NewFileSet()
   285  	f, err := parser.ParseFile(fset, "", string(file), parser.ParseComments)
   286  	if err != nil {
   287  		return err
   288  	}
   289  	for _, pkg := range []string{"vm", "vm_indent", "vm_color", "vm_color_indent"} {
   290  		f.Name.Name = pkg
   291  		var buf bytes.Buffer
   292  		printer.Fprint(&buf, fset, f)
   293  		path := filepath.Join(repoRoot(), "internal", "encoder", pkg, "vm.go")
   294  		source, err := format.Source(buf.Bytes())
   295  		if err != nil {
   296  			return err
   297  		}
   298  		if err := os.WriteFile(path, source, 0644); err != nil {
   299  			return err
   300  		}
   301  	}
   302  	return nil
   303  }
   304  
   305  func repoRoot() string {
   306  	_, file, _, _ := runtime.Caller(0)
   307  	relativePathFromRepoRoot := filepath.Join("internal", "cmd", "generator")
   308  	return strings.TrimSuffix(filepath.Dir(file), relativePathFromRepoRoot)
   309  }
   310  
   311  //go:generate go run main.go
   312  func main() {
   313  	if err := generateVM(); err != nil {
   314  		panic(err)
   315  	}
   316  	if err := _main(); err != nil {
   317  		panic(err)
   318  	}
   319  }