You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

619 lines
12 KiB

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