// Mgmt // Copyright (C) James Shubin and the project contributors // Written by James Shubin and the project contributors // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // Additional permission under GNU GPL version 3 section 7 // // If you modify this program, or any covered work, by linking or combining it // with embedded mcl code and modules (and that the embedded mcl code and // modules which link with this program, contain a copy of their source code in // the authoritative form) containing parts covered by the terms of any other // license, the licensors of this program grant you additional permission to // convey the resulting work. Furthermore, the licensors of this program grant // the original author, James Shubin, additional permission to update this // additional permission if he deems it necessary to achieve the goals of this // additional permission. package types import ( "context" "errors" "fmt" "net" "reflect" "sort" "strconv" "strings" "github.com/purpleidea/mgmt/util/errwrap" ) var ( // ErrNilValue is returned when ValueOf() attempts to represent a nil // pointer as an mcl value. This is not supported in mcl. ErrNilValue = errors.New("cannot represent a nil golang value in mcl") // ErrInvalidValue is returned when ValueOf() is called on an invalid or // zero reflect.Value. ErrInvalidValue = errors.New("cannot represent invalid reflect.Value") ) // Value represents an interface to get values out of each type. It is similar // to the reflection interfaces used in the golang standard library. type Value interface { fmt.Stringer // String() string (for display purposes) Type() *Type Less(Value) bool // to find the smaller of the two values (for sort) Cmp(Value) error // error if the two values aren't the same Copy() Value // returns a copy of this value Value() interface{} Bool() bool Str() string Int() int64 Float() float64 List() []Value Map() map[Value]Value // keys must all have same type, same for values Struct() map[string]Value Func() interface{} // func(interfaces.Txn, []interfaces.Func) (interfaces.Func, error) } // ValueOfGolang is a helper that takes a golang value, and produces the mcl // equivalent internal representation. This is very useful for writing tests. A // reminder that if you pass in a nil value, or something containing a nil // value, then you won't get what you want. See our documentation for ValueOf. func ValueOfGolang(i interface{}) (Value, error) { return ValueOf(reflect.ValueOf(i)) } // ValueOf takes a reflect.Value and returns an equivalent Value. Remember that // the mcl type system currently can't represent certain values that *are* // possible in golang. This is intentional. For example, mcl can't represent a // *string (pointer to a string) where as this is quite common in golang. This // is because mcl has no `nil/null` values. It is designed this way to avoid the // well-known expensive "null-pointer-exception" style bugs. A version two of // the language might consider an "Optional" type. In the meantime, you can // still represent an "undefined" value, but only so far as when it's passed to // a resource field. This is done with our "elvis" operator. When using this // function, if you pass in something with a nil value, then expect a panic or // an error if you're lucky. func ValueOf(v reflect.Value) (Value, error) { // Gracefully handle invalid values instead of panic(). Invalid // reflect.Value values can come from nil values, or the zero value. if !v.IsValid() { return nil, ErrInvalidValue } value := v typ := value.Type() kind := typ.Kind() for kind == reflect.Ptr { // Prevent panic() if value is a nil pointer and return an error. if value.IsNil() { return nil, ErrNilValue } typ = typ.Elem() // un-nest one pointer kind = typ.Kind() // un-nest value from pointer value = value.Elem() // XXX: is this correct? } // Special cases: if value.CanInterface() { if v, ok := (value.Interface()).(net.HardwareAddr); ok { return &StrValue{V: v.String()}, nil } } // TODO: net/url.URL, time.Duration, etc. Note: avoid net/mail.Address switch kind { // match on destination field kind case reflect.Bool: return &BoolValue{V: value.Bool()}, nil case reflect.String: return &StrValue{V: value.String()}, nil case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: return &IntValue{V: value.Int()}, nil case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: return &IntValue{V: int64(value.Uint())}, nil case reflect.Float64, reflect.Float32: return &FloatValue{V: value.Float()}, nil case reflect.Array, reflect.Slice: values := []Value{} for i := 0; i < value.Len(); i++ { x := value.Index(i) v, err := ValueOf(x) // recurse if err != nil { return nil, err } values = append(values, v) } t, err := TypeOf(value.Type().Elem()) // type of contents if err != nil { return nil, errwrap.Wrapf(err, "can't determine type of %+v", value) } return &ListValue{ T: NewType(fmt.Sprintf("[]%s", t.String())), V: values, }, nil case reflect.Map: m := make(map[Value]Value) // loop through the list of map keys in undefined order for _, mk := range value.MapKeys() { mv := value.MapIndex(mk) k, err := ValueOf(mk) // recurse if err != nil { return nil, err } v, err := ValueOf(mv) // recurse if err != nil { return nil, err } m[k] = v } kt, err := TypeOf(value.Type().Key()) // type of key if err != nil { return nil, errwrap.Wrapf(err, "can't determine key type of %+v", value) } vt, err := TypeOf(value.Type().Elem()) // type of value if err != nil { return nil, errwrap.Wrapf(err, "can't determine value type of %+v", value) } return &MapValue{ T: NewType(fmt.Sprintf("map{%s: %s}", kt.String(), vt.String())), V: m, }, nil case reflect.Struct: // TODO: we could take this simpler "get the full type" approach // for all the values, but I think that building them up when // possible for the other cases is a more robust approach! t, err := TypeOf(value.Type()) if err != nil { return nil, errwrap.Wrapf(err, "can't determine type of %+v", value) } l := value.NumField() // number of struct fields according to value if l != len(t.Ord) { // programming error? return nil, fmt.Errorf("incompatible number of fields") } values := make(map[string]Value) for i := 0; i < l; i++ { x := value.Field(i) v, err := ValueOf(x) // recurse if err != nil { return nil, err } name := t.Ord[i] // how else can we get the field name? values[name] = v } return &StructValue{ T: t, V: values, }, nil case reflect.Func: t, err := TypeOf(value.Type()) if err != nil { return nil, errwrap.Wrapf(err, "can't determine type of %+v", value) } if t.Out == nil { return nil, fmt.Errorf("cannot only represent functions with one output value") } f := func(ctx context.Context, args []Value) (Value, error) { in := []reflect.Value{} for _, x := range args { // TODO: should we build this method instead? //v := x.Reflect() // types.Value -> reflect.Value v := reflect.ValueOf(x.Value()) in = append(in, v) } // FIXME: can we pass in ctx ? // FIXME: can we trap panic's ? out := value.Call(in) // []reflect.Value if len(out) != 1 { // TODO: panic, b/c already checked in TypeOf? return nil, fmt.Errorf("cannot only represent functions with one output value") } return ValueOf(out[0]) // recurse } return &FuncValue{ T: t, V: f, }, nil // TODO: should this return a variant value? // TODO: add this into ConfigurableValueOf like ConfigurableTypeOf ? case reflect.Interface: opts := []TypeOfOption{ //StructTagOpt(StructTag), //StrictStructTagOpt(false), //SkipBadStructFieldsOpt(false), AllowInterfaceTypeOpt(true), } t, err := ConfigurableTypeOf(value.Type(), opts...) //t, err := TypeOf(value.Type()) if err != nil { return nil, errwrap.Wrapf(err, "can't determine type of %+v", value) } v, err := ValueOf(value.Elem()) // recurse if err != nil { return nil, errwrap.Wrapf(err, "can't determine value of %+v", value) } return &VariantValue{ T: t, V: v, }, nil default: return nil, fmt.Errorf("unable to represent value of %+v which has kind: %v", v, kind) } } // Into mutates the given reflect.Value with the data represented by the Value. // // Container types like map/list (and to a certain extent structs) will be // cleared before adding the contained data such that the existing data doesn't // affect the outcome, and the output reflect.Value directly maps to the input // Value. // // In almost every case, it is likely that the reflect.Value will be modified, // instantiating nil pointers and even potentially partially filling data before // returning an error. It should be assumed that if this returns an error, the // reflect.Value passed in has been trashed and should be discarded before // reuse. func Into(v Value, rv reflect.Value) error { typ := rv.Type() kind := typ.Kind() for kind == reflect.Ptr { typ = typ.Elem() // un-nest one pointer kind = typ.Kind() // if pointer was nil, instantiate the destination type and point // at it to prevent nil pointer dereference when setting values if rv.IsNil() { rv.Set(reflect.New(typ)) } rv = rv.Elem() // un-nest rv from pointer } if !rv.CanSet() { return fmt.Errorf("can't set value, is it unexported?") } // capture rv and v in a closure that is static for the scope of this Into() call // mustInto ensures rv is in a list of compatible types before attempting to reflect it mustInto := func(kinds ...reflect.Kind) error { // sigh. Go can be so elegant, and then it makes you do this for _, n := range kinds { if kind == n { return nil } } // No matching kind found, must be an incompatible conversion return fmt.Errorf("cannot Into() %+v of type %s into %s", v, v.Type(), typ) } if typ == nil { return fmt.Errorf("cannot Into() %+v of type %s into a nil type", v, v.Type()) } // This is used when we are setting a resource field which has type of // interface{} instead of a string, bool, list, etc... if isInterface := typ.Kind() == reflect.Interface; isInterface { //x := reflect.ValueOf(v) // no! // use the value with type interface{}, not types.Value x := reflect.ValueOf(v.Value()) rv.Set(x) return nil } switch v := v.(type) { case *BoolValue: if err := mustInto(reflect.Bool); err != nil { return err } rv.SetBool(v.V) return nil case *StrValue: if err := mustInto(reflect.String); err != nil { return err } rv.SetString(v.V) return nil case *IntValue: // overflow check switch kind { // match on destination field kind case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: ff := reflect.Zero(typ) // test on a non-ptr equivalent if ff.OverflowInt(v.V) { // this is valid! return fmt.Errorf("%+v is an `%s`, and rv `%d` will overflow it", rv.Interface(), rv.Kind(), v.V) } rv.SetInt(v.V) return nil case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: ff := reflect.Zero(typ) if ff.OverflowUint(uint64(v.V)) { // TODO: is this correct? return fmt.Errorf("%+v is an `%s`, and rv `%d` will overflow it", rv.Interface(), rv.Kind(), v.V) } rv.SetUint(uint64(v.V)) return nil default: return fmt.Errorf("cannot Into() %+v of type %s into %s", v, v.Type(), typ) } case *FloatValue: if err := mustInto(reflect.Float32, reflect.Float64); err != nil { return err } ff := reflect.Zero(typ) if ff.OverflowFloat(v.V) { return fmt.Errorf("%+v is an `%s`, and value `%f` will overflow it", rv.Interface(), rv.Kind(), v.V) } rv.SetFloat(v.V) return nil case *ListValue: count := len(v.V) switch kind { case reflect.Slice: pow := nextPowerOfTwo(uint(count)) nval := reflect.MakeSlice(rv.Type(), count, int(pow)) rv.Set(nval) case reflect.Array: if count > rv.Len() { return fmt.Errorf("%+v is too small for %+v", typ, v) } rv.Set(reflect.New(typ).Elem()) default: return mustInto() // nothing, always returns err } for i, x := range v.V { f := rv.Index(i) el := reflect.New(f.Type()).Elem() if err := Into(x, el); err != nil { // recurse return err } f.Set(el) } return nil case *MapValue: if err := mustInto(reflect.Map); err != nil { return err } rv.Set(reflect.MakeMapWithSize(typ, len(v.V))) // convert both key and value, then set them in the map for mk, mv := range v.V { key := reflect.New(typ.Key()).Elem() if err := Into(mk, key); err != nil { // recurse return err } val := reflect.New(typ.Elem()).Elem() if err := Into(mv, val); err != nil { // recurse return err } rv.SetMapIndex(key, val) } return nil case *StructValue: if err := mustInto(reflect.Struct); err != nil { return err } // Into sets the value of the given reflect.Value to the value of this obj mapping, err := TypeStructTagToFieldName(typ) if err != nil { return err } keys := []string{} for k := range v.T.Map { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { // loop in deterministic order mk := k // map mcl field name -> go field name based on `lang:""` tag if key, exists := mapping[k]; exists { mk = key } field := rv.FieldByName(mk) if err := Into(v.V[k], field); err != nil { // recurse return err } } return nil //case *FuncValue: // if err := mustInto(reflect.Func); err != nil { // return err // } // // // wrap our function with the translation that is necessary // fn := func(args []reflect.Value) (results []reflect.Value) { // build // innerArgs := []Value{} // for _, x := range args { // v, err := ValueOf(x) // reflect.Value -> Value // if err != nil { // panic(fmt.Errorf("can't determine value of %+v", x)) // } // innerArgs = append(innerArgs, v) // } // result, err := v.V(innerArgs) // call it // if err != nil { // // when calling our function with the Call method, then // // we get the error output and have a chance to decide // // what to do with it, but when calling it from within // // a normal golang function call, the error represents // // that something went horribly wrong, aka a panic... // panic(fmt.Errorf("function panic: %+v", err)) // } // out := reflect.New(rv.Type().Out(0)) // // convert the lang result back to a Go value // if err := Into(result, out); err != nil { // panic(fmt.Errorf("function return conversion panic: %+v", err)) // } // return []reflect.Value{out} // only one result // } // rv.Set(reflect.MakeFunc(rv.Type(), fn)) // return nil case *VariantValue: return Into(v.V, rv) default: return fmt.Errorf("cannot Into() %+v of type (%T) %s into %s", v, v, v.Type(), typ) } } // ValueSlice is a linear list of values. It is used for sorting purposes. type ValueSlice []Value func (vs ValueSlice) Len() int { return len(vs) } func (vs ValueSlice) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } func (vs ValueSlice) Less(i, j int) bool { return vs[i].Less(vs[j]) } // Base implements the missing methods that all types need. type Base struct{} // Bool represents the value of this type as a bool if it is one. If this is not // a bool, then this panics. func (obj *Base) Bool() bool { panic("not a bool") } // Str represents the value of this type as a string if it is one. If this is // not a string, then this panics. func (obj *Base) Str() string { panic("not an str") // yes, i think this is the correct grammar } // Int represents the value of this type as an integer if it is one. If this is // not an integer, then this panics. func (obj *Base) Int() int64 { panic("not an int") } // Float represents the value of this type as a float if it is one. If this is // not a float, then this panics. func (obj *Base) Float() float64 { panic("not a float") } // List represents the value of this type as a list if it is one. If this is not // a list, then this panics. func (obj *Base) List() []Value { panic("not a list") } // Map represents the value of this type as a dictionary if it is one. If this // is not a map, then this panics. func (obj *Base) Map() map[Value]Value { panic("not a list") } // Struct represents the value of this type as a struct if it is one. If this is // not a struct, then this panics. func (obj *Base) Struct() map[string]Value { panic("not a struct") } // Func represents the value of this type as a function if it is one. If this is // not a function, then this panics. func (obj *Base) Func() interface{} { panic("not a func") } // Less compares to value and returns true if we're smaller. It is recommended // that this base implementation of the method be replaced in the specific type. // This *may* panic if the two types aren't the same. // NOTE: this can be used as an example template to write your own function. //func (obj *Base) Less(v Value) bool { // // TODO: cheap less, be smarter in each type eg: int's should cmp as int // return obj.String() < v.String() //} // Cmp returns an error if this value isn't the same as the arg passed in. This // implementation uses the base Less implementation and should be replaced. It // is always nice to implement this properly so that we get better error output. // NOTE: this can be used as an example template to write your own function. //func (obj *Base) Cmp(v Value) error { // // if they're both true or both false, then they must be the same, // // because we expect that if x < & && y < x then x == y // if obj.Less(v) != v.Less(obj) { // return fmt.Errorf("values differ according to less") // } // return nil //} // BoolValue represents a boolean value. type BoolValue struct { Base V bool } // NewBool creates a new boolean value. func NewBool() *BoolValue { return &BoolValue{} } // String returns a visual representation of this value. func (obj *BoolValue) String() string { return strconv.FormatBool(obj.V) // true or false //if obj.V { // return "true" //} //return "false" } // Type returns the type data structure that represents this type. func (obj *BoolValue) Type() *Type { return NewType("bool") } // Less compares to value and returns true if we're smaller. This panics if the // two types aren't the same. func (obj *BoolValue) Less(v Value) bool { //return obj.String() < v.(*BoolValue).String() if obj.V != v.(*BoolValue).V { // there must be one false // f, t -> t ; t, f -> f return !obj.V // TODO: should `false` sort less? } return false // they're the same } // Cmp returns an error if this value isn't the same as the arg passed in. func (obj *BoolValue) Cmp(val Value) error { if obj == nil || val == nil { return fmt.Errorf("cannot cmp to nil") } if err := obj.Type().Cmp(val.Type()); err != nil { return errwrap.Wrapf(err, "cannot cmp types") } if obj.V != val.(*BoolValue).V { return fmt.Errorf("values are different") } return nil } // Copy returns a copy of this value. func (obj *BoolValue) Copy() Value { return &BoolValue{V: obj.V} } // Value returns the raw value of this type. func (obj *BoolValue) Value() interface{} { return obj.V } // Bool represents the value of this type as a bool if it is one. If this is not // a bool, then this panics. func (obj *BoolValue) Bool() bool { return obj.V } // StrValue represents a string value. type StrValue struct { Base V string } // NewStr creates a new string value. func NewStr() *StrValue { return &StrValue{} } // String returns a visual representation of this value. func (obj *StrValue) String() string { return strconv.Quote(obj.V) // wraps in quotes, turns tabs into \t etc... //return fmt.Sprintf(`"%s"`, obj.V) } // Type returns the type data structure that represents this type. func (obj *StrValue) Type() *Type { return NewType("str") } // Less compares to value and returns true if we're smaller. This panics if the // two types aren't the same. func (obj *StrValue) Less(v Value) bool { return obj.V < v.(*StrValue).V } // Cmp returns an error if this value isn't the same as the arg passed in. func (obj *StrValue) Cmp(val Value) error { if obj == nil || val == nil { return fmt.Errorf("cannot cmp to nil") } if err := obj.Type().Cmp(val.Type()); err != nil { return errwrap.Wrapf(err, "cannot cmp types") } if obj.V != val.(*StrValue).V { return fmt.Errorf("values are different") } return nil } // Copy returns a copy of this value. func (obj *StrValue) Copy() Value { return &StrValue{V: obj.V} } // Value returns the raw value of this type. func (obj *StrValue) Value() interface{} { return obj.V } // Str represents the value of this type as a string if it is one. If this is // not a string, then this panics. func (obj *StrValue) Str() string { return obj.V } // IntValue represents an integer value. type IntValue struct { Base V int64 } // NewInt creates a new int value. func NewInt() *IntValue { return &IntValue{} } // String returns a visual representation of this value. func (obj *IntValue) String() string { return strconv.FormatInt(obj.V, 10) } // Type returns the type data structure that represents this type. func (obj *IntValue) Type() *Type { return NewType("int") } // Less compares to value and returns true if we're smaller. This panics if the // two types aren't the same. func (obj *IntValue) Less(v Value) bool { return obj.V < v.(*IntValue).V } // Cmp returns an error if this value isn't the same as the arg passed in. func (obj *IntValue) Cmp(val Value) error { if obj == nil || val == nil { return fmt.Errorf("cannot cmp to nil") } if err := obj.Type().Cmp(val.Type()); err != nil { return errwrap.Wrapf(err, "cannot cmp types") } if obj.V != val.(*IntValue).V { return fmt.Errorf("values are different") } return nil } // Copy returns a copy of this value. func (obj *IntValue) Copy() Value { return &IntValue{V: obj.V} } // Value returns the raw value of this type. func (obj *IntValue) Value() interface{} { return obj.V } // Int represents the value of this type as an integer if it is one. If this is // not an integer, then this panics. func (obj *IntValue) Int() int64 { return obj.V } // FloatValue represents an integer value. type FloatValue struct { Base V float64 } // NewFloat creates a new float value. func NewFloat() *FloatValue { return &FloatValue{} } // String returns a visual representation of this value. func (obj *FloatValue) String() string { // TODO: is this the right display mode? // FIXME: floats don't print nicely: https://github.com/golang/go/issues/46118 return strconv.FormatFloat(obj.V, 'f', -1, 64) // -1 for exact precision } // Type returns the type data structure that represents this type. func (obj *FloatValue) Type() *Type { return NewType("float") } // Less compares to value and returns true if we're smaller. This panics if the // two types aren't the same. func (obj *FloatValue) Less(v Value) bool { return obj.V < v.(*FloatValue).V } // Cmp returns an error if this value isn't the same as the arg passed in. func (obj *FloatValue) Cmp(val Value) error { if obj == nil || val == nil { return fmt.Errorf("cannot cmp to nil") } if err := obj.Type().Cmp(val.Type()); err != nil { return errwrap.Wrapf(err, "cannot cmp types") } // FIXME: should we compare with an epsilon? if obj.V != val.(*FloatValue).V { return fmt.Errorf("values are different") } return nil } // Copy returns a copy of this value. func (obj *FloatValue) Copy() Value { return &FloatValue{V: obj.V} } // Value returns the raw value of this type. func (obj *FloatValue) Value() interface{} { return obj.V } // Float represents the value of this type as a float if it is one. If this is // not a float, then this panics. func (obj *FloatValue) Float() float64 { return obj.V } // ListValue represents a list value. type ListValue struct { Base V []Value // all elements must have type T.Val T *Type } // NewList creates a new list with the specified list type. func NewList(t *Type) *ListValue { if t.Kind != KindList { return nil // sanity check } return &ListValue{ V: []Value{}, T: t, } } // String returns a visual representation of this value. func (obj *ListValue) String() string { var s []string for _, x := range obj.V { s = append(s, x.String()) } return fmt.Sprintf("[%s]", strings.Join(s, ", ")) } // Type returns the type data structure that represents this type. func (obj *ListValue) Type() *Type { return obj.T } // Less compares to value and returns true if we're smaller. This panics if the // two types aren't the same. func (obj *ListValue) Less(v Value) bool { V := v.(*ListValue).V i, j := len(obj.V), len(V) for x := 0; x < i && x < j; x++ { // keep to min count of both lists if obj.V[x].Less(V[x]) { return true } } return i < j // TODO: i think this is correct :) } // Cmp returns an error if this value isn't the same as the arg passed in. func (obj *ListValue) Cmp(val Value) error { if obj == nil || val == nil { return fmt.Errorf("cannot cmp to nil") } if err := obj.Type().Cmp(val.Type()); err != nil { return errwrap.Wrapf(err, "cannot cmp types") } cmp := val.(*ListValue) if len(obj.V) != len(cmp.V) { return fmt.Errorf("lists have different lengths") } for i := range obj.V { if err := obj.V[i].Cmp(cmp.V[i]); err != nil { return errwrap.Wrapf(err, "index %d did not cmp", i) } } return nil } // Copy returns a copy of this value. func (obj *ListValue) Copy() Value { v := []Value{} for _, x := range obj.V { v = append(v, x.Copy()) } return &ListValue{ V: v, T: obj.T.Copy(), } } // Value returns the raw value of this type. func (obj *ListValue) Value() interface{} { typ := obj.T.Reflect() // create an empty slice (of len=0) with room for cap=len(obj.V) elements val := reflect.MakeSlice(typ, 0, len(obj.V)) for _, x := range obj.V { val = reflect.Append(val, reflect.ValueOf(x.Value())) // recurse } return val.Interface() } // List represents the value of this type as a list if it is one. If this is not // a list, then this panics. func (obj *ListValue) List() []Value { return obj.V } // Add adds an element to this list. It errors if the type does not match. func (obj *ListValue) Add(v Value) error { if obj.T.Val.Kind != KindVariant { // skip cmp if dest is a variant if err := obj.T.Val.Cmp(v.Type()); err != nil { return errwrap.Wrapf(err, "value does not match list element type") } } obj.V = append(obj.V, v) return nil } // Lookup looks up a value by index. On success it also returns the Value. func (obj *ListValue) Lookup(index int) (value Value, exists bool) { if index >= 0 && index < len(obj.V) { return obj.V[index], true // found } return nil, false } // Contains searches for a value in the list. On success it returns the index. func (obj *ListValue) Contains(v Value) (index int, exists bool) { for i, x := range obj.V { if v.Cmp(x) == nil { return i, true } } return -1, false } // MapValue represents a dictionary value. type MapValue struct { Base // the types of all keys and values are represented inside of T V map[Value]Value T *Type } // NewMap creates a new map with the specified map type. func NewMap(t *Type) *MapValue { if t.Kind != KindMap { return nil // sanity check } return &MapValue{ V: make(map[Value]Value), T: t, } } // String returns a visual representation of this value. func (obj *MapValue) String() string { keys := []Value{} for k := range obj.V { keys = append(keys, k) } sort.Sort(ValueSlice(keys)) // deterministic print order var s []string for _, k := range keys { s = append(s, fmt.Sprintf("%s: %s", k.String(), obj.V[k].String())) } return fmt.Sprintf("{%s}", strings.Join(s, ", ")) } // Type returns the type data structure that represents this type. func (obj *MapValue) Type() *Type { return obj.T } // Less compares to value and returns true if we're smaller. This panics if the // two types aren't the same. func (obj *MapValue) Less(v Value) bool { V := v.(*MapValue) return obj.String() < V.String() // FIXME: implement a proper less func } // Cmp returns an error if this value isn't the same as the arg passed in. func (obj *MapValue) Cmp(val Value) error { if obj == nil || val == nil { return fmt.Errorf("cannot cmp to nil") } if err := obj.Type().Cmp(val.Type()); err != nil { return errwrap.Wrapf(err, "cannot cmp types") } cmp := val.(*MapValue) if len(obj.V) != len(cmp.V) { return fmt.Errorf("maps have different lengths") } for k := range obj.V { v, exists := cmp.V[k] if !exists { return fmt.Errorf("key %s does not exist", k) } if err := obj.V[k].Cmp(v); err != nil { return errwrap.Wrapf(err, "index %s did not cmp", k) } } return nil } // Copy returns a copy of this value. func (obj *MapValue) Copy() Value { m := map[Value]Value{} for k, v := range obj.V { m[k.Copy()] = v.Copy() } return &MapValue{ V: m, T: obj.T.Copy(), } } // Value returns the raw value of this type. func (obj *MapValue) Value() interface{} { typ := obj.T.Reflect() val := reflect.MakeMap(typ) for k, v := range obj.V { val.SetMapIndex(reflect.ValueOf(k.Value()), reflect.ValueOf(v.Value())) // dual recurse } return val.Interface() } // Map represents the value of this type as a dictionary if it is one. If this // is not a map, then this panics. func (obj *MapValue) Map() map[Value]Value { return obj.V } // Add adds an element to this map. It errors if the types do not match. func (obj *MapValue) Add(k, v Value) error { // TODO: change method name? //if obj.T.Key.Kind != KindVariant { if err := obj.T.Key.Cmp(k.Type()); err != nil { return errwrap.Wrapf(err, "key does not match map key type") } //} if obj.T.Val.Kind != KindVariant { // skip cmp if dest is a variant if err := obj.T.Val.Cmp(v.Type()); err != nil { return errwrap.Wrapf(err, "val does not match map val type") } } obj.V[k] = v return nil } // Lookup searches the map for a key. On success it also returns the Value. func (obj *MapValue) Lookup(key Value) (value Value, exists bool) { //v, exists := obj.V[key] // not what we want! for k, v := range obj.V { if k.Cmp(key) == nil { return v, true // found } } return nil, false } // StructValue represents a struct value. The keys are ordered. // TODO: if all functions require arg names to call, we don't need to order! type StructValue struct { Base V map[string]Value // each field can have a different type T *Type // contains ordered field types } // NewStruct creates a new struct with the specified field types. func NewStruct(t *Type) *StructValue { if t.Kind != KindStruct { return nil // sanity check } v := make(map[string]Value) for _, k := range t.Ord { v[k] = t.Map[k].New() // don't leave struct fields uninitialized } return &StructValue{ V: v, T: t, // TODO: should we allow changes to this after create? } } // String returns a visual representation of this value. func (obj *StructValue) String() string { var s []string for _, k := range obj.T.Ord { s = append(s, fmt.Sprintf("%s: %s", k, obj.V[k].String())) } return fmt.Sprintf("struct{%s}", strings.Join(s, "; ")) } // Type returns the type data structure that represents this type. func (obj *StructValue) Type() *Type { return obj.T } // Less compares to value and returns true if we're smaller. This panics if the // two types aren't the same. func (obj *StructValue) Less(v Value) bool { V := v.(*StructValue) return obj.String() < V.String() // FIXME: implement a proper less func } // Cmp returns an error if this value isn't the same as the arg passed in. func (obj *StructValue) Cmp(val Value) error { if obj == nil || val == nil { return fmt.Errorf("cannot cmp to nil") } if err := obj.Type().Cmp(val.Type()); err != nil { return errwrap.Wrapf(err, "cannot cmp types") } cmp := val.(*StructValue) // compare values for k := range obj.V { if err := obj.V[k].Cmp(cmp.V[k]); err != nil { return errwrap.Wrapf(err, "field %s did not cmp", k) } } return nil } // Copy returns a copy of this value. func (obj *StructValue) Copy() Value { m := map[string]Value{} for k, v := range obj.V { m[k] = v.Copy() } return &StructValue{ V: m, T: obj.T.Copy(), } } // Value returns the raw value of this type. func (obj *StructValue) Value() interface{} { typ := obj.T.Reflect() val := reflect.New(typ).Elem() // New returns a PtrTo(typ) for _, k := range obj.T.Ord { val.FieldByName(k).Set(reflect.ValueOf(obj.V[k].Value())) // recurse } return val.Interface() } // Struct represents the value of this type as a struct if it is one. If this is // not a struct, then this panics. func (obj *StructValue) Struct() map[string]Value { return obj.V } // Set sets a field to this value. It errors if the types do not match. func (obj *StructValue) Set(k string, v Value) error { // TODO: change method name? typ, exists := obj.T.Map[k] if !exists { return fmt.Errorf("field %s does not exist", k) } if typ.Kind != KindVariant { // skip cmp if dest is a variant if err := typ.Cmp(v.Type()); err != nil { return errwrap.Wrapf(err, "value of type does not match field type") } } obj.V[k] = v // set return nil } // Lookup searches the struct for a key. On success it also returns the Value. func (obj *StructValue) Lookup(k string) (value Value, exists bool) { v, exists := obj.V[k] // FIXME: should we return zero values if missing? return v, exists } // FuncValue represents a function which takes a list of Value arguments and // returns a Value. It can also return an error which could represent that // something went horribly wrong. (Think, an internal panic.) // // This is not general enough to represent all functions in the language (see // the full.FuncValue), but it is a useful common case. // // FuncValue is not a Value, but it is a useful building block for implementing // Func nodes. type FuncValue struct { Base V func(context.Context, []Value) (Value, error) T *Type // contains ordered field types, arg names are a bonus part } // NewFunc creates a useless function which will get overwritten by something // more useful later. func NewFunc(t *Type) *FuncValue { if t.Kind != KindFunc { return nil // sanity check } v := func(context.Context, []Value) (Value, error) { // You were not supposed to call the temporary function, you // were supposed to replace it with a real implementation! return nil, fmt.Errorf("nil function") } // return an empty interface{} return &FuncValue{ V: v, T: t, } } // String returns a visual representation of this value. func (obj *FuncValue) String() string { return fmt.Sprintf("func(%+v)", obj.T) // TODO: can't print obj.V w/o vet warning } // Type returns the type data structure that represents this type. func (obj *FuncValue) Type() *Type { return obj.T } // Less compares to value and returns true if we're smaller. This panics if the // two types aren't the same. In this situation, they can't be compared so we // panic. func (obj *FuncValue) Less(v Value) bool { //V := v.(*FuncValue) //return obj.String() < V.String() // FIXME: implement a proper less func panic("you cannot compare functions") } // Cmp returns an error if this value isn't the same as the arg passed in. In // this situation, they can't be compared so we panic. func (obj *FuncValue) Cmp(val Value) error { //if obj == nil || val == nil { // return fmt.Errorf("cannot cmp to nil") //} //if err := obj.Type().Cmp(val.Type()); err != nil { // return errwrap.Wrapf(err, "cannot cmp types") //} // //return fmt.Errorf("cannot cmp funcs") // TODO: can we ? panic("you cannot compare functions") } // Copy returns a copy of this value. func (obj *FuncValue) Copy() Value { return &FuncValue{ V: obj.V, T: obj.T.Copy(), } } // Value returns the raw value of this type. func (obj *FuncValue) Value() interface{} { //typ := obj.T.Reflect() // //// wrap our function with the translation that is necessary //fn := func(args []reflect.Value) (results []reflect.Value) { // build // innerArgs := []Value{} // for _, x := range args { // v, err := ValueOf(x) // reflect.Value -> Value // if err != nil { // panic(fmt.Sprintf("can't determine value of %+v", x)) // } // innerArgs = append(innerArgs, v) // } // result, err := obj.V(innerArgs) // call it // if err != nil { // // when calling our function with the Call method, then // // we get the error output and have a chance to decide // // what to do with it, but when calling it from within // // a normal golang function call, the error represents // // that something went horribly wrong, aka a panic... // panic(fmt.Sprintf("function panic: %+v", err)) // } // return []reflect.Value{reflect.ValueOf(result.Value())} // only one result //} //val := reflect.MakeFunc(typ, fn) //return val.Interface() return obj.V } // Call runs the function value and returns its result. It returns an error if // something goes wrong during execution, and panic's if you call this with // inappropriate input types, or if it returns an inappropriate output type. func (obj *FuncValue) Call(ctx context.Context, args []Value) (Value, error) { // cmp input args type to obj.T length := len(obj.T.Ord) if length != len(args) { return nil, fmt.Errorf("arg length of %d does not match expected of %d", len(args), length) } for i := 0; i < length; i++ { if err := args[i].Type().Cmp(obj.T.Map[obj.T.Ord[i]]); err != nil { return nil, errwrap.Wrapf(err, "cannot cmp input types") } } result, err := obj.V(ctx, args) // call it if result == nil { if err == nil { return nil, fmt.Errorf("function returned nil result") } return nil, err } if err := result.Type().Cmp(obj.T.Out); err != nil { return nil, errwrap.Wrapf(err, "cannot cmp return types") } return result, err } // VariantValue represents a variant value. type VariantValue struct { Base V Value // formerly I experimented with using interface{} instead T *Type } // NewVariant creates a new variant value. // TODO: I haven't thought about this thoroughly yet. func NewVariant(t *Type) *VariantValue { if t.Kind != KindVariant { return nil // sanity check } return &VariantValue{ T: t, } } // String returns a visual representation of this value. func (obj *VariantValue) String() string { //return fmt.Sprintf("%v", obj.V) return obj.V.String() } // Type returns the type data structure that represents this type. func (obj *VariantValue) Type() *Type { return obj.T } // Less compares to value and returns true if we're smaller. This panics if the // two types aren't the same. For variants, the two sub types must be the same. func (obj *VariantValue) Less(v Value) bool { //return obj.String() < v.String() // FIXME: implement a proper less func V := v.(*VariantValue).V return obj.V.Less(V) } // Cmp returns an error if this value isn't the same as the arg passed in. func (obj *VariantValue) Cmp(val Value) error { if obj == nil || val == nil { return fmt.Errorf("cannot cmp to nil") } if err := obj.Type().Cmp(val.Type()); err != nil { return errwrap.Wrapf(err, "cannot cmp types") } V := val.(*VariantValue).V //if !reflect.DeepEqual(obj.V, V) { // return fmt.Errorf("values are different") //} if err := obj.V.Type().Cmp(V.Type()); err != nil { return errwrap.Wrapf(err, "cannot cmp sub types") } if err := obj.V.Cmp(V); err != nil { return errwrap.Wrapf(err, "values are different") } return nil } // Copy returns a copy of this value. func (obj *VariantValue) Copy() Value { return &VariantValue{ V: obj.V.Copy(), T: obj.T.Copy(), } } // Value returns the raw value of this type. func (obj *VariantValue) Value() interface{} { return obj.V.Value() } // Bool represents the value of this type as a bool if it is one. If this is not // a bool, then this panics. func (obj *VariantValue) Bool() bool { //return obj.V.(bool) return obj.V.Bool() } // Str represents the value of this type as a string if it is one. If this is // not a string, then this panics. func (obj *VariantValue) Str() string { //return obj.V.(string) return obj.V.Str() } // Int represents the value of this type as an integer if it is one. If this is // not an integer, then this panics. func (obj *VariantValue) Int() int64 { //return obj.V.(int64) return obj.V.Int() } // Float represents the value of this type as a float if it is one. If this is // not a float, then this panics. func (obj *VariantValue) Float() float64 { //return obj.V.(float64) return obj.V.Float() } // List represents the value of this type as a list if it is one. If this is not // a list, then this panics. func (obj *VariantValue) List() []Value { return obj.V.List() } // Map represents the value of this type as a dictionary if it is one. If this // is not a map, then this panics. func (obj *VariantValue) Map() map[Value]Value { return obj.V.Map() } // Struct represents the value of this type as a struct if it is one. If this is // not a struct, then this panics. func (obj *VariantValue) Struct() map[string]Value { return obj.V.Struct() } // Func represents the value of this type as a function if it is one. If this is // not a function, then this panics. func (obj *VariantValue) Func() interface{} { return obj.V.Func() }