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) }