diff --git a/lang/types/type.go b/lang/types/type.go index b8f03561..eec7d488 100644 --- a/lang/types/type.go +++ b/lang/types/type.go @@ -51,6 +51,7 @@ const ( // Basic types defined here as a convenience for use with Type.Cmp(X). var ( + TypeNil = NewType("nil") TypeBool = NewType("bool") TypeStr = NewType("str") TypeInt = NewType("int") @@ -384,6 +385,10 @@ func NewType(s string) *Type { // return a type with correctly unified unification variables. func newType(s string, table map[uint]*Elem) *Type { switch s { + case "nil": + return &Type{ + Kind: KindNil, + } case "bool": return &Type{ Kind: KindBool, @@ -683,6 +688,8 @@ func (obj *Type) New() Value { panic("malformed type") } switch obj.Kind { + case KindNil: + return NewNil() case KindBool: return NewBool() case KindStr: @@ -717,6 +724,8 @@ func (obj *Type) String() string { // helper function that is used by the real String function. func (obj *Type) string(table map[*Elem]uint) string { switch obj.Kind { + case KindNil: + return "nil" case KindBool: return "bool" case KindStr: @@ -840,6 +849,8 @@ func (obj *Type) cmp(typ *Type, table1, table2 map[*Elem]uint) error { return fmt.Errorf("base kind does not match (%+v != %+v)", obj.Kind, typ.Kind) } switch obj.Kind { + case KindNil: + return nil case KindBool: return nil case KindStr: @@ -1124,6 +1135,8 @@ func (obj *Type) HasVariant() bool { return false } switch obj.Kind { + case KindNil: + return false case KindBool: return false case KindStr: @@ -1212,6 +1225,8 @@ func (obj *Type) HasUni() bool { } switch obj.Kind { + case KindNil: + return false case KindBool: return false case KindStr: @@ -1330,6 +1345,8 @@ func (obj *Type) ComplexCmp(typ *Type) (string, error) { // only container types are left to match... switch obj.Kind { + case KindNil: + return "", nil // regular cmp case KindBool: return "", nil // regular cmp case KindStr: diff --git a/lang/types/value.go b/lang/types/value.go index 8bb2f2c8..132c4820 100644 --- a/lang/types/value.go +++ b/lang/types/value.go @@ -599,6 +599,50 @@ func (obj *Base) Func() interface{} { // return nil //} +// NilValue represents a nil value. It should be used only in rare situations. +type NilValue struct { + Base +} + +// NewNil creates a new nil value. +func NewNil() *NilValue { return &NilValue{} } + +// String returns a visual representation of this value. +func (obj *NilValue) String() string { + return "nil" +} + +// Type returns the type data structure that represents this type. +func (obj *NilValue) Type() *Type { return NewType("nil") } + +// Less compares to value and returns true if we're smaller. This panics if the +// two types aren't the same. This always returns false for nil. +func (obj *NilValue) Less(v Value) bool { + return false // they're the same +} + +// Cmp returns an error if this value isn't the same as the arg passed in. +func (obj *NilValue) 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 nil +} + +// Copy returns a copy of this value. +func (obj *NilValue) Copy() Value { + return &NilValue{} +} + +// Value returns the raw value of this type. +func (obj *NilValue) Value() interface{} { + return nil +} + // BoolValue represents a boolean value. type BoolValue struct { Base