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