1472 lines
42 KiB
Go
1472 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
|
|
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()
|
|
}
|