package gen
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
identNext = 0
|
|
identPrefix = "za"
|
|
)
|
|
|
|
func resetIdent(prefix string) {
|
|
identPrefix = prefix
|
|
identNext = 0
|
|
}
|
|
|
|
// generate a random identifier name
|
|
func randIdent() string {
|
|
identNext++
|
|
return fmt.Sprintf("%s%04d", identPrefix, identNext)
|
|
}
|
|
|
|
// This code defines the type declaration tree.
|
|
//
|
|
// Consider the following:
|
|
//
|
|
// type Marshaler struct {
|
|
// Thing1 *float64 `msg:"thing1"`
|
|
// Body []byte `msg:"body"`
|
|
// }
|
|
//
|
|
// A parser using this generator as a backend
|
|
// should parse the above into:
|
|
//
|
|
// var val Elem = &Ptr{
|
|
// name: "z",
|
|
// Value: &Struct{
|
|
// Name: "Marshaler",
|
|
// Fields: []StructField{
|
|
// {
|
|
// FieldTag: "thing1",
|
|
// FieldElem: &Ptr{
|
|
// name: "z.Thing1",
|
|
// Value: &BaseElem{
|
|
// name: "*z.Thing1",
|
|
// Value: Float64,
|
|
// Convert: false,
|
|
// },
|
|
// },
|
|
// },
|
|
// {
|
|
// FieldTag: "body",
|
|
// FieldElem: &BaseElem{
|
|
// name: "z.Body",
|
|
// Value: Bytes,
|
|
// Convert: false,
|
|
// },
|
|
// },
|
|
// },
|
|
// },
|
|
// }
|
|
|
|
// Base is one of the
|
|
// base types
|
|
type Primitive uint8
|
|
|
|
// this is effectively the
|
|
// list of currently available
|
|
// ReadXxxx / WriteXxxx methods.
|
|
const (
|
|
Invalid Primitive = iota
|
|
Bytes
|
|
String
|
|
Float32
|
|
Float64
|
|
Complex64
|
|
Complex128
|
|
Uint
|
|
Uint8
|
|
Uint16
|
|
Uint32
|
|
Uint64
|
|
Byte
|
|
Int
|
|
Int8
|
|
Int16
|
|
Int32
|
|
Int64
|
|
Bool
|
|
Intf // interface{}
|
|
Time // time.Time
|
|
Ext // extension
|
|
|
|
IDENT // IDENT means an unrecognized identifier
|
|
)
|
|
|
|
// all of the recognized identities
|
|
// that map to primitive types
|
|
var primitives = map[string]Primitive{
|
|
"[]byte": Bytes,
|
|
"string": String,
|
|
"float32": Float32,
|
|
"float64": Float64,
|
|
"complex64": Complex64,
|
|
"complex128": Complex128,
|
|
"uint": Uint,
|
|
"uint8": Uint8,
|
|
"uint16": Uint16,
|
|
"uint32": Uint32,
|
|
"uint64": Uint64,
|
|
"byte": Byte,
|
|
"rune": Int32,
|
|
"int": Int,
|
|
"int8": Int8,
|
|
"int16": Int16,
|
|
"int32": Int32,
|
|
"int64": Int64,
|
|
"bool": Bool,
|
|
"interface{}": Intf,
|
|
"time.Time": Time,
|
|
"msgp.Extension": Ext,
|
|
}
|
|
|
|
// types built into the library
|
|
// that satisfy all of the
|
|
// interfaces.
|
|
var builtins = map[string]struct{}{
|
|
"msgp.Raw": struct{}{},
|
|
"msgp.Number": struct{}{},
|
|
}
|
|
|
|
// common data/methods for every Elem
|
|
type common struct{ vname, alias string }
|
|
|
|
func (c *common) SetVarname(s string) { c.vname = s }
|
|
func (c *common) Varname() string { return c.vname }
|
|
func (c *common) Alias(typ string) { c.alias = typ }
|
|
func (c *common) hidden() {}
|
|
|
|
func IsPrintable(e Elem) bool {
|
|
if be, ok := e.(*BaseElem); ok && !be.Printable() {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Elem is a go type capable of being
|
|
// serialized into MessagePack. It is
|
|
// implemented by *Ptr, *Struct, *Array,
|
|
// *Slice, *Map, and *BaseElem.
|
|
type Elem interface {
|
|
// SetVarname sets this nodes
|
|
// variable name and recursively
|
|
// sets the names of all its children.
|
|
// In general, this should only be
|
|
// called on the parent of the tree.
|
|
SetVarname(s string)
|
|
|
|
// Varname returns the variable
|
|
// name of the element.
|
|
Varname() string
|
|
|
|
// TypeName is the canonical
|
|
// go type name of the node
|
|
// e.g. "string", "int", "map[string]float64"
|
|
// OR the alias name, if it has been set.
|
|
TypeName() string
|
|
|
|
// Alias sets a type (alias) name
|
|
Alias(typ string)
|
|
|
|
// Copy should perform a deep copy of the object
|
|
Copy() Elem
|
|
|
|
// Complexity returns a measure of the
|
|
// complexity of element (greater than
|
|
// or equal to 1.)
|
|
Complexity() int
|
|
|
|
hidden()
|
|
}
|
|
|
|
// Ident returns the *BaseElem that corresponds
|
|
// to the provided identity.
|
|
func Ident(id string) *BaseElem {
|
|
p, ok := primitives[id]
|
|
if ok {
|
|
return &BaseElem{Value: p}
|
|
}
|
|
be := &BaseElem{Value: IDENT}
|
|
be.Alias(id)
|
|
return be
|
|
}
|
|
|
|
type Array struct {
|
|
common
|
|
Index string // index variable name
|
|
Size string // array size
|
|
Els Elem // child
|
|
}
|
|
|
|
func (a *Array) SetVarname(s string) {
|
|
a.common.SetVarname(s)
|
|
ridx:
|
|
a.Index = randIdent()
|
|
|
|
// try to avoid using the same
|
|
// index as a parent slice
|
|
if strings.Contains(a.Varname(), a.Index) {
|
|
goto ridx
|
|
}
|
|
|
|
a.Els.SetVarname(fmt.Sprintf("%s[%s]", a.Varname(), a.Index))
|
|
}
|
|
|
|
func (a *Array) TypeName() string {
|
|
if a.common.alias != "" {
|
|
return a.common.alias
|
|
}
|
|
a.common.Alias(fmt.Sprintf("[%s]%s", a.Size, a.Els.TypeName()))
|
|
return a.common.alias
|
|
}
|
|
|
|
func (a *Array) Copy() Elem {
|
|
b := *a
|
|
b.Els = a.Els.Copy()
|
|
return &b
|
|
}
|
|
|
|
func (a *Array) Complexity() int { return 1 + a.Els.Complexity() }
|
|
|
|
// Map is a map[string]Elem
|
|
type Map struct {
|
|
common
|
|
Keyidx string // key variable name
|
|
Validx string // value variable name
|
|
Value Elem // value element
|
|
}
|
|
|
|
func (m *Map) SetVarname(s string) {
|
|
m.common.SetVarname(s)
|
|
ridx:
|
|
m.Keyidx = randIdent()
|
|
m.Validx = randIdent()
|
|
|
|
// just in case
|
|
if m.Keyidx == m.Validx {
|
|
goto ridx
|
|
}
|
|
|
|
m.Value.SetVarname(m.Validx)
|
|
}
|
|
|
|
func (m *Map) TypeName() string {
|
|
if m.common.alias != "" {
|
|
return m.common.alias
|
|
}
|
|
m.common.Alias("map[string]" + m.Value.TypeName())
|
|
return m.common.alias
|
|
}
|
|
|
|
func (m *Map) Copy() Elem {
|
|
g := *m
|
|
g.Value = m.Value.Copy()
|
|
return &g
|
|
}
|
|
|
|
func (m *Map) Complexity() int { return 2 + m.Value.Complexity() }
|
|
|
|
type Slice struct {
|
|
common
|
|
Index string
|
|
Els Elem // The type of each element
|
|
}
|
|
|
|
func (s *Slice) SetVarname(a string) {
|
|
s.common.SetVarname(a)
|
|
s.Index = randIdent()
|
|
varName := s.Varname()
|
|
if varName[0] == '*' {
|
|
// Pointer-to-slice requires parenthesis for slicing.
|
|
varName = "(" + varName + ")"
|
|
}
|
|
s.Els.SetVarname(fmt.Sprintf("%s[%s]", varName, s.Index))
|
|
}
|
|
|
|
func (s *Slice) TypeName() string {
|
|
if s.common.alias != "" {
|
|
return s.common.alias
|
|
}
|
|
s.common.Alias("[]" + s.Els.TypeName())
|
|
return s.common.alias
|
|
}
|
|
|
|
func (s *Slice) Copy() Elem {
|
|
z := *s
|
|
z.Els = s.Els.Copy()
|
|
return &z
|
|
}
|
|
|
|
func (s *Slice) Complexity() int {
|
|
return 1 + s.Els.Complexity()
|
|
}
|
|
|
|
type Ptr struct {
|
|
common
|
|
Value Elem
|
|
}
|
|
|
|
func (s *Ptr) SetVarname(a string) {
|
|
s.common.SetVarname(a)
|
|
|
|
// struct fields are dereferenced
|
|
// automatically...
|
|
switch x := s.Value.(type) {
|
|
case *Struct:
|
|
// struct fields are automatically dereferenced
|
|
x.SetVarname(a)
|
|
return
|
|
|
|
case *BaseElem:
|
|
// identities have pointer receivers
|
|
if x.Value == IDENT {
|
|
x.SetVarname(a)
|
|
} else {
|
|
x.SetVarname("*" + a)
|
|
}
|
|
return
|
|
|
|
default:
|
|
s.Value.SetVarname("*" + a)
|
|
return
|
|
}
|
|
}
|
|
|
|
func (s *Ptr) TypeName() string {
|
|
if s.common.alias != "" {
|
|
return s.common.alias
|
|
}
|
|
s.common.Alias("*" + s.Value.TypeName())
|
|
return s.common.alias
|
|
}
|
|
|
|
func (s *Ptr) Copy() Elem {
|
|
v := *s
|
|
v.Value = s.Value.Copy()
|
|
return &v
|
|
}
|
|
|
|
func (s *Ptr) Complexity() int { return 1 + s.Value.Complexity() }
|
|
|
|
func (s *Ptr) Needsinit() bool {
|
|
if be, ok := s.Value.(*BaseElem); ok && be.needsref {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
type Struct struct {
|
|
common
|
|
Fields []StructField // field list
|
|
AsTuple bool // write as an array instead of a map
|
|
}
|
|
|
|
func (s *Struct) TypeName() string {
|
|
if s.common.alias != "" {
|
|
return s.common.alias
|
|
}
|
|
str := "struct{\n"
|
|
for i := range s.Fields {
|
|
str += s.Fields[i].FieldName +
|
|
" " + s.Fields[i].FieldElem.TypeName() +
|
|
" " + s.Fields[i].RawTag + ";\n"
|
|
}
|
|
str += "}"
|
|
s.common.Alias(str)
|
|
return s.common.alias
|
|
}
|
|
|
|
func (s *Struct) SetVarname(a string) {
|
|
s.common.SetVarname(a)
|
|
writeStructFields(s.Fields, a)
|
|
}
|
|
|
|
func (s *Struct) Copy() Elem {
|
|
g := *s
|
|
g.Fields = make([]StructField, len(s.Fields))
|
|
copy(g.Fields, s.Fields)
|
|
for i := range s.Fields {
|
|
g.Fields[i].FieldElem = s.Fields[i].FieldElem.Copy()
|
|
}
|
|
return &g
|
|
}
|
|
|
|
func (s *Struct) Complexity() int {
|
|
c := 1
|
|
for i := range s.Fields {
|
|
c += s.Fields[i].FieldElem.Complexity()
|
|
}
|
|
return c
|
|
}
|
|
|
|
type StructField struct {
|
|
FieldTag string // the string inside the `msg:""` tag
|
|
RawTag string // the full struct tag
|
|
FieldName string // the name of the struct field
|
|
FieldElem Elem // the field type
|
|
}
|
|
|
|
type ShimMode int
|
|
|
|
const (
|
|
Cast ShimMode = iota
|
|
Convert
|
|
)
|
|
|
|
// BaseElem is an element that
|
|
// can be represented by a primitive
|
|
// MessagePack type.
|
|
type BaseElem struct {
|
|
common
|
|
ShimMode ShimMode // Method used to shim
|
|
ShimToBase string // shim to base type, or empty
|
|
ShimFromBase string // shim from base type, or empty
|
|
Value Primitive // Type of element
|
|
Convert bool // should we do an explicit conversion?
|
|
mustinline bool // must inline; not printable
|
|
needsref bool // needs reference for shim
|
|
}
|
|
|
|
func (s *BaseElem) Printable() bool { return !s.mustinline }
|
|
|
|
func (s *BaseElem) Alias(typ string) {
|
|
s.common.Alias(typ)
|
|
if s.Value != IDENT {
|
|
s.Convert = true
|
|
}
|
|
if strings.Contains(typ, ".") {
|
|
s.mustinline = true
|
|
}
|
|
}
|
|
|
|
func (s *BaseElem) SetVarname(a string) {
|
|
// extensions whose parents
|
|
// are not pointers need to
|
|
// be explicitly referenced
|
|
if s.Value == Ext || s.needsref {
|
|
if strings.HasPrefix(a, "*") {
|
|
s.common.SetVarname(a[1:])
|
|
return
|
|
}
|
|
s.common.SetVarname("&" + a)
|
|
return
|
|
}
|
|
|
|
s.common.SetVarname(a)
|
|
}
|
|
|
|
// TypeName returns the syntactically correct Go
|
|
// type name for the base element.
|
|
func (s *BaseElem) TypeName() string {
|
|
if s.common.alias != "" {
|
|
return s.common.alias
|
|
}
|
|
s.common.Alias(s.BaseType())
|
|
return s.common.alias
|
|
}
|
|
|
|
// ToBase, used if Convert==true, is used as tmp = {{ToBase}}({{Varname}})
|
|
func (s *BaseElem) ToBase() string {
|
|
if s.ShimToBase != "" {
|
|
return s.ShimToBase
|
|
}
|
|
return s.BaseType()
|
|
}
|
|
|
|
// FromBase, used if Convert==true, is used as {{Varname}} = {{FromBase}}(tmp)
|
|
func (s *BaseElem) FromBase() string {
|
|
if s.ShimFromBase != "" {
|
|
return s.ShimFromBase
|
|
}
|
|
return s.TypeName()
|
|
}
|
|
|
|
// BaseName returns the string form of the
|
|
// base type (e.g. Float64, Ident, etc)
|
|
func (s *BaseElem) BaseName() string {
|
|
// time is a special case;
|
|
// we strip the package prefix
|
|
if s.Value == Time {
|
|
return "Time"
|
|
}
|
|
return s.Value.String()
|
|
}
|
|
|
|
func (s *BaseElem) BaseType() string {
|
|
switch s.Value {
|
|
case IDENT:
|
|
return s.TypeName()
|
|
|
|
// exceptions to the naming/capitalization
|
|
// rule:
|
|
case Intf:
|
|
return "interface{}"
|
|
case Bytes:
|
|
return "[]byte"
|
|
case Time:
|
|
return "time.Time"
|
|
case Ext:
|
|
return "msgp.Extension"
|
|
|
|
// everything else is base.String() with
|
|
// the first letter as lowercase
|
|
default:
|
|
return strings.ToLower(s.BaseName())
|
|
}
|
|
}
|
|
|
|
func (s *BaseElem) Needsref(b bool) {
|
|
s.needsref = b
|
|
}
|
|
|
|
func (s *BaseElem) Copy() Elem {
|
|
g := *s
|
|
return &g
|
|
}
|
|
|
|
func (s *BaseElem) Complexity() int {
|
|
if s.Convert && !s.mustinline {
|
|
return 2
|
|
}
|
|
// we need to return 1 if !printable(),
|
|
// in order to make sure that stuff gets
|
|
// inlined appropriately
|
|
return 1
|
|
}
|
|
|
|
// Resolved returns whether or not
|
|
// the type of the element is
|
|
// a primitive or a builtin provided
|
|
// by the package.
|
|
func (s *BaseElem) Resolved() bool {
|
|
if s.Value == IDENT {
|
|
_, ok := builtins[s.TypeName()]
|
|
return ok
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (k Primitive) String() string {
|
|
switch k {
|
|
case String:
|
|
return "String"
|
|
case Bytes:
|
|
return "Bytes"
|
|
case Float32:
|
|
return "Float32"
|
|
case Float64:
|
|
return "Float64"
|
|
case Complex64:
|
|
return "Complex64"
|
|
case Complex128:
|
|
return "Complex128"
|
|
case Uint:
|
|
return "Uint"
|
|
case Uint8:
|
|
return "Uint8"
|
|
case Uint16:
|
|
return "Uint16"
|
|
case Uint32:
|
|
return "Uint32"
|
|
case Uint64:
|
|
return "Uint64"
|
|
case Byte:
|
|
return "Byte"
|
|
case Int:
|
|
return "Int"
|
|
case Int8:
|
|
return "Int8"
|
|
case Int16:
|
|
return "Int16"
|
|
case Int32:
|
|
return "Int32"
|
|
case Int64:
|
|
return "Int64"
|
|
case Bool:
|
|
return "Bool"
|
|
case Intf:
|
|
return "Intf"
|
|
case Time:
|
|
return "time.Time"
|
|
case Ext:
|
|
return "Extension"
|
|
case IDENT:
|
|
return "Ident"
|
|
default:
|
|
return "INVALID"
|
|
}
|
|
}
|
|
|
|
// writeStructFields is a trampoline for writeBase for
|
|
// all of the fields in a struct
|
|
func writeStructFields(s []StructField, name string) {
|
|
for i := range s {
|
|
s[i].FieldElem.SetVarname(fmt.Sprintf("%s.%s", name, s[i].FieldName))
|
|
}
|
|
}
|
|
|
|
// coerceArraySize ensures we can compare constant array lengths.
|
|
//
|
|
// msgpack array headers are 32 bit unsigned, which is reflected in the
|
|
// ArrayHeader implementation in this library using uint32. On the Go side, we
|
|
// can declare array lengths as any constant integer width, which breaks when
|
|
// attempting a direct comparison to an array header's uint32.
|
|
//
|
|
func coerceArraySize(asz string) string {
|
|
return fmt.Sprintf("uint32(%s)", asz)
|
|
}
|