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