force using TrueValue, FalseValue, UndefinedValue
This commit is contained in:
parent
481d79cf68
commit
85da0cdc24
27 changed files with 214 additions and 128 deletions
|
@ -123,7 +123,7 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
}
|
||||
case []byte:
|
||||
if bytes.Compare(expected, actual.([]byte)) != 0 {
|
||||
return failExpectedActual(t, expected, actual, msg...)
|
||||
return failExpectedActual(t, string(expected), string(actual.([]byte)), msg...)
|
||||
}
|
||||
case []int:
|
||||
if !equalIntSlice(expected, actual.([]int)) {
|
||||
|
@ -160,7 +160,9 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
case *objects.Char:
|
||||
return Equal(t, expected.Value, actual.(*objects.Char).Value)
|
||||
case *objects.Bool:
|
||||
return Equal(t, expected.Value, actual.(*objects.Bool).Value)
|
||||
if expected != actual {
|
||||
return failExpectedActual(t, expected, actual, msg...)
|
||||
}
|
||||
case *objects.ReturnValue:
|
||||
return Equal(t, expected.Value, actual.(objects.ReturnValue).Value)
|
||||
case *objects.Array:
|
||||
|
@ -169,7 +171,7 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
return equalObjectSlice(t, expected.Value, actual.(*objects.ImmutableArray).Value)
|
||||
case *objects.Bytes:
|
||||
if bytes.Compare(expected.Value, actual.(*objects.Bytes).Value) != 0 {
|
||||
return failExpectedActual(t, expected.Value, actual.(*objects.Bytes).Value, msg...)
|
||||
return failExpectedActual(t, string(expected.Value), string(actual.(*objects.Bytes).Value), msg...)
|
||||
}
|
||||
case *objects.Map:
|
||||
return equalObjectMap(t, expected.Value, actual.(*objects.Map).Value)
|
||||
|
@ -180,7 +182,9 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
case *objects.Closure:
|
||||
return equalClosure(t, expected, actual.(*objects.Closure))
|
||||
case *objects.Undefined:
|
||||
return true
|
||||
if expected != actual {
|
||||
return failExpectedActual(t, expected, actual, msg...)
|
||||
}
|
||||
case *objects.Error:
|
||||
return Equal(t, expected.Value, actual.(*objects.Error).Value)
|
||||
case error:
|
||||
|
|
|
@ -21,7 +21,16 @@ func (b *Bytecode) Decode(r io.Reader) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return dec.Decode(&b.Constants)
|
||||
if err := dec.Decode(&b.Constants); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// replace Bool and Undefined with known value
|
||||
for i, v := range b.Constants {
|
||||
b.Constants[i] = cleanupObjects(v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode writes Bytecode data to the writer.
|
||||
|
@ -32,9 +41,32 @@ func (b *Bytecode) Encode(w io.Writer) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// constants
|
||||
return enc.Encode(b.Constants)
|
||||
}
|
||||
|
||||
func cleanupObjects(o objects.Object) objects.Object {
|
||||
switch o := o.(type) {
|
||||
case *objects.Bool:
|
||||
if o.IsFalsy() {
|
||||
return objects.FalseValue
|
||||
}
|
||||
return objects.TrueValue
|
||||
case *objects.Undefined:
|
||||
return objects.UndefinedValue
|
||||
case *objects.Array:
|
||||
for i, v := range o.Value {
|
||||
o.Value[i] = cleanupObjects(v)
|
||||
}
|
||||
case *objects.Map:
|
||||
for k, v := range o.Value {
|
||||
o.Value[k] = cleanupObjects(v)
|
||||
}
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func init() {
|
||||
gob.Register(&objects.Int{})
|
||||
gob.Register(&objects.Float{})
|
||||
|
|
|
@ -14,16 +14,19 @@ func TestBytecode(t *testing.T) {
|
|||
|
||||
testBytecodeSerialization(t, bytecode(
|
||||
concat(), objectsArray(
|
||||
objects.UndefinedValue,
|
||||
&objects.Array{
|
||||
Value: objectsArray(
|
||||
&objects.Int{Value: 12},
|
||||
&objects.String{Value: "foo"},
|
||||
&objects.Bool{Value: true},
|
||||
objects.TrueValue,
|
||||
objects.FalseValue,
|
||||
&objects.Float{Value: 93.11},
|
||||
&objects.Char{Value: 'x'},
|
||||
objects.UndefinedValue,
|
||||
),
|
||||
},
|
||||
&objects.Bool{Value: false},
|
||||
objects.FalseValue,
|
||||
&objects.Char{Value: 'y'},
|
||||
&objects.Float{Value: 93.11},
|
||||
compiledFunction(1, 0,
|
||||
|
@ -36,11 +39,12 @@ func TestBytecode(t *testing.T) {
|
|||
&objects.Map{
|
||||
Value: map[string]objects.Object{
|
||||
"a": &objects.Float{Value: -93.1},
|
||||
"b": &objects.Bool{Value: false},
|
||||
"b": objects.FalseValue,
|
||||
"c": objects.UndefinedValue,
|
||||
},
|
||||
},
|
||||
&objects.String{Value: "bar"},
|
||||
&objects.Undefined{})))
|
||||
objects.UndefinedValue)))
|
||||
|
||||
testBytecodeSerialization(t, bytecode(
|
||||
concat(
|
||||
|
|
|
@ -43,7 +43,11 @@ func FuncARB(fn func() bool) *objects.UserFunction {
|
|||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
return &objects.Bool{Value: fn()}, nil
|
||||
if fn() {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -340,7 +344,11 @@ func FuncAFIRB(fn func(float64, int) bool) *objects.UserFunction {
|
|||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.Bool{Value: fn(f1, i2)}, nil
|
||||
if fn(f1, i2) {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +367,11 @@ func FuncAFRB(fn func(float64) bool) *objects.UserFunction {
|
|||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.Bool{Value: fn(f1)}, nil
|
||||
if fn(f1) {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ func TestFuncAIR(t *testing.T) {
|
|||
uf := stdlib.FuncAIR(func(int) {})
|
||||
ret, err := uf.Call(&objects.Int{Value: 10})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Undefined{}, ret)
|
||||
assert.Equal(t, objects.UndefinedValue, ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ func TestFuncAR(t *testing.T) {
|
|||
uf := stdlib.FuncAR(func() {})
|
||||
ret, err := uf.Call()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Undefined{}, ret)
|
||||
assert.Equal(t, objects.UndefinedValue, ret)
|
||||
ret, err = uf.Call(objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
@ -227,7 +227,7 @@ func TestFuncAFRB(t *testing.T) {
|
|||
})
|
||||
ret, err := uf.Call(&objects.Float{Value: 0.1})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Bool{Value: true}, ret)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
ret, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
||||
|
@ -279,7 +279,7 @@ func TestFuncAFIRB(t *testing.T) {
|
|||
})
|
||||
ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Int{Value: 20})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Bool{Value: true}, ret)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
ret, err = uf.Call()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
ret, err = uf.Call(objects.TrueValue)
|
||||
|
|
|
@ -6,11 +6,12 @@ import (
|
|||
|
||||
// Bool represents a boolean value.
|
||||
type Bool struct {
|
||||
Value bool
|
||||
// this is intentionally non-public to force using objects.TrueValue and FalseValue always
|
||||
value bool
|
||||
}
|
||||
|
||||
func (o *Bool) String() string {
|
||||
if o.Value {
|
||||
if o.value {
|
||||
return "true"
|
||||
}
|
||||
|
||||
|
@ -30,22 +31,34 @@ func (o *Bool) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Bool) Copy() Object {
|
||||
v := Bool{Value: o.Value}
|
||||
return &v
|
||||
return o
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Bool) IsFalsy() bool {
|
||||
return !o.Value
|
||||
return !o.value
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Bool) Equals(x Object) bool {
|
||||
t, ok := x.(*Bool)
|
||||
if !ok {
|
||||
return false
|
||||
return o == x
|
||||
}
|
||||
|
||||
// GobDecode decodes bool value from input bytes.
|
||||
func (o *Bool) GobDecode(b []byte) (err error) {
|
||||
o.value = b[0] == 1
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GobEncode encodes bool values into bytes.
|
||||
func (o *Bool) GobEncode() (b []byte, err error) {
|
||||
if o.value {
|
||||
b = []byte{1}
|
||||
} else {
|
||||
b = []byte{0}
|
||||
}
|
||||
|
||||
return o.Value == t.Value
|
||||
return
|
||||
}
|
||||
|
|
|
@ -77,7 +77,11 @@ func builtinBool(args ...Object) (Object, error) {
|
|||
|
||||
v, ok := ToBool(args[0])
|
||||
if ok {
|
||||
return &Bool{Value: v}, nil
|
||||
if v {
|
||||
return TrueValue, nil
|
||||
}
|
||||
|
||||
return FalseValue, nil
|
||||
}
|
||||
|
||||
return UndefinedValue, nil
|
||||
|
|
|
@ -89,7 +89,7 @@ func builtinIsUndefined(args ...Object) (Object, error) {
|
|||
return nil, ErrWrongNumArguments
|
||||
}
|
||||
|
||||
if _, ok := args[0].(*Undefined); ok {
|
||||
if args[0] == UndefinedValue {
|
||||
return TrueValue, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
// ToString will try to convert object o to string value.
|
||||
func ToString(o Object) (v string, ok bool) {
|
||||
if _, isUndefined := o.(*Undefined); isUndefined {
|
||||
if o == UndefinedValue {
|
||||
//ok = false
|
||||
return
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ func ToInt(o Object) (v int, ok bool) {
|
|||
v = int(o.Value)
|
||||
ok = true
|
||||
case *Bool:
|
||||
if o.Value {
|
||||
if o == TrueValue {
|
||||
v = 1
|
||||
}
|
||||
ok = true
|
||||
|
@ -65,7 +65,7 @@ func ToInt64(o Object) (v int64, ok bool) {
|
|||
v = int64(o.Value)
|
||||
ok = true
|
||||
case *Bool:
|
||||
if o.Value {
|
||||
if o == TrueValue {
|
||||
v = 1
|
||||
}
|
||||
ok = true
|
||||
|
@ -150,7 +150,7 @@ func objectToInterface(o Object) (res interface{}) {
|
|||
case *Float:
|
||||
res = o.Value
|
||||
case *Bool:
|
||||
res = o.Value
|
||||
res = o == TrueValue
|
||||
case *Char:
|
||||
res = o.Value
|
||||
case *Bytes:
|
||||
|
@ -176,7 +176,7 @@ func objectToInterface(o Object) (res interface{}) {
|
|||
func FromInterface(v interface{}) (Object, error) {
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
return &Undefined{}, nil
|
||||
return UndefinedValue, nil
|
||||
case string:
|
||||
return &String{Value: v}, nil
|
||||
case int64:
|
||||
|
@ -184,7 +184,10 @@ func FromInterface(v interface{}) (Object, error) {
|
|||
case int:
|
||||
return &Int{Value: int64(v)}, nil
|
||||
case bool:
|
||||
return &Bool{Value: v}, nil
|
||||
if v {
|
||||
return TrueValue, nil
|
||||
}
|
||||
return FalseValue, nil
|
||||
case rune:
|
||||
return &Char{Value: v}, nil
|
||||
case byte:
|
||||
|
|
|
@ -41,28 +41,28 @@ func TestFloat_BinaryOp(t *testing.T) {
|
|||
// float < float
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := float64(-2); r <= 2.1; r += 0.4 {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Less, &objects.Float{Value: r}, &objects.Bool{Value: l < r})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Less, &objects.Float{Value: r}, boolValue(l < r))
|
||||
}
|
||||
}
|
||||
|
||||
// float > float
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := float64(-2); r <= 2.1; r += 0.4 {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Greater, &objects.Float{Value: r}, &objects.Bool{Value: l > r})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Greater, &objects.Float{Value: r}, boolValue(l > r))
|
||||
}
|
||||
}
|
||||
|
||||
// float <= float
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := float64(-2); r <= 2.1; r += 0.4 {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.LessEq, &objects.Float{Value: r}, &objects.Bool{Value: l <= r})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.LessEq, &objects.Float{Value: r}, boolValue(l <= r))
|
||||
}
|
||||
}
|
||||
|
||||
// float >= float
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := float64(-2); r <= 2.1; r += 0.4 {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.GreaterEq, &objects.Float{Value: r}, &objects.Bool{Value: l >= r})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.GreaterEq, &objects.Float{Value: r}, boolValue(l >= r))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,28 +99,28 @@ func TestFloat_BinaryOp(t *testing.T) {
|
|||
// float < int
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Less, &objects.Int{Value: r}, &objects.Bool{Value: l < float64(r)})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Less, &objects.Int{Value: r}, boolValue(l < float64(r)))
|
||||
}
|
||||
}
|
||||
|
||||
// float > int
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Greater, &objects.Int{Value: r}, &objects.Bool{Value: l > float64(r)})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.Greater, &objects.Int{Value: r}, boolValue(l > float64(r)))
|
||||
}
|
||||
}
|
||||
|
||||
// float <= int
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.LessEq, &objects.Int{Value: r}, &objects.Bool{Value: l <= float64(r)})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.LessEq, &objects.Int{Value: r}, boolValue(l <= float64(r)))
|
||||
}
|
||||
}
|
||||
|
||||
// float >= int
|
||||
for l := float64(-2); l <= 2.1; l += 0.4 {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.GreaterEq, &objects.Int{Value: r}, &objects.Bool{Value: l >= float64(r)})
|
||||
testBinaryOp(t, &objects.Float{Value: l}, token.GreaterEq, &objects.Int{Value: r}, boolValue(l >= float64(r)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,28 +114,28 @@ func TestInt_BinaryOp(t *testing.T) {
|
|||
// int < int
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Less, &objects.Int{Value: r}, &objects.Bool{Value: l < r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Less, &objects.Int{Value: r}, boolValue(l < r))
|
||||
}
|
||||
}
|
||||
|
||||
// int > int
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Greater, &objects.Int{Value: r}, &objects.Bool{Value: l > r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Greater, &objects.Int{Value: r}, boolValue(l > r))
|
||||
}
|
||||
}
|
||||
|
||||
// int <= int
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.LessEq, &objects.Int{Value: r}, &objects.Bool{Value: l <= r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.LessEq, &objects.Int{Value: r}, boolValue(l <= r))
|
||||
}
|
||||
}
|
||||
|
||||
// int >= int
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := int64(-2); r <= 2; r++ {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.GreaterEq, &objects.Int{Value: r}, &objects.Bool{Value: l >= r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.GreaterEq, &objects.Int{Value: r}, boolValue(l >= r))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,28 +172,28 @@ func TestInt_BinaryOp(t *testing.T) {
|
|||
// int < float
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := float64(-2); r <= 2.1; r += 0.5 {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Less, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) < r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Less, &objects.Float{Value: r}, boolValue(float64(l) < r))
|
||||
}
|
||||
}
|
||||
|
||||
// int > float
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := float64(-2); r <= 2.1; r += 0.5 {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Greater, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) > r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.Greater, &objects.Float{Value: r}, boolValue(float64(l) > r))
|
||||
}
|
||||
}
|
||||
|
||||
// int <= float
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := float64(-2); r <= 2.1; r += 0.5 {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.LessEq, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) <= r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.LessEq, &objects.Float{Value: r}, boolValue(float64(l) <= r))
|
||||
}
|
||||
}
|
||||
|
||||
// int >= float
|
||||
for l := int64(-2); l <= 2; l++ {
|
||||
for r := float64(-2); r <= 2.1; r += 0.5 {
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.GreaterEq, &objects.Float{Value: r}, &objects.Bool{Value: float64(l) >= r})
|
||||
testBinaryOp(t, &objects.Int{Value: l}, token.GreaterEq, &objects.Float{Value: r}, boolValue(float64(l) >= r))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ package objects
|
|||
|
||||
var (
|
||||
// TrueValue represents a true value.
|
||||
TrueValue Object = &Bool{Value: true}
|
||||
TrueValue Object = &Bool{value: true}
|
||||
|
||||
// FalseValue represents a false value.
|
||||
FalseValue Object = &Bool{Value: false}
|
||||
FalseValue Object = &Bool{value: false}
|
||||
|
||||
// UndefinedValue represents an undefined value.
|
||||
UndefinedValue Object = &Undefined{}
|
||||
|
|
|
@ -15,3 +15,11 @@ func testBinaryOp(t *testing.T, lhs objects.Object, op token.Token, rhs objects.
|
|||
|
||||
return assert.NoError(t, err) && assert.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
func boolValue(b bool) objects.Object {
|
||||
if b {
|
||||
return objects.TrueValue
|
||||
}
|
||||
|
||||
return objects.FalseValue
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ func (o *Undefined) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Undefined) Copy() Object {
|
||||
return &Undefined{}
|
||||
return o
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
|
@ -33,7 +33,5 @@ func (o *Undefined) IsFalsy() bool {
|
|||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Undefined) Equals(x Object) bool {
|
||||
_, ok := x.(*Undefined)
|
||||
|
||||
return ok
|
||||
return o == x
|
||||
}
|
||||
|
|
|
@ -617,23 +617,21 @@ func (v *VM) Run() error {
|
|||
left := v.stack[v.sp-3]
|
||||
v.sp -= 3
|
||||
|
||||
var lowIdx, highIdx int64
|
||||
|
||||
switch low := (*low).(type) {
|
||||
case *objects.Undefined:
|
||||
//lowIdx = 0
|
||||
case *objects.Int:
|
||||
lowIdx = low.Value
|
||||
default:
|
||||
return fmt.Errorf("non-integer slice index: %s", low.TypeName())
|
||||
var lowIdx int64
|
||||
if *low != objects.UndefinedValue {
|
||||
if low, ok := (*low).(*objects.Int); ok {
|
||||
lowIdx = low.Value
|
||||
} else {
|
||||
return fmt.Errorf("non-integer slice index: %s", low.TypeName())
|
||||
}
|
||||
}
|
||||
|
||||
switch high := (*high).(type) {
|
||||
case *objects.Undefined:
|
||||
highIdx = -1 // will be replaced by number of elements
|
||||
case *objects.Int:
|
||||
var highIdx int64
|
||||
if *high == objects.UndefinedValue {
|
||||
highIdx = -1
|
||||
} else if high, ok := (*high).(*objects.Int); ok {
|
||||
highIdx = high.Value
|
||||
default:
|
||||
} else {
|
||||
return fmt.Errorf("non-integer slice index: %s", high.TypeName())
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestBuiltinFunction(t *testing.T) {
|
||||
|
@ -24,14 +26,14 @@ func TestBuiltinFunction(t *testing.T) {
|
|||
expect(t, `out = int(true)`, 1)
|
||||
expect(t, `out = int(false)`, 0)
|
||||
expect(t, `out = int('8')`, 56)
|
||||
expect(t, `out = int([1])`, undefined())
|
||||
expect(t, `out = int({a: 1})`, undefined())
|
||||
expect(t, `out = int(undefined)`, undefined())
|
||||
expect(t, `out = int([1])`, objects.UndefinedValue)
|
||||
expect(t, `out = int({a: 1})`, objects.UndefinedValue)
|
||||
expect(t, `out = int(undefined)`, objects.UndefinedValue)
|
||||
expect(t, `out = int("-522", 1)`, -522)
|
||||
expect(t, `out = int(undefined, 1)`, 1)
|
||||
expect(t, `out = int(undefined, 1.8)`, 1.8)
|
||||
expect(t, `out = int(undefined, string(1))`, "1")
|
||||
expect(t, `out = int(undefined, undefined)`, undefined())
|
||||
expect(t, `out = int(undefined, undefined)`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `out = string(1)`, "1")
|
||||
expect(t, `out = string(1.8)`, "1.8")
|
||||
|
@ -41,40 +43,40 @@ func TestBuiltinFunction(t *testing.T) {
|
|||
expect(t, `out = string('8')`, "8")
|
||||
expect(t, `out = string([1,8.1,true,3])`, "[1, 8.1, true, 3]")
|
||||
expect(t, `out = string({b: "foo"})`, `{b: "foo"}`)
|
||||
expect(t, `out = string(undefined)`, undefined()) // not "undefined"
|
||||
expect(t, `out = string(undefined)`, objects.UndefinedValue) // not "undefined"
|
||||
expect(t, `out = string(1, "-522")`, "1")
|
||||
expect(t, `out = string(undefined, "-522")`, "-522") // not "undefined"
|
||||
|
||||
expect(t, `out = float(1)`, 1.0)
|
||||
expect(t, `out = float(1.8)`, 1.8)
|
||||
expect(t, `out = float("-52.2")`, -52.2)
|
||||
expect(t, `out = float(true)`, undefined())
|
||||
expect(t, `out = float(false)`, undefined())
|
||||
expect(t, `out = float('8')`, undefined())
|
||||
expect(t, `out = float([1,8.1,true,3])`, undefined())
|
||||
expect(t, `out = float({a: 1, b: "foo"})`, undefined())
|
||||
expect(t, `out = float(undefined)`, undefined())
|
||||
expect(t, `out = float(true)`, objects.UndefinedValue)
|
||||
expect(t, `out = float(false)`, objects.UndefinedValue)
|
||||
expect(t, `out = float('8')`, objects.UndefinedValue)
|
||||
expect(t, `out = float([1,8.1,true,3])`, objects.UndefinedValue)
|
||||
expect(t, `out = float({a: 1, b: "foo"})`, objects.UndefinedValue)
|
||||
expect(t, `out = float(undefined)`, objects.UndefinedValue)
|
||||
expect(t, `out = float("-52.2", 1.8)`, -52.2)
|
||||
expect(t, `out = float(undefined, 1)`, 1)
|
||||
expect(t, `out = float(undefined, 1.8)`, 1.8)
|
||||
expect(t, `out = float(undefined, "-52.2")`, "-52.2")
|
||||
expect(t, `out = float(undefined, char(56))`, '8')
|
||||
expect(t, `out = float(undefined, undefined)`, undefined())
|
||||
expect(t, `out = float(undefined, undefined)`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `out = char(56)`, '8')
|
||||
expect(t, `out = char(1.8)`, undefined())
|
||||
expect(t, `out = char("-52.2")`, undefined())
|
||||
expect(t, `out = char(true)`, undefined())
|
||||
expect(t, `out = char(false)`, undefined())
|
||||
expect(t, `out = char(1.8)`, objects.UndefinedValue)
|
||||
expect(t, `out = char("-52.2")`, objects.UndefinedValue)
|
||||
expect(t, `out = char(true)`, objects.UndefinedValue)
|
||||
expect(t, `out = char(false)`, objects.UndefinedValue)
|
||||
expect(t, `out = char('8')`, '8')
|
||||
expect(t, `out = char([1,8.1,true,3])`, undefined())
|
||||
expect(t, `out = char({a: 1, b: "foo"})`, undefined())
|
||||
expect(t, `out = char(undefined)`, undefined())
|
||||
expect(t, `out = char([1,8.1,true,3])`, objects.UndefinedValue)
|
||||
expect(t, `out = char({a: 1, b: "foo"})`, objects.UndefinedValue)
|
||||
expect(t, `out = char(undefined)`, objects.UndefinedValue)
|
||||
expect(t, `out = char(56, 'a')`, '8')
|
||||
expect(t, `out = char(undefined, '8')`, '8')
|
||||
expect(t, `out = char(undefined, 56)`, 56)
|
||||
expect(t, `out = char(undefined, "-52.2")`, "-52.2")
|
||||
expect(t, `out = char(undefined, undefined)`, undefined())
|
||||
expect(t, `out = char(undefined, undefined)`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `out = bool(1)`, true) // non-zero integer: true
|
||||
expect(t, `out = bool(0)`, false) // zero: true
|
||||
|
@ -93,20 +95,20 @@ func TestBuiltinFunction(t *testing.T) {
|
|||
expect(t, `out = bool(undefined)`, false) // undefined: false
|
||||
|
||||
expect(t, `out = bytes(1)`, []byte{0})
|
||||
expect(t, `out = bytes(1.8)`, undefined())
|
||||
expect(t, `out = bytes(1.8)`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes("-522")`, []byte{'-', '5', '2', '2'})
|
||||
expect(t, `out = bytes(true)`, undefined())
|
||||
expect(t, `out = bytes(false)`, undefined())
|
||||
expect(t, `out = bytes('8')`, undefined())
|
||||
expect(t, `out = bytes([1])`, undefined())
|
||||
expect(t, `out = bytes({a: 1})`, undefined())
|
||||
expect(t, `out = bytes(undefined)`, undefined())
|
||||
expect(t, `out = bytes(true)`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes(false)`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes('8')`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes([1])`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes({a: 1})`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes(undefined)`, objects.UndefinedValue)
|
||||
expect(t, `out = bytes("-522", ['8'])`, []byte{'-', '5', '2', '2'})
|
||||
expect(t, `out = bytes(undefined, "-522")`, "-522")
|
||||
expect(t, `out = bytes(undefined, 1)`, 1)
|
||||
expect(t, `out = bytes(undefined, 1.8)`, 1.8)
|
||||
expect(t, `out = bytes(undefined, int("-522"))`, -522)
|
||||
expect(t, `out = bytes(undefined, undefined)`, undefined())
|
||||
expect(t, `out = bytes(undefined, undefined)`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `out = is_error(error(1))`, true)
|
||||
expect(t, `out = is_error(1)`, false)
|
||||
|
|
|
@ -2,13 +2,15 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestFunction(t *testing.T) {
|
||||
// function with no "return" statement returns "invalid" value.
|
||||
expect(t, `f1 := func() {}; out = f1();`, undefined())
|
||||
expect(t, `f1 := func() {}; f2 := func() { return f1(); }; f1(); out = f2();`, undefined())
|
||||
expect(t, `f := func(x) { x; }; out = f(5);`, undefined())
|
||||
expect(t, `f1 := func() {}; out = f1();`, objects.UndefinedValue)
|
||||
expect(t, `f1 := func() {}; f2 := func() { return f1(); }; f1(); out = f2();`, objects.UndefinedValue)
|
||||
expect(t, `f := func(x) { x; }; out = f(5);`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `f := func(x) { return x; }; out = f(5);`, 5)
|
||||
expect(t, `f := func(x) { return x * 2; }; out = f(5);`, 10)
|
||||
|
|
|
@ -2,16 +2,18 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestIf(t *testing.T) {
|
||||
expect(t, `if (true) { out = 10 }`, 10)
|
||||
expect(t, `if (false) { out = 10 }`, undefined())
|
||||
expect(t, `if (false) { out = 10 }`, objects.UndefinedValue)
|
||||
expect(t, `if (false) { out = 10 } else { out = 20 }`, 20)
|
||||
expect(t, `if (1) { out = 10 }`, 10)
|
||||
expect(t, `if (0) { out = 10 } else { out = 20 }`, 20)
|
||||
expect(t, `if (1 < 2) { out = 10 }`, 10)
|
||||
expect(t, `if (1 > 2) { out = 10 }`, undefined())
|
||||
expect(t, `if (1 > 2) { out = 10 }`, objects.UndefinedValue)
|
||||
expect(t, `if (1 < 2) { out = 10 } else { out = 20 }`, 10)
|
||||
expect(t, `if (1 > 2) { out = 10 } else { out = 20 }`, 20)
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ func TestIndexable(t *testing.T) {
|
|||
dict := func() *StringDict { return &StringDict{Value: map[string]string{"a": "foo", "b": "bar"}} }
|
||||
expectWithSymbols(t, `out = dict["a"]`, "foo", SYM{"dict": dict()})
|
||||
expectWithSymbols(t, `out = dict["B"]`, "bar", SYM{"dict": dict()})
|
||||
expectWithSymbols(t, `out = dict["x"]`, undefined(), SYM{"dict": dict()})
|
||||
expectWithSymbols(t, `out = dict["x"]`, objects.UndefinedValue, SYM{"dict": dict()})
|
||||
expectErrorWithSymbols(t, `out = dict[0]`, SYM{"dict": dict()})
|
||||
|
||||
strCir := func() *StringCircle { return &StringCircle{Value: []string{"one", "two", "three"}} }
|
||||
|
@ -173,7 +173,7 @@ func TestIndexable(t *testing.T) {
|
|||
strArr := func() *StringArray { return &StringArray{Value: []string{"one", "two", "three"}} }
|
||||
expectWithSymbols(t, `out = arr["one"]`, 0, SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr["three"]`, 2, SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr["four"]`, undefined(), SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr["four"]`, objects.UndefinedValue, SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr[0]`, "one", SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr[1]`, "two", SYM{"arr": strArr()})
|
||||
expectErrorWithSymbols(t, `out = arr[-1]`, SYM{"arr": strArr()})
|
||||
|
|
|
@ -2,6 +2,8 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
|
@ -17,9 +19,9 @@ out = {
|
|||
})
|
||||
|
||||
expect(t, `out = {foo: 5}["foo"]`, 5)
|
||||
expect(t, `out = {foo: 5}["bar"]`, undefined())
|
||||
expect(t, `out = {foo: 5}["bar"]`, objects.UndefinedValue)
|
||||
expect(t, `key := "foo"; out = {foo: 5}[key]`, 5)
|
||||
expect(t, `out = {}["foo"]`, undefined())
|
||||
expect(t, `out = {}["foo"]`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `
|
||||
m := {
|
||||
|
|
|
@ -2,12 +2,14 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestSelector(t *testing.T) {
|
||||
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k1`, 5)
|
||||
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k2`, "foo")
|
||||
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k3`, undefined())
|
||||
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k3`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `
|
||||
a := {
|
||||
|
|
|
@ -133,7 +133,10 @@ func toObject(v interface{}) objects.Object {
|
|||
case int: // for convenience
|
||||
return &objects.Int{Value: int64(v)}
|
||||
case bool:
|
||||
return &objects.Bool{Value: v}
|
||||
if v {
|
||||
return objects.TrueValue
|
||||
}
|
||||
return objects.FalseValue
|
||||
case rune:
|
||||
return &objects.Char{Value: v}
|
||||
case byte: // for convenience
|
||||
|
@ -326,7 +329,7 @@ func objectZeroCopy(o objects.Object) objects.Object {
|
|||
case *objects.Map:
|
||||
return &objects.Map{}
|
||||
case *objects.Undefined:
|
||||
return &objects.Undefined{}
|
||||
return objects.UndefinedValue
|
||||
case *objects.Error:
|
||||
return &objects.Error{}
|
||||
case *objects.Bytes:
|
||||
|
@ -341,7 +344,3 @@ func objectZeroCopy(o objects.Object) objects.Object {
|
|||
panic(fmt.Errorf("unknown object type: %s", o.TypeName()))
|
||||
}
|
||||
}
|
||||
|
||||
func undefined() *objects.Undefined {
|
||||
return &objects.Undefined{}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package runtime_test
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
func TestUndefined(t *testing.T) {
|
||||
expect(t, `out = undefined`, undefined())
|
||||
expect(t, `out = undefined`, objects.UndefinedValue)
|
||||
expect(t, `out = undefined == undefined`, true)
|
||||
expect(t, `out = undefined == 1`, false)
|
||||
expect(t, `out = 1 == undefined`, false)
|
||||
|
|
|
@ -8,8 +8,6 @@ import (
|
|||
"github.com/d5/tengo/runtime"
|
||||
)
|
||||
|
||||
var undefined objects.Object = &objects.Undefined{}
|
||||
|
||||
// Compiled is a compiled instance of the user script.
|
||||
// Use Script.Compile() to create Compiled object.
|
||||
type Compiled struct {
|
||||
|
@ -53,20 +51,18 @@ func (c *Compiled) IsDefined(name string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
_, isUndefined := (*v).(*objects.Undefined)
|
||||
|
||||
return !isUndefined
|
||||
return *v != objects.UndefinedValue
|
||||
}
|
||||
|
||||
// Get returns a variable identified by the name.
|
||||
func (c *Compiled) Get(name string) *Variable {
|
||||
value := &undefined
|
||||
value := &objects.UndefinedValue
|
||||
|
||||
symbol, _, ok := c.symbolTable.Resolve(name)
|
||||
if ok && symbol.Scope == compiler.ScopeGlobal {
|
||||
value = c.machine.Globals()[symbol.Index]
|
||||
if value == nil {
|
||||
value = &undefined
|
||||
value = &objects.UndefinedValue
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +80,7 @@ func (c *Compiled) GetAll() []*Variable {
|
|||
if ok && symbol.Scope == compiler.ScopeGlobal {
|
||||
value := c.machine.Globals()[symbol.Index]
|
||||
if value == nil {
|
||||
value = &undefined
|
||||
value = &objects.UndefinedValue
|
||||
}
|
||||
|
||||
vars = append(vars, &Variable{
|
||||
|
|
|
@ -15,7 +15,10 @@ func objectToInterface(o objects.Object) interface{} {
|
|||
case *objects.Float:
|
||||
return val.Value
|
||||
case *objects.Bool:
|
||||
return val.Value
|
||||
if val == objects.TrueValue {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case *objects.Char:
|
||||
return val.Value
|
||||
case *objects.String:
|
||||
|
|
|
@ -145,7 +145,5 @@ func (v *Variable) Object() objects.Object {
|
|||
|
||||
// IsUndefined returns true if the underlying value is undefined.
|
||||
func (v *Variable) IsUndefined() bool {
|
||||
_, isUndefined := (*v.value).(*objects.Undefined)
|
||||
|
||||
return isUndefined
|
||||
return *v.value == objects.UndefinedValue
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ func TestVariable(t *testing.T) {
|
|||
FloatValue: 0,
|
||||
BoolValue: true,
|
||||
StringValue: "true",
|
||||
Object: &objects.Bool{Value: true},
|
||||
Object: objects.TrueValue,
|
||||
},
|
||||
{
|
||||
Name: "d",
|
||||
|
|
Loading…
Reference in a new issue