Most of the time, we don't need to have a dynamic call sub graph, since the actual function call could be represented statically as it originally was before lambda functions were implemented. Simplifying the graph shape has important performance benefits in terms of both keep the graph smaller (memory, etc) and in avoiding the need to run transactions at runtime (speed) to reshape the graph. Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
1475 lines
42 KiB
Go
1475 lines
42 KiB
Go
// Mgmt
|
|
// Copyright (C) James Shubin and the project contributors
|
|
// Written by James Shubin <james@shubin.ca> 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 <https://www.gnu.org/licenses/>.
|
|
//
|
|
// 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")
|
|
|
|
// ValueFalse is a false value in our system. Can be used where needed.
|
|
ValueFalse, _ = ValueOfGolang(false)
|
|
|
|
// ValueTrue is a true value in our system. Can be used where needed.
|
|
ValueTrue, _ = ValueOfGolang(true)
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
// Len returns the number of elements in this list.
|
|
func (obj *ListValue) Len() int {
|
|
return len(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
|
|
}
|
|
|
|
// Len returns the number of elements in this map.
|
|
func (obj *MapValue) Len() int {
|
|
return len(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
|
|
if obj.T == nil {
|
|
return nil, fmt.Errorf("the type is nil")
|
|
}
|
|
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()
|
|
}
|