go-json/cmd/generator/main.go

669 lines
16 KiB
Go

package main
import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"path/filepath"
"runtime"
"strings"
"text/template"
)
type opType struct {
Op string
Code string
Indent func() string
Escaped func() string
HeadToPtrHead func() string
HeadToNPtrHead func() string
HeadToAnonymousHead func() string
HeadToOmitEmptyHead func() string
HeadToStringTagHead func() string
PtrHeadToHead func() string
FieldToEnd func() string
FieldToOmitEmptyField func() string
FieldToStringTagField func() string
}
func (t opType) IsEscaped() bool {
return t.Op != t.Escaped()
}
func (t opType) IsHeadToPtrHead() bool {
return t.Op != t.HeadToPtrHead()
}
func (t opType) IsHeadToNPtrHead() bool {
return t.Op != t.HeadToNPtrHead()
}
func (t opType) IsHeadToAnonymousHead() bool {
return t.Op != t.HeadToAnonymousHead()
}
func (t opType) IsHeadToOmitEmptyHead() bool {
return t.Op != t.HeadToOmitEmptyHead()
}
func (t opType) IsHeadToStringTagHead() bool {
return t.Op != t.HeadToStringTagHead()
}
func (t opType) IsPtrHeadToHead() bool {
return t.Op != t.PtrHeadToHead()
}
func (t opType) IsFieldToEnd() bool {
return t.Op != t.FieldToEnd()
}
func (t opType) IsFieldToOmitEmptyField() bool {
return t.Op != t.FieldToOmitEmptyField()
}
func (t opType) IsFieldToStringTagField() bool {
return t.Op != t.FieldToStringTagField()
}
func createOpType(op, code string) opType {
return opType{
Op: op,
Code: code,
Indent: func() string { return fmt.Sprintf("%sIndent", op) },
Escaped: func() string { return op },
HeadToPtrHead: func() string { return op },
HeadToNPtrHead: func() string { return op },
HeadToAnonymousHead: func() string { return op },
HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: func() string { return op },
PtrHeadToHead: func() string { return op },
FieldToEnd: func() string { return op },
FieldToOmitEmptyField: func() string { return op },
FieldToStringTagField: func() string { return op },
}
}
func _main() error {
tmpl, err := template.New("").Parse(`// Code generated by cmd/generator. DO NOT EDIT!
package json
type codeType int
const (
{{- range $index, $type := .CodeTypes }}
code{{ $type }} codeType = {{ $index }}
{{- end }}
)
type opType int
const (
{{- range $index, $type := .OpTypes }}
op{{ $type.Op }} opType = {{ $index }}
{{- end }}
)
func (t opType) String() string {
if int(t) >= {{ .OpLen }} {
return t.toNotIndent().String() + "Indent"
}
switch t {
{{- range $type := .OpNotIndentTypes }}
case op{{ $type.Op }}:
return "{{ $type.Op }}"
{{- end }}
}
return ""
}
func (t opType) codeType() codeType {
if int(t) >= {{ .OpLen }} {
return t.toNotIndent().codeType()
}
switch t {
{{- range $type := .OpNotIndentTypes }}
case op{{ $type.Op }}:
return code{{ $type.Code }}
{{- end }}
}
return codeOp
}
func (t opType) toNotIndent() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t) - {{ .OpLen }})
}
return t
}
func (t opType) toIndent() opType {
if int(t) >= {{ .OpLen }} {
return t
}
return opType(int(t) + {{ .OpLen }})
}
func (t opType) toEscaped() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().toEscaped()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsEscaped }}
case op{{ $type.Op }}:
return op{{ call $type.Escaped }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) headToPtrHead() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().headToPtrHead()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsHeadToPtrHead }}
case op{{ $type.Op }}:
return op{{ call $type.HeadToPtrHead }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) headToNPtrHead() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().headToNPtrHead()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsHeadToNPtrHead }}
case op{{ $type.Op }}:
return op{{ call $type.HeadToNPtrHead }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) headToAnonymousHead() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().headToAnonymousHead()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsHeadToAnonymousHead }}
case op{{ $type.Op }}:
return op{{ call $type.HeadToAnonymousHead }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) headToOmitEmptyHead() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().headToOmitEmptyHead()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsHeadToOmitEmptyHead }}
case op{{ $type.Op }}:
return op{{ call $type.HeadToOmitEmptyHead }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) headToStringTagHead() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().headToStringTagHead()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsHeadToStringTagHead }}
case op{{ $type.Op }}:
return op{{ call $type.HeadToStringTagHead }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) ptrHeadToHead() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().ptrHeadToHead()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsPtrHeadToHead }}
case op{{ $type.Op }}:
return op{{ call $type.PtrHeadToHead }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) fieldToEnd() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().fieldToEnd()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsFieldToEnd }}
case op{{ $type.Op }}:
return op{{ call $type.FieldToEnd }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) fieldToOmitEmptyField() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().fieldToOmitEmptyField()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsFieldToOmitEmptyField }}
case op{{ $type.Op }}:
return op{{ call $type.FieldToOmitEmptyField }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) fieldToStringTagField() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().fieldToStringTagField()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsFieldToStringTagField }}
case op{{ $type.Op }}:
return op{{ call $type.FieldToStringTagField }}
{{- end }}
{{- end }}
}
return t
}
`)
if err != nil {
return err
}
codeTypes := []string{
"Op",
"ArrayHead",
"ArrayElem",
"SliceHead",
"SliceElem",
"MapHead",
"MapKey",
"MapValue",
"MapEnd",
"StructFieldRecursive",
"StructField",
"StructEnd",
}
primitiveTypes := []string{
"int", "int8", "int16", "int32", "int64",
"uint", "uint8", "uint16", "uint32", "uint64",
"float32", "float64", "bool", "string", "escapedString", "bytes",
"array", "map", "mapLoad", "slice", "struct", "MarshalJSON", "MarshalText", "recursive",
"intString", "int8String", "int16String", "int32String", "int64String",
"uintString", "uint8String", "uint16String", "uint32String", "uint64String",
"intPtr", "int8Ptr", "int16Ptr", "int32Ptr", "int64Ptr",
"uintPtr", "uint8Ptr", "uint16Ptr", "uint32Ptr", "uint64Ptr",
"float32Ptr", "float64Ptr", "boolPtr", "stringPtr", "escapedStringPtr", "bytesPtr",
"intNPtr", "int8NPtr", "int16NPtr", "int32NPtr", "int64NPtr",
"uintNPtr", "uint8NPtr", "uint16NPtr", "uint32NPtr", "uint64NPtr",
"float32NPtr", "float64NPtr", "boolNPtr", "stringNPtr", "escapedStringNPtr", "bytesNPtr",
}
primitiveTypesUpper := []string{}
for _, typ := range primitiveTypes {
primitiveTypesUpper = append(primitiveTypesUpper, strings.ToUpper(string(typ[0]))+typ[1:])
}
opTypes := []opType{
createOpType("End", "Op"),
createOpType("Interface", "Op"),
createOpType("InterfaceEnd", "Op"),
createOpType("Ptr", "Op"),
createOpType("NPtr", "Op"),
createOpType("SliceHead", "SliceHead"),
createOpType("RootSliceHead", "SliceHead"),
createOpType("SliceElem", "SliceElem"),
createOpType("RootSliceElem", "SliceElem"),
createOpType("SliceEnd", "Op"),
createOpType("ArrayHead", "ArrayHead"),
createOpType("ArrayElem", "ArrayElem"),
createOpType("ArrayEnd", "Op"),
createOpType("MapHead", "MapHead"),
createOpType("MapHeadLoad", "MapHead"),
createOpType("MapKey", "MapKey"),
createOpType("MapValue", "MapValue"),
createOpType("MapEnd", "Op"),
createOpType("StructFieldRecursiveEnd", "Op"),
createOpType("StructAnonymousEnd", "StructEnd"),
}
for _, typ := range primitiveTypesUpper {
typ := typ
optype := createOpType(typ, "Op")
switch typ {
case "String", "StringPtr", "StringNPtr":
optype.Escaped = func() string {
return fmt.Sprintf("Escaped%s", typ)
}
}
opTypes = append(opTypes, optype)
}
for _, escapedOrNot := range []string{"", "Escaped"} {
for _, ptrOrNot := range []string{"", "Ptr", "NPtr"} {
for _, headType := range []string{"", "Anonymous"} {
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
for _, typ := range append(primitiveTypesUpper, "") {
escapedOrNot := escapedOrNot
ptrOrNot := ptrOrNot
headType := headType
opt := opt
typ := typ
op := fmt.Sprintf(
"Struct%sField%s%sHead%s%s",
escapedOrNot,
ptrOrNot,
headType,
opt,
typ,
)
opTypes = append(opTypes, opType{
Op: op,
Code: "StructField",
Indent: func() string { return fmt.Sprintf("%sIndent", op) },
Escaped: func() string {
switch typ {
case "String", "StringPtr", "StringNPtr":
return fmt.Sprintf(
"StructEscapedField%s%sHead%sEscaped%s",
ptrOrNot,
headType,
opt,
typ,
)
}
return fmt.Sprintf(
"StructEscapedField%s%sHead%s%s",
ptrOrNot,
headType,
opt,
typ,
)
},
HeadToPtrHead: func() string {
return fmt.Sprintf(
"Struct%sFieldPtr%sHead%s%s",
escapedOrNot,
headType,
opt,
typ,
)
},
HeadToNPtrHead: func() string {
return fmt.Sprintf(
"Struct%sFieldNPtr%sHead%s%s",
escapedOrNot,
headType,
opt,
typ,
)
},
HeadToAnonymousHead: func() string {
return fmt.Sprintf(
"Struct%sField%sAnonymousHead%s%s",
escapedOrNot,
ptrOrNot,
opt,
typ,
)
},
HeadToOmitEmptyHead: func() string {
return fmt.Sprintf(
"Struct%sField%s%sHeadOmitEmpty%s",
escapedOrNot,
ptrOrNot,
headType,
typ,
)
},
HeadToStringTagHead: func() string {
return fmt.Sprintf(
"Struct%sField%s%sHeadStringTag%s",
escapedOrNot,
ptrOrNot,
headType,
typ,
)
},
PtrHeadToHead: func() string {
return fmt.Sprintf(
"Struct%sField%sHead%s%s",
escapedOrNot,
headType,
opt,
typ,
)
},
FieldToEnd: func() string { return op },
FieldToOmitEmptyField: func() string { return op },
FieldToStringTagField: func() string { return op },
})
}
}
}
}
}
for _, escapedOrNot := range []string{"", "Escaped"} {
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
for _, typ := range append(primitiveTypesUpper, "") {
escapedOrNot := escapedOrNot
opt := opt
typ := typ
op := fmt.Sprintf(
"Struct%sField%s%s",
escapedOrNot,
opt,
typ,
)
opTypes = append(opTypes, opType{
Op: op,
Code: "StructField",
Indent: func() string { return fmt.Sprintf("%sIndent", op) },
Escaped: func() string {
switch typ {
case "String", "StringPtr", "StringNPtr":
return fmt.Sprintf(
"StructEscapedField%sEscaped%s",
opt,
typ,
)
}
return fmt.Sprintf(
"StructEscapedField%s%s",
opt,
typ,
)
},
HeadToPtrHead: func() string { return op },
HeadToNPtrHead: func() string { return op },
HeadToAnonymousHead: func() string { return op },
HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: func() string { return op },
PtrHeadToHead: func() string { return op },
FieldToEnd: func() string {
switch typ {
case "", "Array", "Map", "MapLoad", "Slice", "Struct", "Recursive":
return op
}
return fmt.Sprintf(
"Struct%sEnd%s%s",
escapedOrNot,
opt,
typ,
)
},
FieldToOmitEmptyField: func() string {
return fmt.Sprintf(
"Struct%sFieldOmitEmpty%s",
escapedOrNot,
typ,
)
},
FieldToStringTagField: func() string {
return fmt.Sprintf(
"Struct%sFieldStringTag%s",
escapedOrNot,
typ,
)
},
})
}
}
}
for _, escapedOrNot := range []string{"", "Escaped"} {
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
for _, typ := range append(primitiveTypesUpper, "") {
escapedOrNot := escapedOrNot
opt := opt
typ := typ
op := fmt.Sprintf(
"Struct%sEnd%s%s",
escapedOrNot,
opt,
typ,
)
opTypes = append(opTypes, opType{
Op: op,
Code: "StructEnd",
Indent: func() string { return fmt.Sprintf("%sIndent", op) },
Escaped: func() string {
switch typ {
case "String", "StringPtr", "StringNPtr":
return fmt.Sprintf(
"StructEscapedEnd%sEscaped%s",
opt,
typ,
)
}
return fmt.Sprintf(
"StructEscapedEnd%s%s",
opt,
typ,
)
},
HeadToPtrHead: func() string { return op },
HeadToNPtrHead: func() string { return op },
HeadToAnonymousHead: func() string { return op },
HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: func() string { return op },
PtrHeadToHead: func() string { return op },
FieldToEnd: func() string { return op },
FieldToOmitEmptyField: func() string { return op },
FieldToStringTagField: func() string { return op },
})
}
}
}
indentOpTypes := []opType{}
for _, typ := range opTypes {
typ := typ
indentOpTypes = append(indentOpTypes, opType{
Op: fmt.Sprintf("%sIndent", typ.Op),
Code: typ.Code,
Indent: func() string { return fmt.Sprintf("%sIndent", typ.Op) },
Escaped: func() string { return fmt.Sprintf("%sIndent", typ.Escaped()) },
HeadToPtrHead: func() string {
return fmt.Sprintf("%sIndent", typ.HeadToPtrHead())
},
HeadToNPtrHead: func() string {
return fmt.Sprintf("%sIndent", typ.HeadToNPtrHead())
},
HeadToAnonymousHead: func() string {
return fmt.Sprintf("%sIndent", typ.HeadToAnonymousHead())
},
HeadToOmitEmptyHead: func() string {
return fmt.Sprintf("%sIndent", typ.HeadToOmitEmptyHead())
},
HeadToStringTagHead: func() string {
return fmt.Sprintf("%sIndent", typ.HeadToStringTagHead())
},
PtrHeadToHead: func() string {
return fmt.Sprintf("%sIndent", typ.PtrHeadToHead())
},
FieldToOmitEmptyField: func() string {
return fmt.Sprintf("%sIndent", typ.FieldToOmitEmptyField())
},
FieldToStringTagField: func() string {
return fmt.Sprintf("%sIndent", typ.FieldToStringTagField())
},
FieldToEnd: func() string {
return fmt.Sprintf("%sIndent", typ.FieldToEnd())
},
})
}
var b bytes.Buffer
if err := tmpl.Execute(&b, struct {
CodeTypes []string
OpTypes []opType
OpNotIndentTypes []opType
OpLen int
OpIndentLen int
}{
CodeTypes: codeTypes,
OpTypes: append(opTypes, indentOpTypes...),
OpNotIndentTypes: opTypes,
OpLen: len(opTypes),
OpIndentLen: len(indentOpTypes),
}); err != nil {
return err
}
path := filepath.Join(repoRoot(), "encode_optype.go")
buf, err := format.Source(b.Bytes())
if err != nil {
return err
}
return ioutil.WriteFile(path, buf, 0644)
}
func repoRoot() string {
_, file, _, _ := runtime.Caller(0)
relativePathFromRepoRoot := filepath.Join("cmd", "generator")
return strings.TrimSuffix(filepath.Dir(file), relativePathFromRepoRoot)
}
func main() {
if err := _main(); err != nil {
panic(err)
}
}