mirror of
https://github.com/MontFerret/ferret.git
synced 2025-08-15 20:02:56 +02:00
Fixed sorting
This commit is contained in:
@@ -1334,7 +1334,7 @@ func (v *visitor) emitLoopEnd(loop *Loop) vm.Operand {
|
||||
if loop.Allocate {
|
||||
// TODO: Reuse the dsReg register
|
||||
v.emitter.EmitA(vm.OpClose, loop.Iterator)
|
||||
v.emitter.EmitAB(vm.OpDataSetToList, dst, loop.Result)
|
||||
v.emitter.EmitAB(vm.OpMove, dst, loop.Result)
|
||||
|
||||
if loop.Kind == ForLoop {
|
||||
v.emitter.PatchJump(loop.Jump)
|
||||
|
@@ -12,27 +12,27 @@ type Array struct {
|
||||
data []Value
|
||||
}
|
||||
|
||||
func EmptyArray() List {
|
||||
func EmptyArray() *Array {
|
||||
return &Array{data: make([]Value, 0)}
|
||||
}
|
||||
|
||||
func NewArray(cap int) List {
|
||||
func NewArray(cap int) *Array {
|
||||
return &Array{data: make([]Value, 0, cap)}
|
||||
}
|
||||
|
||||
func NewArray64(cap Int) List {
|
||||
func NewArray64(cap Int) *Array {
|
||||
return &Array{data: make([]Value, 0, cap)}
|
||||
}
|
||||
|
||||
func NewSizedArray(size int) List {
|
||||
func NewSizedArray(size int) *Array {
|
||||
return &Array{data: make([]Value, size)}
|
||||
}
|
||||
|
||||
func NewArrayWith(values ...Value) List {
|
||||
func NewArrayWith(values ...Value) *Array {
|
||||
return &Array{data: values}
|
||||
}
|
||||
|
||||
func NewArrayOf(values []Value) List {
|
||||
func NewArrayOf(values []Value) *Array {
|
||||
return &Array{data: values}
|
||||
}
|
||||
|
||||
@@ -128,8 +128,8 @@ func (t *Array) Copy() Value {
|
||||
return &Array{data: t.copyInternal(0)}
|
||||
}
|
||||
|
||||
func (t *Array) CopyWithCap(_ context.Context, cap Int) (List, error) {
|
||||
return &Array{data: t.copyInternal(cap)}, nil
|
||||
func (t *Array) CopyWithGrowth(cap Int) *Array {
|
||||
return &Array{data: t.copyInternal(cap)}
|
||||
}
|
||||
|
||||
func (t *Array) copyInternal(cap Int) []Value {
|
||||
|
@@ -24,7 +24,7 @@ func TestArrayIterator(t *testing.T) {
|
||||
Convey("One value", t, func() {
|
||||
ctx := context.Background()
|
||||
arr := runtime.NewArray(1)
|
||||
arr.Add(ctx, NewInt(1))
|
||||
arr.Add(ctx, runtime.NewInt(1))
|
||||
iter := runtime.NewArrayIterator(arr)
|
||||
|
||||
hasNext, err := iter.HasNext(ctx)
|
||||
@@ -35,8 +35,8 @@ func TestArrayIterator(t *testing.T) {
|
||||
val, key, err := iter.Next(ctx)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(val, ShouldEqual, NewInt(1))
|
||||
So(key, ShouldEqual, NewInt(0))
|
||||
So(val, ShouldEqual, runtime.NewInt(1))
|
||||
So(key, ShouldEqual, runtime.NewInt(0))
|
||||
|
||||
hasNext, err = iter.HasNext(ctx)
|
||||
|
||||
@@ -47,14 +47,14 @@ func TestArrayIterator(t *testing.T) {
|
||||
Convey("Multiple values", t, func() {
|
||||
ctx := context.Background()
|
||||
arr := runtime.NewArray(5)
|
||||
arr.Push(NewInt(1))
|
||||
arr.Push(NewInt(2))
|
||||
arr.Push(NewInt(3))
|
||||
arr.Push(NewInt(4))
|
||||
arr.Push(NewInt(5))
|
||||
arr.Add(ctx, runtime.NewInt(1))
|
||||
arr.Add(ctx, runtime.NewInt(2))
|
||||
arr.Add(ctx, runtime.NewInt(3))
|
||||
arr.Add(ctx, runtime.NewInt(4))
|
||||
arr.Add(ctx, runtime.NewInt(5))
|
||||
iter := runtime.NewArrayIterator(arr)
|
||||
|
||||
actual := make([]Int, 0, 5)
|
||||
actual := make([]runtime.Int, 0, 5)
|
||||
|
||||
for {
|
||||
hasNext, err := iter.HasNext(ctx)
|
||||
@@ -62,23 +62,22 @@ func TestArrayIterator(t *testing.T) {
|
||||
break
|
||||
}
|
||||
val, _, err := iter.Next(ctx)
|
||||
actual = append(actual, val.(Int))
|
||||
actual = append(actual, val.(runtime.Int))
|
||||
}
|
||||
|
||||
So(actual, ShouldResemble, []Int{1, 2, 3, 4, 5})
|
||||
So(actual, ShouldResemble, []runtime.Int{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkArrayIterator(b *testing.B) {
|
||||
size := 100
|
||||
arr := runtime.NewArray(size)
|
||||
ctx := context.Background()
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
arr.Push(NewInt(i))
|
||||
arr.Add(ctx, runtime.NewInt(i))
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
@@ -4,27 +4,32 @@ import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
c "context"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestArray(t *testing.T) {
|
||||
ctx := c.Background()
|
||||
Convey("#constructor", t, func() {
|
||||
Convey("Should create an empty array", func() {
|
||||
arr := runtime.NewArray(10)
|
||||
size, _ := arr.Length(ctx)
|
||||
|
||||
So(arr.Length(), ShouldEqual, 0)
|
||||
So(size, ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should create an array, from passed values", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
NewInt(1),
|
||||
NewInt(2),
|
||||
NewInt(3),
|
||||
runtime.NewInt(1),
|
||||
runtime.NewInt(2),
|
||||
runtime.NewInt(3),
|
||||
)
|
||||
size, _ := arr.Length(ctx)
|
||||
|
||||
So(arr.Length(), ShouldEqual, 3)
|
||||
So(size, ShouldEqual, 3)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,9 +45,9 @@ func TestArray(t *testing.T) {
|
||||
|
||||
Convey("Should serialize full array", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
NewInt(1),
|
||||
NewInt(2),
|
||||
NewInt(3),
|
||||
runtime.NewInt(1),
|
||||
runtime.NewInt(2),
|
||||
runtime.NewInt(3),
|
||||
)
|
||||
marshaled, err := json.Marshal(arr)
|
||||
|
||||
@@ -55,8 +60,8 @@ func TestArray(t *testing.T) {
|
||||
Convey(".Unwrap", t, func() {
|
||||
Convey("Should return a an array of unwrapped values", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
ZeroInt,
|
||||
ZeroInt,
|
||||
runtime.ZeroInt,
|
||||
runtime.ZeroInt,
|
||||
)
|
||||
|
||||
for _, val := range arr.Unwrap().([]interface{}) {
|
||||
@@ -67,24 +72,24 @@ func TestArray(t *testing.T) {
|
||||
|
||||
Convey(".String", t, func() {
|
||||
Convey("Should return a string representation ", func() {
|
||||
arr := runtime.NewArrayWith(ZeroInt, ZeroInt)
|
||||
arr := runtime.NewArrayWith(runtime.ZeroInt, runtime.ZeroInt)
|
||||
|
||||
So(arr.String(), ShouldEqual, "[0,0]")
|
||||
})
|
||||
})
|
||||
|
||||
Convey(".CompareValues", t, func() {
|
||||
Convey(".Compareruntime.Values", t, func() {
|
||||
Convey("It should return 1 for all non-array and non-object values", func() {
|
||||
arr := runtime.NewArrayWith(ZeroInt, ZeroInt)
|
||||
arr := runtime.NewArrayWith(runtime.ZeroInt, runtime.ZeroInt)
|
||||
|
||||
So(arr.Compare(None), ShouldEqual, 1)
|
||||
So(arr.Compare(ZeroInt), ShouldEqual, 1)
|
||||
So(arr.Compare(ZeroFloat), ShouldEqual, 1)
|
||||
So(arr.Compare(EmptyString), ShouldEqual, 1)
|
||||
So(arr.Compare(runtime.None), ShouldEqual, 1)
|
||||
So(arr.Compare(runtime.ZeroInt), ShouldEqual, 1)
|
||||
So(arr.Compare(runtime.ZeroFloat), ShouldEqual, 1)
|
||||
So(arr.Compare(runtime.EmptyString), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("It should return -1 for all object values", func() {
|
||||
arr := runtime.NewArrayWith(ZeroInt, ZeroInt)
|
||||
arr := runtime.NewArrayWith(runtime.ZeroInt, runtime.ZeroInt)
|
||||
obj := runtime.NewObject()
|
||||
|
||||
So(arr.Compare(obj), ShouldEqual, -1)
|
||||
@@ -98,15 +103,15 @@ func TestArray(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("It should return 1 when other array is empty", func() {
|
||||
arr1 := runtime.NewArrayWith(ZeroFloat)
|
||||
arr1 := runtime.NewArrayWith(runtime.ZeroFloat)
|
||||
arr2 := runtime.NewArray(1)
|
||||
|
||||
So(arr1.Compare(arr2), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("It should return 1 when values are bigger", func() {
|
||||
arr1 := runtime.NewArrayWith(NewInt(1))
|
||||
arr2 := runtime.NewArrayWith(ZeroInt)
|
||||
arr1 := runtime.NewArrayWith(runtime.NewInt(1))
|
||||
arr2 := runtime.NewArrayWith(runtime.ZeroInt)
|
||||
|
||||
So(arr1.Compare(arr2), ShouldEqual, 1)
|
||||
})
|
||||
@@ -114,10 +119,10 @@ func TestArray(t *testing.T) {
|
||||
Convey("It should return 0 when arrays are equal", func() {
|
||||
Convey("When only simple types are nested", func() {
|
||||
arr1 := runtime.NewArrayWith(
|
||||
NewInt(0), NewString("str"),
|
||||
runtime.NewInt(0), runtime.NewString("str"),
|
||||
)
|
||||
arr2 := runtime.NewArrayWith(
|
||||
NewInt(0), NewString("str"),
|
||||
runtime.NewInt(0), runtime.NewString("str"),
|
||||
)
|
||||
|
||||
So(arr1.Compare(arr2), ShouldEqual, 0)
|
||||
@@ -126,18 +131,18 @@ func TestArray(t *testing.T) {
|
||||
Convey("When object and array are nested at the same time", func() {
|
||||
arr1 := runtime.NewArrayWith(
|
||||
runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("one", NewInt(1)),
|
||||
runtime.NewObjectProperty("one", runtime.NewInt(1)),
|
||||
),
|
||||
runtime.NewArrayWith(
|
||||
NewInt(2),
|
||||
runtime.NewInt(2),
|
||||
),
|
||||
)
|
||||
arr2 := runtime.NewArrayWith(
|
||||
runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("one", NewInt(1)),
|
||||
runtime.NewObjectProperty("one", runtime.NewInt(1)),
|
||||
),
|
||||
runtime.NewArrayWith(
|
||||
NewInt(2),
|
||||
runtime.NewInt(2),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -147,12 +152,12 @@ func TestArray(t *testing.T) {
|
||||
Convey("When only objects are nested", func() {
|
||||
arr1 := runtime.NewArrayWith(
|
||||
runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("one", NewInt(1)),
|
||||
runtime.NewObjectProperty("one", runtime.NewInt(1)),
|
||||
),
|
||||
)
|
||||
arr2 := runtime.NewArrayWith(
|
||||
runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("one", NewInt(1)),
|
||||
runtime.NewObjectProperty("one", runtime.NewInt(1)),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -162,12 +167,12 @@ func TestArray(t *testing.T) {
|
||||
Convey("When only arrays are nested", func() {
|
||||
arr1 := runtime.NewArrayWith(
|
||||
runtime.NewArrayWith(
|
||||
NewInt(2),
|
||||
runtime.NewInt(2),
|
||||
),
|
||||
)
|
||||
arr2 := runtime.NewArrayWith(
|
||||
runtime.NewArrayWith(
|
||||
NewInt(2),
|
||||
runtime.NewInt(2),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -176,21 +181,21 @@ func TestArray(t *testing.T) {
|
||||
|
||||
Convey("When simple and complex types at the same time", func() {
|
||||
arr1 := runtime.NewArrayWith(
|
||||
NewInt(0),
|
||||
runtime.NewInt(0),
|
||||
runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("one", NewInt(1)),
|
||||
runtime.NewObjectProperty("one", runtime.NewInt(1)),
|
||||
),
|
||||
runtime.NewArrayWith(
|
||||
NewInt(2),
|
||||
runtime.NewInt(2),
|
||||
),
|
||||
)
|
||||
arr2 := runtime.NewArrayWith(
|
||||
NewInt(0),
|
||||
runtime.NewInt(0),
|
||||
runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("one", NewInt(1)),
|
||||
runtime.NewObjectProperty("one", runtime.NewInt(1)),
|
||||
),
|
||||
runtime.NewArrayWith(
|
||||
NewInt(2),
|
||||
runtime.NewInt(2),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -221,9 +226,9 @@ func TestArray(t *testing.T) {
|
||||
Convey(".Hash", t, func() {
|
||||
Convey("It should calculate hash of non-empty array", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
NewInt(1),
|
||||
NewInt(2),
|
||||
NewInt(3),
|
||||
runtime.NewInt(1),
|
||||
runtime.NewInt(2),
|
||||
runtime.NewInt(3),
|
||||
)
|
||||
|
||||
h := arr.Hash()
|
||||
@@ -241,13 +246,13 @@ func TestArray(t *testing.T) {
|
||||
|
||||
Convey("Hash sum should be consistent", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
True,
|
||||
NewInt(1),
|
||||
NewFloat(1.1),
|
||||
NewString("foobar"),
|
||||
NewCurrentDateTime(),
|
||||
runtime.NewArrayWith(NewInt(1), True),
|
||||
runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewString("bar"))),
|
||||
runtime.True,
|
||||
runtime.NewInt(1),
|
||||
runtime.NewFloat(1.1),
|
||||
runtime.NewString("foobar"),
|
||||
runtime.NewCurrentDateTime(),
|
||||
runtime.NewArrayWith(runtime.NewInt(1), runtime.True),
|
||||
runtime.NewObjectWith(runtime.NewObjectProperty("foo", runtime.NewString("bar"))),
|
||||
)
|
||||
|
||||
h1 := arr.Hash()
|
||||
@@ -261,62 +266,70 @@ func TestArray(t *testing.T) {
|
||||
Convey("Should return 0 when empty", func() {
|
||||
arr := runtime.NewArray(1)
|
||||
|
||||
So(arr.Length(), ShouldEqual, 0)
|
||||
size, _ := arr.Length(ctx)
|
||||
|
||||
So(size, ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should return greater than 0 when not empty", func() {
|
||||
arr := runtime.NewArrayWith(ZeroInt, ZeroInt)
|
||||
arr := runtime.NewArrayWith(runtime.ZeroInt, runtime.ZeroInt)
|
||||
|
||||
So(arr.Length(), ShouldEqual, 2)
|
||||
size, _ := arr.Length(ctx)
|
||||
|
||||
So(size, ShouldEqual, 2)
|
||||
})
|
||||
})
|
||||
|
||||
Convey(".ForEach", t, func() {
|
||||
Convey(".ForEachIter", t, func() {
|
||||
Convey("Should iterate over elements", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
NewInt(1),
|
||||
NewInt(2),
|
||||
NewInt(3),
|
||||
runtime.NewInt(1),
|
||||
runtime.NewInt(2),
|
||||
runtime.NewInt(3),
|
||||
)
|
||||
counter := 0
|
||||
|
||||
arr.ForEach(func(value Value, idx int) bool {
|
||||
_ = arr.ForEach(ctx, func(_ c.Context, val runtime.Value, idx runtime.Int) (runtime.Boolean, error) {
|
||||
counter++
|
||||
|
||||
return true
|
||||
return true, nil
|
||||
})
|
||||
|
||||
So(counter, ShouldEqual, arr.Length())
|
||||
size, _ := arr.Length(ctx)
|
||||
|
||||
So(counter, ShouldEqual, size)
|
||||
})
|
||||
|
||||
Convey("Should not iterate when empty", func() {
|
||||
arr := runtime.NewArrayWith()
|
||||
counter := 0
|
||||
|
||||
arr.ForEach(func(value Value, idx int) bool {
|
||||
_ = arr.ForEach(ctx, func(_ c.Context, val runtime.Value, idx runtime.Int) (runtime.Boolean, error) {
|
||||
counter++
|
||||
|
||||
return true
|
||||
return true, nil
|
||||
})
|
||||
|
||||
So(counter, ShouldEqual, arr.Length())
|
||||
size, _ := arr.Length(ctx)
|
||||
|
||||
So(counter, ShouldEqual, size)
|
||||
})
|
||||
|
||||
//Convey("Should break iteration when false returned", func() {
|
||||
// arr := values.NewArrayWith(
|
||||
// values.NewInt(1),
|
||||
// values.NewInt(2),
|
||||
// values.NewInt(3),
|
||||
// values.NewInt(4),
|
||||
// values.NewInt(5),
|
||||
// values.runtime.NewInt(1),
|
||||
// values.runtime.NewInt(2),
|
||||
// values.runtime.NewInt(3),
|
||||
// values.runtime.NewInt(4),
|
||||
// values.runtime.NewInt(5),
|
||||
// )
|
||||
// threshold := 3
|
||||
// counter := 0
|
||||
//
|
||||
// arr.ForEach(func(value core.Value, idx int) bool {
|
||||
// arr.ForEachIter(func(value core.runtime.Value, idx int) bool {
|
||||
// counter++
|
||||
//
|
||||
// return value.CompareValues(values.NewInt(threshold)) == -1
|
||||
// return value.Compareruntime.Values(values.runtime.NewInt(threshold)) == -1
|
||||
// })
|
||||
//
|
||||
// So(counter, ShouldEqual, threshold)
|
||||
@@ -326,32 +339,32 @@ func TestArray(t *testing.T) {
|
||||
//Convey(".Get", t, func() {
|
||||
// Convey("Should return item by index", func() {
|
||||
// arr := values.NewArrayWith(
|
||||
// values.NewInt(1),
|
||||
// values.NewInt(2),
|
||||
// values.NewInt(3),
|
||||
// values.NewInt(4),
|
||||
// values.NewInt(5),
|
||||
// values.runtime.NewInt(1),
|
||||
// values.runtime.NewInt(2),
|
||||
// values.runtime.NewInt(3),
|
||||
// values.runtime.NewInt(4),
|
||||
// values.runtime.NewInt(5),
|
||||
// )
|
||||
//
|
||||
// el := arr.Get(1)
|
||||
//
|
||||
// So(el.CompareValues(values.NewInt(2)), ShouldEqual, 0)
|
||||
// So(el.Compareruntime.Values(values.runtime.NewInt(2)), ShouldEqual, 0)
|
||||
// })
|
||||
//
|
||||
// Convey("Should return None when no items", func() {
|
||||
// Convey("Should return runtime.None when no items", func() {
|
||||
// arr := values.NewArrayWith()
|
||||
//
|
||||
// el := arr.Get(1)
|
||||
//
|
||||
// So(el.CompareValues(values.None), ShouldEqual, 0)
|
||||
// So(el.Compareruntime.Values(values.runtime.None), ShouldEqual, 0)
|
||||
// })
|
||||
//})
|
||||
|
||||
Convey(".Set", t, func() {
|
||||
//Convey("Should set item by index", func() {
|
||||
// arr := values.NewArrayWith(values.ZeroInt)
|
||||
// arr := values.NewArrayWith(values.runtime.ZeroInt)
|
||||
//
|
||||
// err := arr.Set(0, values.NewInt(1))
|
||||
// err := arr.Set(0, values.runtime.NewInt(1))
|
||||
//
|
||||
// So(err, ShouldBeNil)
|
||||
// So(arr.Length(), ShouldEqual, 1)
|
||||
@@ -361,10 +374,12 @@ func TestArray(t *testing.T) {
|
||||
Convey("Should return an error when index is out of bounds", func() {
|
||||
arr := runtime.NewArray(10)
|
||||
|
||||
err := arr.Set(0, NewInt(1))
|
||||
err := arr.Set(ctx, 0, runtime.NewInt(1))
|
||||
|
||||
So(err, ShouldNotBeNil)
|
||||
So(arr.Length(), ShouldEqual, 0)
|
||||
size, _ := arr.Length(ctx)
|
||||
|
||||
So(size, ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -372,184 +387,202 @@ func TestArray(t *testing.T) {
|
||||
Convey("Should add an item", func() {
|
||||
arr := runtime.NewArray(10)
|
||||
|
||||
src := []Value{
|
||||
ZeroInt,
|
||||
ZeroInt,
|
||||
ZeroInt,
|
||||
ZeroInt,
|
||||
ZeroInt,
|
||||
src := []runtime.Value{
|
||||
runtime.ZeroInt,
|
||||
runtime.ZeroInt,
|
||||
runtime.ZeroInt,
|
||||
runtime.ZeroInt,
|
||||
runtime.ZeroInt,
|
||||
}
|
||||
|
||||
for _, val := range src {
|
||||
arr.Push(val)
|
||||
arr.Add(ctx, val)
|
||||
}
|
||||
|
||||
So(arr.Length(), ShouldEqual, len(src))
|
||||
size, _ := arr.Length(ctx)
|
||||
|
||||
So(size, ShouldEqual, len(src))
|
||||
})
|
||||
})
|
||||
|
||||
//Convey(".Slice", t, func() {
|
||||
// Convey("Should return a slice", func() {
|
||||
// arr := values.NewArrayWith(
|
||||
// values.NewInt(0),
|
||||
// values.NewInt(1),
|
||||
// values.NewInt(2),
|
||||
// values.NewInt(3),
|
||||
// values.NewInt(4),
|
||||
// values.NewInt(5),
|
||||
// values.runtime.NewInt(0),
|
||||
// values.runtime.NewInt(1),
|
||||
// values.runtime.NewInt(2),
|
||||
// values.runtime.NewInt(3),
|
||||
// values.runtime.NewInt(4),
|
||||
// values.runtime.NewInt(5),
|
||||
// )
|
||||
//
|
||||
// s := arr.Slice(0, 1)
|
||||
//
|
||||
// So(s.Length(), ShouldEqual, 1)
|
||||
// So(s.Get(0).CompareValues(values.ZeroInt), ShouldEqual, 0)
|
||||
// So(s.Length(ctx), ShouldEqual, 1)
|
||||
// So(s.Get(0).Compareruntime.Values(values.runtime.ZeroInt), ShouldEqual, 0)
|
||||
//
|
||||
// s2 := arr.Slice(2, arr.Length())
|
||||
// s2 := arr.Slice(2, arr.Length(ctx))
|
||||
//
|
||||
// So(s2.Length(), ShouldEqual, arr.Length()-2)
|
||||
// So(s2.Length(ctx), ShouldEqual, arr.Length(ctx)-2)
|
||||
// })
|
||||
//})
|
||||
|
||||
Convey(".Insert", t, func() {
|
||||
Convey("Should insert an item in the middle of an array", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
NewInt(0),
|
||||
NewInt(1),
|
||||
NewInt(2),
|
||||
NewInt(3),
|
||||
NewInt(4),
|
||||
NewInt(5),
|
||||
runtime.NewInt(0),
|
||||
runtime.NewInt(1),
|
||||
runtime.NewInt(2),
|
||||
runtime.NewInt(3),
|
||||
runtime.NewInt(4),
|
||||
runtime.NewInt(5),
|
||||
)
|
||||
|
||||
lenBefore := arr.Length()
|
||||
lenBefore, _ := arr.Length(ctx)
|
||||
|
||||
arr.Insert(3, NewInt(100))
|
||||
arr.Insert(ctx, 3, runtime.NewInt(100))
|
||||
|
||||
lenAfter := arr.Length()
|
||||
lenAfter, _ := arr.Length(ctx)
|
||||
|
||||
act, _ := arr.Get(ctx, 3)
|
||||
|
||||
So(lenAfter, ShouldBeGreaterThan, lenBefore)
|
||||
So(arr.Get(3), ShouldEqual, 100)
|
||||
So(act, ShouldEqual, 100)
|
||||
})
|
||||
})
|
||||
|
||||
Convey(".RemoveAt", t, func() {
|
||||
Convey("Should remove an item from the middle", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
NewInt(0),
|
||||
NewInt(1),
|
||||
NewInt(2),
|
||||
NewInt(3),
|
||||
NewInt(4),
|
||||
NewInt(5),
|
||||
runtime.NewInt(0),
|
||||
runtime.NewInt(1),
|
||||
runtime.NewInt(2),
|
||||
runtime.NewInt(3),
|
||||
runtime.NewInt(4),
|
||||
runtime.NewInt(5),
|
||||
)
|
||||
|
||||
lenBefore := arr.Length()
|
||||
lenBefore, _ := arr.Length(ctx)
|
||||
|
||||
arr.RemoveAt(3)
|
||||
arr.RemoveAt(ctx, 3)
|
||||
|
||||
lenAfter := arr.Length()
|
||||
lenAfter, _ := arr.Length(ctx)
|
||||
|
||||
val, _ := arr.Get(ctx, 3)
|
||||
|
||||
So(lenAfter, ShouldBeLessThan, lenBefore)
|
||||
So(arr.Get(3), ShouldEqual, 4)
|
||||
So(val, ShouldEqual, 4)
|
||||
})
|
||||
|
||||
Convey("Should remove an item from the end", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
NewInt(0),
|
||||
NewInt(1),
|
||||
NewInt(2),
|
||||
NewInt(3),
|
||||
NewInt(4),
|
||||
NewInt(5),
|
||||
runtime.NewInt(0),
|
||||
runtime.NewInt(1),
|
||||
runtime.NewInt(2),
|
||||
runtime.NewInt(3),
|
||||
runtime.NewInt(4),
|
||||
runtime.NewInt(5),
|
||||
)
|
||||
|
||||
lenBefore := arr.Length()
|
||||
lenBefore, _ := arr.Length(ctx)
|
||||
|
||||
arr.RemoveAt(5)
|
||||
arr.RemoveAt(ctx, 5)
|
||||
|
||||
lenAfter := arr.Length()
|
||||
lenAfter, _ := arr.Length(ctx)
|
||||
|
||||
val, _ := arr.Get(ctx, 4)
|
||||
|
||||
So(lenAfter, ShouldBeLessThan, lenBefore)
|
||||
So(lenAfter, ShouldEqual, 5)
|
||||
So(arr.Get(4), ShouldEqual, 4)
|
||||
So(val, ShouldEqual, 4)
|
||||
})
|
||||
|
||||
Convey("Should remove an item from the beginning", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
NewInt(0),
|
||||
NewInt(1),
|
||||
NewInt(2),
|
||||
NewInt(3),
|
||||
NewInt(4),
|
||||
NewInt(5),
|
||||
runtime.NewInt(0),
|
||||
runtime.NewInt(1),
|
||||
runtime.NewInt(2),
|
||||
runtime.NewInt(3),
|
||||
runtime.NewInt(4),
|
||||
runtime.NewInt(5),
|
||||
)
|
||||
|
||||
lenBefore := arr.Length()
|
||||
lenBefore, _ := arr.Length(ctx)
|
||||
|
||||
arr.RemoveAt(0)
|
||||
arr.RemoveAt(ctx, 0)
|
||||
|
||||
lenAfter := arr.Length()
|
||||
lenAfter, _ := arr.Length(ctx)
|
||||
|
||||
val, _ := arr.Get(ctx, 0)
|
||||
|
||||
So(lenAfter, ShouldBeLessThan, lenBefore)
|
||||
So(arr.Get(0), ShouldEqual, 1)
|
||||
So(val, ShouldEqual, 1)
|
||||
})
|
||||
})
|
||||
|
||||
Convey(".Clone", t, func() {
|
||||
Convey("Cloned array should be equal to source array", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
NewInt(0),
|
||||
runtime.NewInt(0),
|
||||
runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("one", NewInt(1)),
|
||||
runtime.NewObjectProperty("one", runtime.NewInt(1)),
|
||||
),
|
||||
runtime.NewArrayWith(
|
||||
NewInt(2),
|
||||
runtime.NewInt(2),
|
||||
),
|
||||
)
|
||||
|
||||
clone := arr.Clone().(*runtime.Array)
|
||||
cloned, _ := arr.Clone(ctx)
|
||||
clone := cloned.(*runtime.Array)
|
||||
|
||||
So(arr.Length(), ShouldEqual, clone.Length())
|
||||
size, _ := arr.Length(ctx)
|
||||
cloneSize, _ := clone.Length(ctx)
|
||||
|
||||
So(size, ShouldEqual, cloneSize)
|
||||
So(arr.Compare(clone), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Cloned array should be independent of the source array", func() {
|
||||
arr := runtime.NewArrayWith(
|
||||
NewInt(0),
|
||||
NewInt(1),
|
||||
NewInt(2),
|
||||
NewInt(3),
|
||||
NewInt(4),
|
||||
NewInt(5),
|
||||
runtime.NewInt(0),
|
||||
runtime.NewInt(1),
|
||||
runtime.NewInt(2),
|
||||
runtime.NewInt(3),
|
||||
runtime.NewInt(4),
|
||||
runtime.NewInt(5),
|
||||
)
|
||||
|
||||
clone := arr.Clone().(*runtime.Array)
|
||||
cloned, _ := arr.Clone(ctx)
|
||||
clone := cloned.(*runtime.Array)
|
||||
|
||||
arr.Push(NewInt(6))
|
||||
arr.Add(ctx, runtime.NewInt(6))
|
||||
|
||||
So(arr.Length(), ShouldNotEqual, clone.Length())
|
||||
size, _ := arr.Length(ctx)
|
||||
cloneSize, _ := clone.Length(ctx)
|
||||
|
||||
So(size, ShouldEqual, cloneSize)
|
||||
So(arr.Compare(clone), ShouldNotEqual, 0)
|
||||
})
|
||||
|
||||
//Convey("Cloned array must contain copies of the nested objects", func() {
|
||||
// arr := values.NewArrayWith(
|
||||
// values.NewArrayWith(
|
||||
// values.NewInt(0),
|
||||
// values.NewInt(1),
|
||||
// values.NewInt(2),
|
||||
// values.NewInt(3),
|
||||
// values.NewInt(4),
|
||||
// values.runtime.NewInt(0),
|
||||
// values.runtime.NewInt(1),
|
||||
// values.runtime.NewInt(2),
|
||||
// values.runtime.NewInt(3),
|
||||
// values.runtime.NewInt(4),
|
||||
// ),
|
||||
// )
|
||||
//
|
||||
// clone := arr.Clone().(*values.Array)
|
||||
//
|
||||
// nestedInArr := arr.Get(values.NewInt(0)).(*values.Array)
|
||||
// nestedInArr.Push(values.NewInt(5))
|
||||
// nestedInArr := arr.Get(values.runtime.NewInt(0)).(*values.Array)
|
||||
// nestedInArr.Add(ctx, values.runtime.NewInt(5))
|
||||
//
|
||||
// nestedInClone := clone.Get(values.NewInt(0)).(*values.Array)
|
||||
// nestedInClone := clone.Get(values.runtime.NewInt(0)).(*values.Array)
|
||||
//
|
||||
// So(nestedInArr.CompareValues(nestedInClone), ShouldNotEqual, 0)
|
||||
// So(nestedInArr.Compareruntime.Values(nestedInClone), ShouldNotEqual, 0)
|
||||
//})
|
||||
})
|
||||
}
|
||||
|
@@ -79,7 +79,7 @@ func AssertList(input Value) error {
|
||||
}
|
||||
|
||||
func AssertItemsOf(ctx context.Context, input Iterable, assertion TypeAssertion) error {
|
||||
return ForEachOf(ctx, input, func(ctx context.Context, value, _ Value) (Boolean, error) {
|
||||
return ForEach(ctx, input, func(ctx context.Context, value, _ Value) (Boolean, error) {
|
||||
if err := assertion(value); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ package runtime_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
. "github.com/MontFerret/ferret/pkg/runtime"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
func TestBoolean(t *testing.T) {
|
||||
Convey(".MarshalJSON", t, func() {
|
||||
Convey("Should serialize a boolean items", func() {
|
||||
b := runtime.True
|
||||
b := True
|
||||
marshaled, err := b.MarshalJSON()
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
@@ -22,19 +22,19 @@ func TestBoolean(t *testing.T) {
|
||||
|
||||
Convey(".Unwrap", t, func() {
|
||||
Convey("Should return an unwrapped items", func() {
|
||||
So(runtime.True.Unwrap(), ShouldHaveSameTypeAs, true)
|
||||
So(True.Unwrap(), ShouldHaveSameTypeAs, true)
|
||||
})
|
||||
})
|
||||
|
||||
Convey(".String", t, func() {
|
||||
Convey("Should return a string representation ", func() {
|
||||
So(runtime.True.String(), ShouldEqual, "true")
|
||||
So(True.String(), ShouldEqual, "true")
|
||||
})
|
||||
})
|
||||
|
||||
Convey(".CompareValues", t, func() {
|
||||
Convey("It should return 1 when compared to None", func() {
|
||||
So(runtime.True.Compare(runtime.None), ShouldEqual, 1)
|
||||
So(True.Compare(None), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("It should return -1 for all non-boolean values", func() {
|
||||
@@ -42,8 +42,8 @@ func TestBoolean(t *testing.T) {
|
||||
NewString("foo"),
|
||||
NewInt(1),
|
||||
NewFloat(1.1),
|
||||
runtime.NewArray(10),
|
||||
runtime.NewObject(),
|
||||
NewArray(10),
|
||||
NewObject(),
|
||||
}
|
||||
|
||||
for _, v := range vals {
|
||||
|
@@ -26,7 +26,6 @@ type (
|
||||
Cloneable
|
||||
Iterable
|
||||
|
||||
IsEmpty(ctx context.Context) (Boolean, error)
|
||||
Clear(ctx context.Context) error
|
||||
}
|
||||
|
||||
@@ -35,22 +34,20 @@ type (
|
||||
List interface {
|
||||
Collection
|
||||
Indexed
|
||||
Sortable
|
||||
|
||||
Add(ctx context.Context, value Value) error
|
||||
Set(ctx context.Context, idx Int, value Value) error
|
||||
Insert(ctx context.Context, idx Int, value Value) error
|
||||
Remove(ctx context.Context, value Value) error
|
||||
RemoveAt(ctx context.Context, idx Int) (Value, error)
|
||||
Insert(ctx context.Context, idx Int, value Value) error
|
||||
Swap(ctx context.Context, a, b Int) error
|
||||
|
||||
Find(ctx context.Context, predicate IndexedPredicate) (List, error)
|
||||
FindOne(ctx context.Context, predicate IndexedPredicate) (Value, Boolean, error)
|
||||
Contains(ctx context.Context, value Value) (Boolean, error)
|
||||
IndexOf(ctx context.Context, value Value) (Int, error)
|
||||
First(context.Context) (Value, error)
|
||||
Last(context.Context) (Value, error)
|
||||
Slice(ctx context.Context, start, end Int) (List, error)
|
||||
CopyWithCap(ctx context.Context, cap Int) (List, error)
|
||||
|
||||
ForEach(ctx context.Context, predicate IndexedPredicate) error
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ var (
|
||||
NewBinary = runtime.NewBinary
|
||||
NewBoolean = runtime.NewBoolean
|
||||
|
||||
ForEach = runtime.ForEach
|
||||
ForEach = runtime.ForEachIter
|
||||
|
||||
NewFunctions = runtime.NewFunctions
|
||||
NewFunctionsFromMap = runtime.NewFunctionsFromMap
|
||||
|
@@ -4,36 +4,21 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestSourceError(t *testing.T) {
|
||||
Convey("Should match", t, func() {
|
||||
sm := NewSourceMap("test", 1, 1)
|
||||
|
||||
msg := "test at 1:1"
|
||||
cause := errors.New("cause")
|
||||
e := errors.Errorf("%s: %s", cause.Error(), msg)
|
||||
|
||||
cse := core.SourceError(sm, cause)
|
||||
So(cse, ShouldNotBeNil)
|
||||
So(cse.Error(), ShouldEqual, e.Error())
|
||||
})
|
||||
}
|
||||
|
||||
func TestTypeError(t *testing.T) {
|
||||
Convey("Should match", t, func() {
|
||||
e := runtime.TypeError(TypeA{})
|
||||
e := runtime.TypeError(runtime.True, runtime.TypeList)
|
||||
So(e, ShouldNotBeNil)
|
||||
|
||||
e = runtime.TypeError(TypeA{}, TypeB{})
|
||||
e = runtime.TypeError(runtime.True, runtime.TypeList, runtime.TypeString)
|
||||
So(e, ShouldNotBeNil)
|
||||
|
||||
cause := errors.New("invalid type: expected type_b or type_c, but got type_a")
|
||||
e = runtime.TypeError(TypeA{}, TypeB{}, TypeC{})
|
||||
cause := errors.New("invalid type: expected foo or bar, but got boolean")
|
||||
e = runtime.TypeError(runtime.True, "foo", "bar")
|
||||
So(e.Error(), ShouldEqual, cause.Error())
|
||||
})
|
||||
}
|
||||
|
@@ -72,7 +72,7 @@ func TestIsNil(t *testing.T) {
|
||||
}
|
||||
|
||||
func (t *CustomValue) MarshalJSON() ([]byte, error) {
|
||||
return nil, ErrNotImplemented
|
||||
return nil, runtime.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (t *CustomValue) String() string {
|
||||
@@ -87,8 +87,8 @@ func (t *CustomValue) Hash() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (t *CustomValue) Copy() Value {
|
||||
return None
|
||||
func (t *CustomValue) Copy() runtime.Value {
|
||||
return runtime.None
|
||||
}
|
||||
|
||||
func TestHelpers(t *testing.T) {
|
||||
@@ -96,14 +96,14 @@ func TestHelpers(t *testing.T) {
|
||||
Convey("Parse", func() {
|
||||
Convey("It should parse values", func() {
|
||||
inputs := []struct {
|
||||
Parsed Value
|
||||
Parsed runtime.Value
|
||||
Raw interface{}
|
||||
}{
|
||||
{Parsed: NewInt(1), Raw: int(1)},
|
||||
{Parsed: NewInt(1), Raw: int8(1)},
|
||||
{Parsed: NewInt(1), Raw: int16(1)},
|
||||
{Parsed: NewInt(1), Raw: int32(1)},
|
||||
{Parsed: NewInt(1), Raw: int64(1)},
|
||||
{Parsed: runtime.NewInt(1), Raw: int(1)},
|
||||
{Parsed: runtime.NewInt(1), Raw: int8(1)},
|
||||
{Parsed: runtime.NewInt(1), Raw: int16(1)},
|
||||
{Parsed: runtime.NewInt(1), Raw: int32(1)},
|
||||
{Parsed: runtime.NewInt(1), Raw: int64(1)},
|
||||
}
|
||||
|
||||
for _, input := range inputs {
|
||||
@@ -120,58 +120,58 @@ func TestHelpers(t *testing.T) {
|
||||
|
||||
Convey("ToBoolean", func() {
|
||||
Convey("Should convert values", func() {
|
||||
inputs := [][]Value{
|
||||
inputs := [][]runtime.Value{
|
||||
{
|
||||
None,
|
||||
False,
|
||||
runtime.None,
|
||||
runtime.False,
|
||||
},
|
||||
{
|
||||
True,
|
||||
True,
|
||||
runtime.True,
|
||||
runtime.True,
|
||||
},
|
||||
{
|
||||
False,
|
||||
False,
|
||||
runtime.False,
|
||||
runtime.False,
|
||||
},
|
||||
{
|
||||
NewInt(1),
|
||||
True,
|
||||
runtime.NewInt(1),
|
||||
runtime.True,
|
||||
},
|
||||
{
|
||||
NewInt(0),
|
||||
False,
|
||||
runtime.NewInt(0),
|
||||
runtime.False,
|
||||
},
|
||||
{
|
||||
NewFloat(1),
|
||||
True,
|
||||
runtime.NewFloat(1),
|
||||
runtime.True,
|
||||
},
|
||||
{
|
||||
NewFloat(0),
|
||||
False,
|
||||
runtime.NewFloat(0),
|
||||
runtime.False,
|
||||
},
|
||||
{
|
||||
NewString("Foo"),
|
||||
True,
|
||||
runtime.NewString("Foo"),
|
||||
runtime.True,
|
||||
},
|
||||
{
|
||||
EmptyString,
|
||||
False,
|
||||
runtime.EmptyString,
|
||||
runtime.False,
|
||||
},
|
||||
{
|
||||
NewCurrentDateTime(),
|
||||
True,
|
||||
runtime.NewCurrentDateTime(),
|
||||
runtime.True,
|
||||
},
|
||||
{
|
||||
NewArray(1),
|
||||
True,
|
||||
runtime.NewArray(1),
|
||||
runtime.True,
|
||||
},
|
||||
{
|
||||
NewObject(),
|
||||
True,
|
||||
runtime.NewObject(),
|
||||
runtime.True,
|
||||
},
|
||||
{
|
||||
NewBinary([]byte("")),
|
||||
True,
|
||||
runtime.NewBinary([]byte("")),
|
||||
runtime.True,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -184,152 +184,187 @@ func TestHelpers(t *testing.T) {
|
||||
})
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
Convey("ToFloat", func() {
|
||||
Convey("Should convert Int", func() {
|
||||
input := NewInt(100)
|
||||
output := runtime.ToFloat(input)
|
||||
input := runtime.NewInt(100)
|
||||
output, err := runtime.ToFloat(ctx, input)
|
||||
|
||||
So(output, ShouldEqual, NewFloat(100))
|
||||
So(err, ShouldBeNil)
|
||||
So(output, ShouldEqual, runtime.NewFloat(100))
|
||||
})
|
||||
|
||||
Convey("Should convert Float", func() {
|
||||
input := NewFloat(100)
|
||||
output := runtime.ToFloat(input)
|
||||
input := runtime.NewFloat(100)
|
||||
output, err := runtime.ToFloat(ctx, input)
|
||||
|
||||
So(output, ShouldEqual, NewFloat(100))
|
||||
So(err, ShouldBeNil)
|
||||
So(output, ShouldEqual, runtime.NewFloat(100))
|
||||
})
|
||||
|
||||
Convey("Should convert String", func() {
|
||||
input := NewString("100.1")
|
||||
output := runtime.ToFloat(input)
|
||||
input := runtime.NewString("100.1")
|
||||
output, err := runtime.ToFloat(ctx, input)
|
||||
|
||||
So(output, ShouldEqual, NewFloat(100.1))
|
||||
So(err, ShouldBeNil)
|
||||
So(output, ShouldEqual, runtime.NewFloat(100.1))
|
||||
|
||||
output2 := runtime.ToFloat(NewString("foobar"))
|
||||
So(output2, ShouldEqual, ZeroFloat)
|
||||
output2, err := runtime.ToFloat(ctx, runtime.NewString("foobar"))
|
||||
So(output2, ShouldEqual, runtime.ZeroFloat)
|
||||
})
|
||||
|
||||
Convey("Should convert Boolean", func() {
|
||||
So(runtime.ToFloat(True), ShouldEqual, NewFloat(1))
|
||||
So(runtime.ToFloat(False), ShouldEqual, NewFloat(0))
|
||||
out, err := runtime.ToFloat(ctx, runtime.True)
|
||||
So(err, ShouldBeNil)
|
||||
So(out, ShouldEqual, runtime.NewFloat(1))
|
||||
|
||||
out, err = runtime.ToFloat(ctx, runtime.False)
|
||||
So(err, ShouldBeNil)
|
||||
So(out, ShouldEqual, runtime.NewFloat(0))
|
||||
})
|
||||
|
||||
Convey("Should convert Array with single item", func() {
|
||||
So(runtime.ToFloat(NewArrayWith(NewFloat(1))), ShouldEqual, NewFloat(1))
|
||||
out, err := runtime.ToFloat(ctx, runtime.NewArrayWith(runtime.NewFloat(1)))
|
||||
So(err, ShouldBeNil)
|
||||
So(out, ShouldEqual, runtime.NewFloat(1))
|
||||
})
|
||||
|
||||
Convey("Should convert Array with multiple items", func() {
|
||||
arg := NewArrayWith(NewFloat(1), NewFloat(1))
|
||||
arg := runtime.NewArrayWith(runtime.NewFloat(1), runtime.NewFloat(1))
|
||||
|
||||
So(runtime.ToFloat(arg), ShouldEqual, NewFloat(2))
|
||||
out, err := runtime.ToFloat(ctx, arg)
|
||||
So(err, ShouldBeNil)
|
||||
So(out, ShouldEqual, runtime.NewFloat(2))
|
||||
})
|
||||
|
||||
Convey("Should convert DateTime", func() {
|
||||
dt := NewCurrentDateTime()
|
||||
dt := runtime.NewCurrentDateTime()
|
||||
ts := dt.Time.Unix()
|
||||
|
||||
So(runtime.ToFloat(dt), ShouldEqual, NewFloat(float64(ts)))
|
||||
out, err := runtime.ToFloat(ctx, dt)
|
||||
So(err, ShouldBeNil)
|
||||
So(out, ShouldEqual, runtime.NewFloat(float64(ts)))
|
||||
})
|
||||
|
||||
Convey("Should NOT convert other types", func() {
|
||||
inputs := []Value{
|
||||
NewObject(),
|
||||
NewBinary([]byte("")),
|
||||
inputs := []runtime.Value{
|
||||
runtime.NewObject(),
|
||||
runtime.NewBinary([]byte("")),
|
||||
}
|
||||
|
||||
for _, input := range inputs {
|
||||
So(runtime.ToFloat(input), ShouldEqual, ZeroFloat)
|
||||
out, err := runtime.ToFloat(ctx, input)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(out, ShouldEqual, runtime.ZeroFloat)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Convey("ToInt", func() {
|
||||
Convey("Should convert Int", func() {
|
||||
input := NewInt(100)
|
||||
output := runtime.ToInt(input)
|
||||
input := runtime.NewInt(100)
|
||||
output, err := runtime.ToInt(ctx, input)
|
||||
|
||||
So(output, ShouldEqual, NewInt(100))
|
||||
So(err, ShouldBeNil)
|
||||
So(output, ShouldEqual, runtime.NewInt(100))
|
||||
})
|
||||
|
||||
Convey("Should convert Float", func() {
|
||||
input := NewFloat(100.1)
|
||||
output := runtime.ToInt(input)
|
||||
|
||||
So(output, ShouldEqual, NewInt(100))
|
||||
input := runtime.NewFloat(100.1)
|
||||
output, err := runtime.ToInt(ctx, input)
|
||||
So(err, ShouldBeNil)
|
||||
So(output, ShouldEqual, runtime.NewInt(100))
|
||||
})
|
||||
|
||||
Convey("Should convert String", func() {
|
||||
input := NewString("100")
|
||||
output := runtime.ToInt(input)
|
||||
input := runtime.NewString("100")
|
||||
output, err := runtime.ToInt(ctx, input)
|
||||
|
||||
So(output, ShouldEqual, NewInt(100))
|
||||
So(err, ShouldBeNil)
|
||||
So(output, ShouldEqual, runtime.NewInt(100))
|
||||
|
||||
output2 := runtime.ToInt(NewString("foobar"))
|
||||
So(output2, ShouldEqual, ZeroInt)
|
||||
output2, err := runtime.ToInt(ctx, runtime.NewString("foobar"))
|
||||
So(err, ShouldNotBeNil)
|
||||
So(output2, ShouldEqual, runtime.ZeroInt)
|
||||
})
|
||||
|
||||
Convey("Should convert Boolean", func() {
|
||||
So(runtime.ToInt(True), ShouldEqual, NewInt(1))
|
||||
So(runtime.ToInt(False), ShouldEqual, NewInt(0))
|
||||
out, err := runtime.ToInt(ctx, runtime.True)
|
||||
So(err, ShouldBeNil)
|
||||
So(out, ShouldEqual, runtime.NewInt(1))
|
||||
|
||||
out, err = runtime.ToInt(ctx, runtime.False)
|
||||
So(err, ShouldBeNil)
|
||||
So(out, ShouldEqual, runtime.NewInt(0))
|
||||
})
|
||||
|
||||
Convey("Should convert Array with single item", func() {
|
||||
So(runtime.ToInt(NewArrayWith(NewFloat(1))), ShouldEqual, NewInt(1))
|
||||
out, err := runtime.ToInt(ctx, runtime.NewArrayWith(runtime.NewInt(1)))
|
||||
So(err, ShouldBeNil)
|
||||
So(out, ShouldEqual, runtime.NewInt(1))
|
||||
})
|
||||
|
||||
Convey("Should convert Array with multiple items", func() {
|
||||
arg := NewArrayWith(NewFloat(1), NewFloat(1))
|
||||
arg := runtime.NewArrayWith(runtime.NewFloat(1), runtime.NewFloat(1))
|
||||
out, err := runtime.ToInt(ctx, arg)
|
||||
|
||||
So(runtime.ToInt(arg), ShouldEqual, NewFloat(2))
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(out, ShouldEqual, runtime.NewFloat(2))
|
||||
})
|
||||
|
||||
Convey("Should convert DateTime", func() {
|
||||
dt := NewCurrentDateTime()
|
||||
dt := runtime.NewCurrentDateTime()
|
||||
ts := dt.Time.Unix()
|
||||
out, err := runtime.ToInt(ctx, dt)
|
||||
|
||||
So(runtime.ToInt(dt), ShouldEqual, NewInt(int(ts)))
|
||||
So(err, ShouldBeNil)
|
||||
So(out, ShouldEqual, runtime.NewInt(int(ts)))
|
||||
})
|
||||
|
||||
Convey("Should NOT convert other types", func() {
|
||||
inputs := []Value{
|
||||
NewObject(),
|
||||
NewBinary([]byte("")),
|
||||
inputs := []runtime.Value{
|
||||
runtime.NewObject(),
|
||||
runtime.NewBinary([]byte("")),
|
||||
}
|
||||
|
||||
for _, input := range inputs {
|
||||
So(runtime.ToInt(input), ShouldEqual, ZeroInt)
|
||||
out, err := runtime.ToInt(ctx, input)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(out, ShouldEqual, runtime.ZeroInt)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Convey("ToList", func() {
|
||||
Convey("Should convert primitives", func() {
|
||||
dt := NewCurrentDateTime()
|
||||
dt := runtime.NewCurrentDateTime()
|
||||
|
||||
inputs := [][]Value{
|
||||
inputs := [][]runtime.Value{
|
||||
{
|
||||
None,
|
||||
NewArray(0),
|
||||
runtime.None,
|
||||
runtime.NewArray(0),
|
||||
},
|
||||
{
|
||||
True,
|
||||
NewArrayWith(True),
|
||||
runtime.True,
|
||||
runtime.NewArrayWith(runtime.True),
|
||||
},
|
||||
{
|
||||
NewInt(1),
|
||||
NewArrayWith(NewInt(1)),
|
||||
runtime.NewInt(1),
|
||||
runtime.NewArrayWith(runtime.NewInt(1)),
|
||||
},
|
||||
{
|
||||
NewFloat(1),
|
||||
NewArrayWith(NewFloat(1)),
|
||||
runtime.NewFloat(1),
|
||||
runtime.NewArrayWith(runtime.NewFloat(1)),
|
||||
},
|
||||
{
|
||||
NewString("foo"),
|
||||
NewArrayWith(NewString("foo")),
|
||||
runtime.NewString("foo"),
|
||||
runtime.NewArrayWith(runtime.NewString("foo")),
|
||||
},
|
||||
{
|
||||
dt,
|
||||
NewArrayWith(dt),
|
||||
runtime.NewArrayWith(dt),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -342,24 +377,24 @@ func TestHelpers(t *testing.T) {
|
||||
})
|
||||
|
||||
//Convey("Should create a copy of a given array", func() {
|
||||
// vals := []core.Value{
|
||||
// values.NewInt(1),
|
||||
// values.NewInt(2),
|
||||
// values.NewInt(3),
|
||||
// values.NewInt(4),
|
||||
// values.NewArray(10),
|
||||
// values.NewObject(),
|
||||
// vals := []core.runtime.Value{
|
||||
// values.runtime.NewInt(1),
|
||||
// values.runtime.NewInt(2),
|
||||
// values.runtime.NewInt(3),
|
||||
// values.runtime.NewInt(4),
|
||||
// values.runtime.NewArray(10),
|
||||
// values.runtime.NewObject(),
|
||||
// }
|
||||
//
|
||||
// input := values.NewArrayWith(vals...)
|
||||
// input := values.runtime.NewArrayWith(vals...)
|
||||
// arr := values.ToList(context.Background(), input)
|
||||
//
|
||||
// So(input == arr, ShouldBeFalse)
|
||||
// So(arr.Length() == input.Length(), ShouldBeTrue)
|
||||
//
|
||||
// for idx := range vals {
|
||||
// expected := input.Get(values.NewInt(idx))
|
||||
// actual := arr.Get(values.NewInt(idx))
|
||||
// expected := input.Get(values.runtime.NewInt(idx))
|
||||
// actual := arr.Get(values.runtime.NewInt(idx))
|
||||
//
|
||||
// // same ref
|
||||
// So(actual == expected, ShouldBeTrue)
|
||||
@@ -369,15 +404,15 @@ func TestHelpers(t *testing.T) {
|
||||
|
||||
//Convey("Should convert object to an array", func() {
|
||||
// input := values.NewObjectWith(
|
||||
// values.NewObjectProperty("foo", values.NewString("bar")),
|
||||
// values.NewObjectProperty("baz", values.NewInt(1)),
|
||||
// values.NewObjectProperty("qaz", values.NewObject()),
|
||||
// values.NewObjectProperty("foo", values.runtime.NewString("bar")),
|
||||
// values.NewObjectProperty("baz", values.runtime.NewInt(1)),
|
||||
// values.NewObjectProperty("qaz", values.runtime.NewObject()),
|
||||
// )
|
||||
//
|
||||
// arr := values.ToList(context.Background(), input).Sort()
|
||||
//
|
||||
// So(arr.String(), ShouldEqual, "[1,\"bar\",{}]")
|
||||
// So(arr.Get(values.NewInt(2)) == input.MustGet("qaz"), ShouldBeTrue)
|
||||
// So(arr.Get(values.runtime.NewInt(2)) == input.MustGet("qaz"), ShouldBeTrue)
|
||||
//})
|
||||
})
|
||||
|
||||
|
@@ -18,14 +18,14 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func ForEachOf(ctx context.Context, input Iterable, predicate Predicate) error {
|
||||
func ForEach(ctx context.Context, input Iterable, predicate Predicate) error {
|
||||
iter, err := input.Iterate(ctx)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ForEach(ctx, iter, predicate)
|
||||
err = ForEachIter(ctx, iter, predicate)
|
||||
closable, ok := iter.(io.Closer)
|
||||
|
||||
if ok {
|
||||
@@ -37,7 +37,7 @@ func ForEachOf(ctx context.Context, input Iterable, predicate Predicate) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func ForEach(ctx context.Context, iter Iterator, predicate Predicate) error {
|
||||
func ForEachIter(ctx context.Context, iter Iterator, predicate Predicate) error {
|
||||
for {
|
||||
hasNext, err := iter.HasNext(ctx)
|
||||
|
||||
|
@@ -24,11 +24,11 @@ func NewObjectProperty(name string, value Value) *ObjectProperty {
|
||||
return &ObjectProperty{name, value}
|
||||
}
|
||||
|
||||
func NewObject() Map {
|
||||
func NewObject() *Object {
|
||||
return &Object{make(map[string]Value)}
|
||||
}
|
||||
|
||||
func NewObjectWith(props ...*ObjectProperty) Map {
|
||||
func NewObjectWith(props ...*ObjectProperty) *Object {
|
||||
obj := &Object{make(map[string]Value)}
|
||||
|
||||
for _, prop := range props {
|
||||
@@ -38,7 +38,7 @@ func NewObjectWith(props ...*ObjectProperty) Map {
|
||||
return obj
|
||||
}
|
||||
|
||||
func NewObjectOf(props map[string]Value) Map {
|
||||
func NewObjectOf(props map[string]Value) *Object {
|
||||
return &Object{props}
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
. "github.com/MontFerret/ferret/pkg/runtime"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
func TestObjectIterator(t *testing.T) {
|
||||
Convey("No values", t, func() {
|
||||
ctx := context.Background()
|
||||
obj := runtime.NewObject()
|
||||
iter := runtime.NewObjectIterator(obj)
|
||||
obj := NewObject()
|
||||
iter := NewObjectIterator(obj)
|
||||
|
||||
hasNext, err := iter.HasNext(ctx)
|
||||
|
||||
@@ -24,9 +24,9 @@ func TestObjectIterator(t *testing.T) {
|
||||
|
||||
Convey("One value", t, func() {
|
||||
ctx := context.Background()
|
||||
obj := runtime.NewObject()
|
||||
obj.Set(NewString("key"), NewInt(1))
|
||||
iter := runtime.NewObjectIterator(obj)
|
||||
obj := NewObject()
|
||||
obj.Set(ctx, NewString("key"), NewInt(1))
|
||||
iter := NewObjectIterator(obj)
|
||||
|
||||
hasNext, err := iter.HasNext(ctx)
|
||||
|
||||
@@ -47,13 +47,13 @@ func TestObjectIterator(t *testing.T) {
|
||||
|
||||
Convey("Multiple values", t, func() {
|
||||
ctx := context.Background()
|
||||
obj := runtime.NewObject()
|
||||
obj.Set(NewString("key1"), NewInt(1))
|
||||
obj.Set(NewString("key2"), NewInt(2))
|
||||
obj.Set(NewString("key3"), NewInt(3))
|
||||
obj.Set(NewString("key4"), NewInt(4))
|
||||
obj.Set(NewString("key5"), NewInt(5))
|
||||
iter := runtime.NewObjectIterator(obj)
|
||||
obj := NewObject()
|
||||
obj.Set(ctx, NewString("key1"), NewInt(1))
|
||||
obj.Set(ctx, NewString("key2"), NewInt(2))
|
||||
obj.Set(ctx, NewString("key3"), NewInt(3))
|
||||
obj.Set(ctx, NewString("key4"), NewInt(4))
|
||||
obj.Set(ctx, NewString("key5"), NewInt(5))
|
||||
iter := NewObjectIterator(obj)
|
||||
|
||||
actual := make([][2]Value, 0, 5)
|
||||
|
||||
@@ -84,14 +84,14 @@ func TestObjectIterator(t *testing.T) {
|
||||
|
||||
func BenchmarkObjectIterator(b *testing.B) {
|
||||
size := 100
|
||||
obj := runtime.NewObject()
|
||||
obj := NewObject()
|
||||
ctx := context.Background()
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
obj.Set(NewString("key"+ToString(NewInt(i)).String()), NewInt(i))
|
||||
obj.Set(ctx, NewString("key"+ToString(NewInt(i)).String()), NewInt(i))
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
iter := runtime.NewObjectIterator(obj)
|
||||
iter := NewObjectIterator(obj)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
|
@@ -1,9 +1,10 @@
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
c "context"
|
||||
"testing"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
. "github.com/MontFerret/ferret/pkg/runtime"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
@@ -11,29 +12,34 @@ import (
|
||||
func TestObject(t *testing.T) {
|
||||
Convey("#constructor", t, func() {
|
||||
Convey("Should create an empty object", func() {
|
||||
obj := runtime.NewObject()
|
||||
obj := NewObject()
|
||||
size, err := obj.Length(c.Background())
|
||||
|
||||
So(obj.Length(), ShouldEqual, 0)
|
||||
So(err, ShouldBeNil)
|
||||
So(size, ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should create an object, from passed values", func() {
|
||||
obj := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("none", None),
|
||||
runtime.NewObjectProperty("boolean", False),
|
||||
runtime.NewObjectProperty("int", NewInt(1)),
|
||||
runtime.NewObjectProperty("float", Float(1)),
|
||||
runtime.NewObjectProperty("string", NewString("1")),
|
||||
runtime.NewObjectProperty("array", NewArray(10)),
|
||||
runtime.NewObjectProperty("object", runtime.NewObject()),
|
||||
obj := NewObjectWith(
|
||||
NewObjectProperty("none", None),
|
||||
NewObjectProperty("boolean", False),
|
||||
NewObjectProperty("int", NewInt(1)),
|
||||
NewObjectProperty("float", Float(1)),
|
||||
NewObjectProperty("string", NewString("1")),
|
||||
NewObjectProperty("array", NewArray(10)),
|
||||
NewObjectProperty("object", NewObject()),
|
||||
)
|
||||
|
||||
So(obj.Length(), ShouldEqual, 7)
|
||||
size, err := obj.Length(c.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(size, ShouldEqual, 7)
|
||||
})
|
||||
})
|
||||
|
||||
Convey(".MarshalJSON", t, func() {
|
||||
Convey("Should serialize an empty object", func() {
|
||||
obj := runtime.NewObject()
|
||||
obj := NewObject()
|
||||
marshaled, err := obj.MarshalJSON()
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
@@ -42,14 +48,14 @@ func TestObject(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Should serialize full object", func() {
|
||||
obj := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("none", None),
|
||||
runtime.NewObjectProperty("boolean", False),
|
||||
runtime.NewObjectProperty("int", NewInt(1)),
|
||||
runtime.NewObjectProperty("float", Float(1)),
|
||||
runtime.NewObjectProperty("string", NewString("1")),
|
||||
runtime.NewObjectProperty("array", NewArray(10)),
|
||||
runtime.NewObjectProperty("object", runtime.NewObject()),
|
||||
obj := NewObjectWith(
|
||||
NewObjectProperty("none", None),
|
||||
NewObjectProperty("boolean", False),
|
||||
NewObjectProperty("int", NewInt(1)),
|
||||
NewObjectProperty("float", Float(1)),
|
||||
NewObjectProperty("string", NewString("1")),
|
||||
NewObjectProperty("array", NewArray(10)),
|
||||
NewObjectProperty("object", NewObject()),
|
||||
)
|
||||
marshaled, err := obj.MarshalJSON()
|
||||
|
||||
@@ -61,9 +67,9 @@ func TestObject(t *testing.T) {
|
||||
|
||||
Convey(".Unwrap", t, func() {
|
||||
Convey("Should return an unwrapped items", func() {
|
||||
obj := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("foo", NewString("foo")),
|
||||
runtime.NewObjectProperty("bar", NewString("bar")),
|
||||
obj := NewObjectWith(
|
||||
NewObjectProperty("foo", NewString("foo")),
|
||||
NewObjectProperty("bar", NewString("bar")),
|
||||
)
|
||||
|
||||
for _, val := range obj.Unwrap().(map[string]interface{}) {
|
||||
@@ -74,8 +80,8 @@ func TestObject(t *testing.T) {
|
||||
|
||||
Convey(".String", t, func() {
|
||||
Convey("Should return a string representation ", func() {
|
||||
obj := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("foo", NewString("bar")),
|
||||
obj := NewObjectWith(
|
||||
NewObjectProperty("foo", NewString("bar")),
|
||||
)
|
||||
|
||||
So(obj.String(), ShouldEqual, "{\"foo\":\"bar\"}")
|
||||
@@ -92,7 +98,7 @@ func TestObject(t *testing.T) {
|
||||
NewString("1"),
|
||||
NewArray(10),
|
||||
}
|
||||
obj := runtime.NewObject()
|
||||
obj := NewObject()
|
||||
|
||||
for _, val := range arr {
|
||||
So(obj.Compare(val), ShouldEqual, 1)
|
||||
@@ -101,26 +107,26 @@ func TestObject(t *testing.T) {
|
||||
|
||||
Convey("It should return -1 for all object values", func() {
|
||||
arr := NewArrayWith(ZeroInt, ZeroInt)
|
||||
obj := runtime.NewObject()
|
||||
obj := NewObject()
|
||||
|
||||
So(arr.Compare(obj), ShouldEqual, -1)
|
||||
})
|
||||
|
||||
Convey("It should return 0 when both objects are empty", func() {
|
||||
obj1 := runtime.NewObject()
|
||||
obj2 := runtime.NewObject()
|
||||
obj1 := NewObject()
|
||||
obj2 := NewObject()
|
||||
|
||||
So(obj1.Compare(obj2), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("It should return 0 when both objects are equal (independent of key order)", func() {
|
||||
obj1 := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("foo", NewString("foo")),
|
||||
runtime.NewObjectProperty("bar", NewString("bar")),
|
||||
obj1 := NewObjectWith(
|
||||
NewObjectProperty("foo", NewString("foo")),
|
||||
NewObjectProperty("bar", NewString("bar")),
|
||||
)
|
||||
obj2 := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("foo", NewString("foo")),
|
||||
runtime.NewObjectProperty("bar", NewString("bar")),
|
||||
obj2 := NewObjectWith(
|
||||
NewObjectProperty("foo", NewString("foo")),
|
||||
NewObjectProperty("bar", NewString("bar")),
|
||||
)
|
||||
|
||||
So(obj1.Compare(obj1), ShouldEqual, 0)
|
||||
@@ -130,82 +136,82 @@ func TestObject(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("It should return 1 when other array is empty", func() {
|
||||
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewString("bar")))
|
||||
obj2 := runtime.NewObject()
|
||||
obj1 := NewObjectWith(NewObjectProperty("foo", NewString("bar")))
|
||||
obj2 := NewObject()
|
||||
|
||||
So(obj1.Compare(obj2), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("It should return 1 when values are bigger", func() {
|
||||
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewFloat(3)))
|
||||
obj2 := runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewFloat(2)))
|
||||
obj1 := NewObjectWith(NewObjectProperty("foo", NewFloat(3)))
|
||||
obj2 := NewObjectWith(NewObjectProperty("foo", NewFloat(2)))
|
||||
|
||||
So(obj1.Compare(obj2), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("It should return 1 when values are less", func() {
|
||||
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewFloat(1)))
|
||||
obj2 := runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewFloat(2)))
|
||||
obj1 := NewObjectWith(NewObjectProperty("foo", NewFloat(1)))
|
||||
obj2 := NewObjectWith(NewObjectProperty("foo", NewFloat(2)))
|
||||
|
||||
So(obj1.Compare(obj2), ShouldEqual, -1)
|
||||
})
|
||||
|
||||
Convey("ArangoDB compatibility", func() {
|
||||
Convey("It should return 1 when {a:1} and {b:2}", func() {
|
||||
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("a", NewInt(1)))
|
||||
obj2 := runtime.NewObjectWith(runtime.NewObjectProperty("b", NewInt(2)))
|
||||
obj1 := NewObjectWith(NewObjectProperty("a", NewInt(1)))
|
||||
obj2 := NewObjectWith(NewObjectProperty("b", NewInt(2)))
|
||||
|
||||
So(obj1.Compare(obj2), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("It should return 0 when {a:1} and {a:1}", func() {
|
||||
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("a", NewInt(1)))
|
||||
obj2 := runtime.NewObjectWith(runtime.NewObjectProperty("a", NewInt(1)))
|
||||
obj1 := NewObjectWith(NewObjectProperty("a", NewInt(1)))
|
||||
obj2 := NewObjectWith(NewObjectProperty("a", NewInt(1)))
|
||||
|
||||
So(obj1.Compare(obj2), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("It should return 0 {a:1, c:2} and {c:2, a:1}", func() {
|
||||
obj1 := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("a", NewInt(1)),
|
||||
runtime.NewObjectProperty("c", NewInt(2)),
|
||||
obj1 := NewObjectWith(
|
||||
NewObjectProperty("a", NewInt(1)),
|
||||
NewObjectProperty("c", NewInt(2)),
|
||||
)
|
||||
obj2 := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("c", NewInt(2)),
|
||||
runtime.NewObjectProperty("a", NewInt(1)),
|
||||
obj2 := NewObjectWith(
|
||||
NewObjectProperty("c", NewInt(2)),
|
||||
NewObjectProperty("a", NewInt(1)),
|
||||
)
|
||||
|
||||
So(obj1.Compare(obj2), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("It should return -1 when {a:1} and {a:2}", func() {
|
||||
obj1 := runtime.NewObjectWith(runtime.NewObjectProperty("a", NewInt(1)))
|
||||
obj2 := runtime.NewObjectWith(runtime.NewObjectProperty("a", NewInt(2)))
|
||||
obj1 := NewObjectWith(NewObjectProperty("a", NewInt(1)))
|
||||
obj2 := NewObjectWith(NewObjectProperty("a", NewInt(2)))
|
||||
|
||||
So(obj1.Compare(obj2), ShouldEqual, -1)
|
||||
})
|
||||
|
||||
Convey("It should return 1 when {a:1, c:2} and {c:2, b:2}", func() {
|
||||
obj1 := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("a", NewInt(1)),
|
||||
runtime.NewObjectProperty("c", NewInt(2)),
|
||||
obj1 := NewObjectWith(
|
||||
NewObjectProperty("a", NewInt(1)),
|
||||
NewObjectProperty("c", NewInt(2)),
|
||||
)
|
||||
obj2 := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("c", NewInt(2)),
|
||||
runtime.NewObjectProperty("b", NewInt(2)),
|
||||
obj2 := NewObjectWith(
|
||||
NewObjectProperty("c", NewInt(2)),
|
||||
NewObjectProperty("b", NewInt(2)),
|
||||
)
|
||||
|
||||
So(obj1.Compare(obj2), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("It should return 1 {a:1, c:3} and {c:2, a:1}", func() {
|
||||
obj1 := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("a", NewInt(1)),
|
||||
runtime.NewObjectProperty("c", NewInt(3)),
|
||||
obj1 := NewObjectWith(
|
||||
NewObjectProperty("a", NewInt(1)),
|
||||
NewObjectProperty("c", NewInt(3)),
|
||||
)
|
||||
obj2 := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("c", NewInt(2)),
|
||||
runtime.NewObjectProperty("a", NewInt(1)),
|
||||
obj2 := NewObjectWith(
|
||||
NewObjectProperty("c", NewInt(2)),
|
||||
NewObjectProperty("a", NewInt(1)),
|
||||
)
|
||||
|
||||
So(obj1.Compare(obj2), ShouldEqual, 1)
|
||||
@@ -215,10 +221,10 @@ func TestObject(t *testing.T) {
|
||||
|
||||
Convey(".Hash", t, func() {
|
||||
Convey("It should calculate hash of non-empty object", func() {
|
||||
v := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("foo", NewString("bar")),
|
||||
runtime.NewObjectProperty("faz", NewInt(1)),
|
||||
runtime.NewObjectProperty("qaz", True),
|
||||
v := NewObjectWith(
|
||||
NewObjectProperty("foo", NewString("bar")),
|
||||
NewObjectProperty("faz", NewInt(1)),
|
||||
NewObjectProperty("qaz", True),
|
||||
)
|
||||
|
||||
h := v.Hash()
|
||||
@@ -227,7 +233,7 @@ func TestObject(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("It should calculate hash of empty object", func() {
|
||||
v := runtime.NewObject()
|
||||
v := NewObject()
|
||||
|
||||
h := v.Hash()
|
||||
|
||||
@@ -235,14 +241,14 @@ func TestObject(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Hash sum should be consistent", func() {
|
||||
v := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("boolean", True),
|
||||
runtime.NewObjectProperty("int", NewInt(1)),
|
||||
runtime.NewObjectProperty("float", NewFloat(1.1)),
|
||||
runtime.NewObjectProperty("string", NewString("foobar")),
|
||||
runtime.NewObjectProperty("datetime", NewCurrentDateTime()),
|
||||
runtime.NewObjectProperty("array", NewArrayWith(NewInt(1), True)),
|
||||
runtime.NewObjectProperty("object", runtime.NewObjectWith(runtime.NewObjectProperty("foo", NewString("bar")))),
|
||||
v := NewObjectWith(
|
||||
NewObjectProperty("boolean", True),
|
||||
NewObjectProperty("int", NewInt(1)),
|
||||
NewObjectProperty("float", NewFloat(1.1)),
|
||||
NewObjectProperty("string", NewString("foobar")),
|
||||
NewObjectProperty("datetime", NewCurrentDateTime()),
|
||||
NewObjectProperty("array", NewArrayWith(NewInt(1), True)),
|
||||
NewObjectProperty("object", NewObjectWith(NewObjectProperty("foo", NewString("bar")))),
|
||||
)
|
||||
|
||||
h1 := v.Hash()
|
||||
@@ -254,66 +260,80 @@ func TestObject(t *testing.T) {
|
||||
|
||||
Convey(".Length", t, func() {
|
||||
Convey("Should return 0 when empty", func() {
|
||||
obj := runtime.NewObject()
|
||||
obj := NewObject()
|
||||
size, err := obj.Length(c.Background())
|
||||
|
||||
So(obj.Length(), ShouldEqual, 0)
|
||||
So(err, ShouldBeNil)
|
||||
So(size, ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should return greater than 0 when not empty", func() {
|
||||
obj := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("foo", ZeroInt),
|
||||
runtime.NewObjectProperty("bar", ZeroInt),
|
||||
obj := NewObjectWith(
|
||||
NewObjectProperty("foo", ZeroInt),
|
||||
NewObjectProperty("bar", ZeroInt),
|
||||
)
|
||||
|
||||
So(obj.Length(), ShouldEqual, 2)
|
||||
size, err := obj.Length(c.Background())
|
||||
So(err, ShouldBeNil)
|
||||
So(size, ShouldEqual, 2)
|
||||
})
|
||||
})
|
||||
|
||||
Convey(".ForEach", t, func() {
|
||||
Convey(".ForEachIter", t, func() {
|
||||
Convey("Should iterate over elements", func() {
|
||||
obj := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("foo", ZeroInt),
|
||||
runtime.NewObjectProperty("bar", ZeroInt),
|
||||
obj := NewObjectWith(
|
||||
NewObjectProperty("bar", ZeroInt),
|
||||
NewObjectProperty("foo", ZeroInt),
|
||||
)
|
||||
counter := 0
|
||||
ctx := c.Background()
|
||||
|
||||
obj.ForEach(func(value Value, key string) bool {
|
||||
obj.ForEach(ctx, func(_ c.Context, value, key Value) (Boolean, error) {
|
||||
counter++
|
||||
|
||||
return true
|
||||
return true, nil
|
||||
})
|
||||
|
||||
So(counter, ShouldEqual, obj.Length())
|
||||
size, err := obj.Length(ctx)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(counter, ShouldEqual, size)
|
||||
})
|
||||
|
||||
Convey("Should not iterate when empty", func() {
|
||||
obj := runtime.NewObject()
|
||||
obj := NewObject()
|
||||
counter := 0
|
||||
|
||||
obj.ForEach(func(value Value, key string) bool {
|
||||
ctx := c.Background()
|
||||
|
||||
obj.ForEach(ctx, func(_ c.Context, value, key Value) (Boolean, error) {
|
||||
counter++
|
||||
|
||||
return true
|
||||
return true, nil
|
||||
})
|
||||
|
||||
So(counter, ShouldEqual, obj.Length())
|
||||
size, err := obj.Length(ctx)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(counter, ShouldEqual, size)
|
||||
})
|
||||
|
||||
Convey("Should break iteration when false returned", func() {
|
||||
obj := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("1", NewInt(1)),
|
||||
runtime.NewObjectProperty("2", NewInt(2)),
|
||||
runtime.NewObjectProperty("3", NewInt(3)),
|
||||
runtime.NewObjectProperty("4", NewInt(4)),
|
||||
runtime.NewObjectProperty("5", NewInt(5)),
|
||||
obj := NewObjectWith(
|
||||
NewObjectProperty("1", NewInt(1)),
|
||||
NewObjectProperty("2", NewInt(2)),
|
||||
NewObjectProperty("3", NewInt(3)),
|
||||
NewObjectProperty("4", NewInt(4)),
|
||||
NewObjectProperty("5", NewInt(5)),
|
||||
)
|
||||
threshold := 3
|
||||
counter := 0
|
||||
ctx := c.Background()
|
||||
|
||||
obj.ForEach(func(value Value, key string) bool {
|
||||
obj.ForEach(ctx, func(_ c.Context, value, key Value) (Boolean, error) {
|
||||
counter++
|
||||
|
||||
return counter < threshold
|
||||
return counter < threshold, nil
|
||||
})
|
||||
|
||||
So(counter, ShouldEqual, threshold)
|
||||
@@ -357,43 +377,49 @@ func TestObject(t *testing.T) {
|
||||
|
||||
Convey(".Clone", t, func() {
|
||||
Convey("Cloned object should be equal to source object", func() {
|
||||
obj := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("one", NewInt(1)),
|
||||
runtime.NewObjectProperty("two", NewInt(2)),
|
||||
obj := NewObjectWith(
|
||||
NewObjectProperty("one", NewInt(1)),
|
||||
NewObjectProperty("two", NewInt(2)),
|
||||
)
|
||||
|
||||
clone := obj.Clone().(*runtime.Object)
|
||||
ctx := c.Background()
|
||||
cloned, _ := obj.Clone(ctx)
|
||||
clone := cloned.(*Object)
|
||||
|
||||
So(obj.Compare(clone), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Cloned object should be independent of the source object", func() {
|
||||
obj := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty("one", NewInt(1)),
|
||||
runtime.NewObjectProperty("two", NewInt(2)),
|
||||
obj := NewObjectWith(
|
||||
NewObjectProperty("one", NewInt(1)),
|
||||
NewObjectProperty("two", NewInt(2)),
|
||||
)
|
||||
|
||||
clone := obj.Clone().(*runtime.Object)
|
||||
ctx := c.Background()
|
||||
cloned, _ := obj.Clone(ctx)
|
||||
clone := cloned.(*Object)
|
||||
|
||||
obj.Remove(NewString("one"))
|
||||
obj.Remove(ctx, NewString("one"))
|
||||
|
||||
So(obj.Compare(clone), ShouldNotEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Cloned object must contain copies of the nested objects", func() {
|
||||
obj := runtime.NewObjectWith(
|
||||
runtime.NewObjectProperty(
|
||||
obj := NewObjectWith(
|
||||
NewObjectProperty(
|
||||
"arr", NewArrayWith(NewInt(1)),
|
||||
),
|
||||
)
|
||||
|
||||
clone := obj.Clone().(*runtime.Object)
|
||||
ctx := c.Background()
|
||||
cloned, _ := obj.Clone(ctx)
|
||||
clone := cloned.(*Object)
|
||||
|
||||
nestedInObj, _ := obj.Get(NewString("arr"))
|
||||
nestedInObj, _ := obj.Get(ctx, NewString("arr"))
|
||||
nestedInObjArr := nestedInObj.(*Array)
|
||||
nestedInObjArr.Push(NewInt(2))
|
||||
nestedInObjArr.Add(ctx, NewInt(2))
|
||||
|
||||
nestedInClone, _ := clone.Get(NewString("arr"))
|
||||
nestedInClone, _ := clone.Get(ctx, NewString("arr"))
|
||||
nestedInCloneArr := nestedInClone.(*Array)
|
||||
|
||||
So(nestedInObjArr.Compare(nestedInCloneArr), ShouldNotEqual, 0)
|
||||
|
224
pkg/runtime/sort.go
Normal file
224
pkg/runtime/sort.go
Normal file
@@ -0,0 +1,224 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type (
|
||||
SortLess = func(ctx context.Context, a, b Int) (Boolean, error)
|
||||
SortSwap = func(ctx context.Context, a, b Int) error
|
||||
)
|
||||
|
||||
// sortInsertionSort sorts data[a:b] using insertion Sort.
|
||||
func sortInsertionSort(ctx context.Context, less SortLess, swap SortSwap, a, b Int) error {
|
||||
for i := a + 1; i < b; i++ {
|
||||
for j := i; j > a; j-- {
|
||||
isLess, err := less(ctx, j, j-1)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isLess {
|
||||
break
|
||||
}
|
||||
|
||||
if err := swap(ctx, j, j-1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortSwapRange(ctx context.Context, swap SortSwap, a, b, n Int) error {
|
||||
for i := ZeroInt; i < n; i++ {
|
||||
if err := swap(ctx, a+i, b+i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func stableSort(ctx context.Context, less SortLess, swap SortSwap, n Int) error {
|
||||
blockSize := Int(20) // must be > 0
|
||||
a, b := ZeroInt, blockSize
|
||||
for b <= n {
|
||||
if err := sortInsertionSort(ctx, less, swap, a, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a = b
|
||||
b += blockSize
|
||||
}
|
||||
|
||||
if err := sortInsertionSort(ctx, less, swap, a, n); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for blockSize < n {
|
||||
a, b = 0, 2*blockSize
|
||||
|
||||
for b <= n {
|
||||
if err := sortSymMerge(ctx, less, swap, a, a+blockSize, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a = b
|
||||
b += 2 * blockSize
|
||||
}
|
||||
|
||||
if m := a + blockSize; m < n {
|
||||
if err := sortSymMerge(ctx, less, swap, a, m, n); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
blockSize *= 2
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortSymMerge(ctx context.Context, less SortLess, swap SortSwap, a, m, b Int) error {
|
||||
// Avoid unnecessary recursions of symMerge
|
||||
// by direct insertion of data[a] into data[m:b]
|
||||
// if data[a:m] only contains one element.
|
||||
if m-a == 1 {
|
||||
// Use binary search to find the lowest index i
|
||||
// such that data[i] >= data[a] for m <= i < b.
|
||||
// Exit the search loop with i == b in case no such index exists.
|
||||
i := m
|
||||
j := b
|
||||
for i < j {
|
||||
h := Int(uint(i+j) >> 1)
|
||||
|
||||
isLess, err := less(ctx, h, a)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isLess {
|
||||
i = h + 1
|
||||
} else {
|
||||
j = h
|
||||
}
|
||||
}
|
||||
// Swap values until data[a] reaches the position before i.
|
||||
for k := a; k < i-1; k++ {
|
||||
if err := swap(ctx, k, k+1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Avoid unnecessary recursions of symMerge
|
||||
// by direct insertion of data[m] into data[a:m]
|
||||
// if data[m:b] only contains one element.
|
||||
if b-m == 1 {
|
||||
// Use binary search to find the lowest index i
|
||||
// such that data[i] > data[m] for a <= i < m.
|
||||
// Exit the search loop with i == m in case no such index exists.
|
||||
i := a
|
||||
j := m
|
||||
for i < j {
|
||||
h := Int(uint(i+j) >> 1)
|
||||
|
||||
isLess, err := less(ctx, m, h)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isLess {
|
||||
i = h + 1
|
||||
} else {
|
||||
j = h
|
||||
}
|
||||
}
|
||||
// Swap values until data[m] reaches the position i.
|
||||
for k := m; k > i; k-- {
|
||||
if err := swap(ctx, k, k-1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
mid := Int(uint(a+b) >> 1)
|
||||
n := mid + m
|
||||
var start, r Int
|
||||
|
||||
if m > mid {
|
||||
start = n - b
|
||||
r = mid
|
||||
} else {
|
||||
start = a
|
||||
r = m
|
||||
}
|
||||
p := n - 1
|
||||
|
||||
for start < r {
|
||||
c := Int(uint(start+r) >> 1)
|
||||
|
||||
isLess, err := less(ctx, p-c, c)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isLess {
|
||||
start = c + 1
|
||||
} else {
|
||||
r = c
|
||||
}
|
||||
}
|
||||
|
||||
end := n - start
|
||||
if start < m && m < end {
|
||||
if err := sortRotate(ctx, swap, start, m, end); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if a < start && start < mid {
|
||||
if err := sortSymMerge(ctx, less, swap, a, start, mid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if mid < end && end < b {
|
||||
if err := sortSymMerge(ctx, less, swap, mid, end, b); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortRotate(ctx context.Context, swap SortSwap, a, m, b Int) error {
|
||||
i := m - a
|
||||
j := b - m
|
||||
|
||||
for i != j {
|
||||
if i > j {
|
||||
if err := sortSwapRange(ctx, swap, m-i, m, j); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i -= j
|
||||
} else {
|
||||
if err := sortSwapRange(ctx, swap, m-i, m+j-i, i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
j -= i
|
||||
}
|
||||
}
|
||||
// i == j
|
||||
return sortSwapRange(ctx, swap, m-i, m, i)
|
||||
}
|
@@ -12,9 +12,96 @@ type Sortable interface {
|
||||
|
||||
// SortDesc sorts the collection in descending order.
|
||||
SortDesc(context.Context) error
|
||||
}
|
||||
|
||||
// SortWith sorts the collection using a custom comparator.
|
||||
SortWith(context.Context, Comparator) error
|
||||
func SortAsc(ctx context.Context, values Value) error {
|
||||
return Sort(ctx, values, true)
|
||||
}
|
||||
|
||||
func SortDesc(ctx context.Context, values Value) error {
|
||||
return Sort(ctx, values, false)
|
||||
}
|
||||
|
||||
// Sort is a generic sorting function that accepts either a List or value that implements Sortable interface.
|
||||
func Sort(ctx context.Context, values Value, ascending Boolean) error {
|
||||
switch value := values.(type) {
|
||||
case Sortable:
|
||||
if ascending {
|
||||
return value.SortAsc(ctx)
|
||||
}
|
||||
|
||||
return value.SortDesc(ctx)
|
||||
case List:
|
||||
return SortList(ctx, value, ascending)
|
||||
default:
|
||||
return TypeError(values, TypeList, TypeSortable)
|
||||
}
|
||||
}
|
||||
|
||||
func SortListAsc(ctx context.Context, values List) error {
|
||||
return SortList(ctx, values, true)
|
||||
}
|
||||
|
||||
func SortListDesc(ctx context.Context, values List) error {
|
||||
return SortList(ctx, values, false)
|
||||
}
|
||||
|
||||
// SortList sorts the given List using the stable Sort algorithm
|
||||
func SortList(ctx context.Context, values List, ascending Boolean) error {
|
||||
var pivot int64 = -1
|
||||
|
||||
if ascending {
|
||||
pivot = 1
|
||||
}
|
||||
|
||||
size, err := values.Length(ctx)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return stableSort(ctx, func(ctx context.Context, a, b Int) (Boolean, error) {
|
||||
aVal, err := values.Get(ctx, a)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
bVal, err := values.Get(ctx, b)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
comp := CompareValues(aVal, bVal) * pivot
|
||||
|
||||
return comp == -1, nil
|
||||
}, values.Swap, size)
|
||||
}
|
||||
|
||||
// SortListWith sorts the given List using the stable Sort algorithm using a custom comparator
|
||||
func SortListWith(ctx context.Context, values List, comparator Comparator) error {
|
||||
size, err := values.Length(ctx)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return stableSort(ctx, func(ctx context.Context, a, b Int) (Boolean, error) {
|
||||
aVal, err := values.Get(ctx, a)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
bVal, err := values.Get(ctx, b)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return comparator(aVal, bVal) == -1, nil
|
||||
}, values.Swap, size)
|
||||
}
|
||||
|
||||
func SortSlice(values []Value, ascending Boolean) {
|
||||
|
36
pkg/runtime/sortable_test.go
Normal file
36
pkg/runtime/sortable_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestSortList(t *testing.T) {
|
||||
Convey("Should sort Array DESC", t, func() {
|
||||
arr1 := runtime.NewArrayWith(runtime.Int(3), runtime.Int(1), runtime.Int(2), runtime.Int(6), runtime.Int(4), runtime.Int(5))
|
||||
|
||||
err := runtime.SortList(context.Background(), arr1, false)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
j, err := arr1.MarshalJSON()
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(string(j), ShouldEqual, `[6,5,4,3,2,1]`)
|
||||
})
|
||||
|
||||
Convey("Should sort Array ASC", t, func() {
|
||||
arr1 := runtime.NewArrayWith(runtime.Int(3), runtime.Int(1), runtime.Int(2), runtime.Int(6), runtime.Int(4), runtime.Int(5))
|
||||
|
||||
err := runtime.SortList(context.Background(), arr1, true)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
j, err := arr1.MarshalJSON()
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(string(j), ShouldEqual, `[1,2,3,4,5,6]`)
|
||||
})
|
||||
}
|
@@ -23,8 +23,10 @@ type (
|
||||
Iterable
|
||||
Measurable
|
||||
Indexed
|
||||
Sortable
|
||||
|
||||
Add(ctx context.Context, value Value) error
|
||||
AddKV(ctx context.Context, key, value Value) error
|
||||
}
|
||||
|
||||
StorageManager interface {
|
@@ -1,6 +1,7 @@
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
c "context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
@@ -34,8 +35,10 @@ func TestString(t *testing.T) {
|
||||
Convey(".Length", t, func() {
|
||||
Convey("Should return unicode length", func() {
|
||||
str := runtime.NewString("Спутник")
|
||||
size, err := str.Length(c.Background())
|
||||
|
||||
So(str.Length(), ShouldEqual, 7)
|
||||
So(err, ShouldBeNil)
|
||||
So(size, ShouldEqual, 7)
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -13,11 +13,13 @@ const (
|
||||
TypeMap = "map"
|
||||
TypeBinary = "binary"
|
||||
|
||||
// Interfaces
|
||||
TypeIterable = "iterable"
|
||||
TypeIterator = "iterator"
|
||||
TypeMeasurable = "measurable"
|
||||
TypeComparable = "comparable"
|
||||
TypeCloneable = "cloneable"
|
||||
TypeSortable = "sortable"
|
||||
TypeObservable = "observable"
|
||||
)
|
||||
|
||||
|
@@ -12,5 +12,6 @@ type Value interface {
|
||||
String() string
|
||||
Unwrap() interface{}
|
||||
Hash() uint64
|
||||
// TODO: Add context and return error. It needs to support values that rely on external storages.
|
||||
Copy() Value
|
||||
}
|
||||
|
@@ -36,22 +36,25 @@ func Append(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
unique = arg3
|
||||
}
|
||||
|
||||
var next runtime.List
|
||||
|
||||
// We do not know for sure if the list is an array or custom List implementation.
|
||||
// Hence, we must solely rely on the List interface.
|
||||
next, err := list.CopyWithCap(ctx, 1)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
switch v := arg.(type) {
|
||||
case *runtime.Array:
|
||||
next = v.CopyWithGrowth(1)
|
||||
case runtime.List:
|
||||
next = v.Copy().(runtime.List)
|
||||
}
|
||||
|
||||
if unique {
|
||||
contains, err := list.Contains(ctx, arg)
|
||||
idx, err := list.IndexOf(ctx, arg)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if contains {
|
||||
if idx > -1 {
|
||||
return next, nil
|
||||
}
|
||||
}
|
||||
|
@@ -27,11 +27,11 @@ func RemoveNth(ctx context.Context, args ...runtime.Value) (runtime.Value, error
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
next, err := list.CopyWithCap(ctx, 0)
|
||||
next := list.Copy().(runtime.List)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
//if err != nil {
|
||||
// return runtime.None, err
|
||||
//}
|
||||
|
||||
_, err = next.RemoveAt(ctx, index)
|
||||
|
||||
|
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
)
|
||||
|
||||
// SORTED sorts all elements in anyArray.
|
||||
// SORTED sorts all elements in the given list.
|
||||
// The function will use the default comparison order for FQL value types.
|
||||
// @param {Any[]} array - Target array.
|
||||
// @return {Any[]} - Sorted array.
|
||||
@@ -31,13 +31,13 @@ func Sorted(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
return runtime.EmptyArray(), nil
|
||||
}
|
||||
|
||||
copied, err := list.CopyWithCap(ctx, 0)
|
||||
copied := list.Copy()
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
//if err != nil {
|
||||
// return runtime.None, err
|
||||
//}
|
||||
|
||||
if err := copied.SortAsc(ctx); err != nil {
|
||||
if err := runtime.SortAsc(ctx, copied); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
|
@@ -28,7 +28,7 @@ func SortedUnique(ctx context.Context, args ...runtime.Value) (runtime.Value, er
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := uniq.SortAsc(ctx); err != nil {
|
||||
if err := runtime.SortAsc(ctx, uniq); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
|
@@ -45,7 +45,7 @@ func Includes(ctx context.Context, args ...runtime.Value) (runtime.Value, error)
|
||||
return runtime.False, err
|
||||
}
|
||||
|
||||
err = runtime.ForEach(ctx, iter, func(c context.Context, value runtime.Value, key runtime.Value) (runtime.Boolean, error) {
|
||||
err = runtime.ForEachIter(ctx, iter, func(c context.Context, value runtime.Value, key runtime.Value) (runtime.Boolean, error) {
|
||||
if runtime.CompareValues(needle, value) == 0 {
|
||||
result = runtime.True
|
||||
|
||||
|
@@ -21,13 +21,16 @@ func Median(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
sorted, err := arr.CopyWithCap(ctx, 0)
|
||||
sorted := arr.Copy().(runtime.List)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
//
|
||||
//sorted, err := arr.CopyWithGrowth(ctx, 0)
|
||||
//
|
||||
//if err != nil {
|
||||
// return runtime.None, err
|
||||
//}
|
||||
|
||||
if err := sorted.SortDesc(ctx); err != nil {
|
||||
if err := runtime.SortDesc(ctx, sorted); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
|
@@ -62,13 +62,13 @@ func Percentile(ctx context.Context, args ...runtime.Value) (runtime.Value, erro
|
||||
return runtime.NaN(), errors.New("input is outside of range")
|
||||
}
|
||||
|
||||
sorted, err := arr.CopyWithCap(ctx, 0)
|
||||
sorted := arr.Copy().(runtime.List)
|
||||
|
||||
if err != nil {
|
||||
return runtime.NaN(), err
|
||||
}
|
||||
//if err != nil {
|
||||
// return runtime.NaN(), err
|
||||
//}
|
||||
|
||||
if err := sorted.SortDesc(ctx); err != nil {
|
||||
if err := runtime.SortAsc(ctx, sorted); err != nil {
|
||||
return runtime.NaN(), err
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/wI2L/jettison"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
)
|
||||
|
||||
type Box[T any] struct {
|
||||
|
@@ -2,23 +2,22 @@ package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
)
|
||||
|
||||
type DataSet struct {
|
||||
uniqueness runtime.MapStorage
|
||||
groups runtime.MapStorage
|
||||
values runtime.ListStorage
|
||||
uniqueness map[uint64]bool
|
||||
groups map[string]runtime.List
|
||||
values runtime.List
|
||||
keyed bool
|
||||
}
|
||||
|
||||
func NewDataSet(distinct bool) *DataSet {
|
||||
var hashmap runtime.MapStorage
|
||||
func NewDataSet(distinct bool) runtime.List {
|
||||
var hashmap map[uint64]bool
|
||||
|
||||
if distinct {
|
||||
hashmap = runtime.NewObject()
|
||||
hashmap = make(map[uint64]bool)
|
||||
}
|
||||
|
||||
return &DataSet{
|
||||
@@ -28,7 +27,7 @@ func NewDataSet(distinct bool) *DataSet {
|
||||
}
|
||||
|
||||
func (ds *DataSet) Sort(ctx context.Context, direction runtime.Int) error {
|
||||
return ds.values.SortWith(ctx, func(first, second runtime.Value) int64 {
|
||||
return runtime.SortListWith(ctx, ds.values, func(first, second runtime.Value) int64 {
|
||||
firstKV, firstOk := first.(*KV)
|
||||
secondKV, secondOk := second.(*KV)
|
||||
|
||||
@@ -49,7 +48,7 @@ func (ds *DataSet) Sort(ctx context.Context, direction runtime.Int) error {
|
||||
}
|
||||
|
||||
func (ds *DataSet) SortMany(ctx context.Context, directions []runtime.Int) error {
|
||||
return ds.values.SortWith(ctx, func(first, second runtime.Value) int64 {
|
||||
return runtime.SortListWith(ctx, ds.values, func(first, second runtime.Value) int64 {
|
||||
firstKV, firstOk := first.(*KV)
|
||||
secondKV, secondOk := second.(*KV)
|
||||
|
||||
@@ -120,7 +119,19 @@ func (ds *DataSet) AddKV(ctx context.Context, key, value runtime.Value) error {
|
||||
}
|
||||
|
||||
func (ds *DataSet) Collect(ctx context.Context, key, value runtime.Value) error {
|
||||
return nil
|
||||
if ds.groups == nil {
|
||||
ds.groups = make(map[string]runtime.List)
|
||||
}
|
||||
|
||||
keyStr := key.String()
|
||||
group, ok := ds.groups[keyStr]
|
||||
|
||||
if !ok {
|
||||
group = runtime.NewArray(8)
|
||||
ds.groups[keyStr] = group
|
||||
}
|
||||
|
||||
return group.Add(ctx, value)
|
||||
}
|
||||
|
||||
func (ds *DataSet) Iterate(ctx context.Context) (runtime.Iterator, error) {
|
||||
@@ -158,30 +169,83 @@ func (ds *DataSet) Copy() runtime.Value {
|
||||
}
|
||||
|
||||
func (ds *DataSet) MarshalJSON() ([]byte, error) {
|
||||
return nil, nil
|
||||
return ds.values.MarshalJSON()
|
||||
}
|
||||
|
||||
func (ds *DataSet) ToList() runtime.List {
|
||||
return ds.values
|
||||
func (ds *DataSet) Compare(other runtime.Value) int64 {
|
||||
return ds.values.Compare(other)
|
||||
}
|
||||
|
||||
func (ds *DataSet) canAdd(ctx context.Context, value runtime.Value) (bool, error) {
|
||||
func (ds *DataSet) Clone(ctx context.Context) (runtime.Cloneable, error) {
|
||||
return ds.values.Clone(ctx)
|
||||
}
|
||||
|
||||
func (ds *DataSet) Clear(ctx context.Context) error {
|
||||
return ds.values.Clear(ctx)
|
||||
}
|
||||
|
||||
func (ds *DataSet) Set(ctx context.Context, idx runtime.Int, value runtime.Value) error {
|
||||
return ds.values.Set(ctx, idx, value)
|
||||
}
|
||||
|
||||
func (ds *DataSet) Remove(ctx context.Context, value runtime.Value) error {
|
||||
return ds.values.Remove(ctx, value)
|
||||
}
|
||||
|
||||
func (ds *DataSet) RemoveAt(ctx context.Context, idx runtime.Int) (runtime.Value, error) {
|
||||
return ds.values.RemoveAt(ctx, idx)
|
||||
}
|
||||
|
||||
func (ds *DataSet) Insert(ctx context.Context, idx runtime.Int, value runtime.Value) error {
|
||||
return ds.values.Insert(ctx, idx, value)
|
||||
}
|
||||
|
||||
func (ds *DataSet) Swap(ctx context.Context, a, b runtime.Int) error {
|
||||
return ds.values.Swap(ctx, a, b)
|
||||
}
|
||||
|
||||
func (ds *DataSet) Find(ctx context.Context, predicate runtime.IndexedPredicate) (runtime.List, error) {
|
||||
return ds.values.Find(ctx, predicate)
|
||||
}
|
||||
|
||||
func (ds *DataSet) FindOne(ctx context.Context, predicate runtime.IndexedPredicate) (runtime.Value, runtime.Boolean, error) {
|
||||
return ds.values.FindOne(ctx, predicate)
|
||||
}
|
||||
|
||||
func (ds *DataSet) IndexOf(ctx context.Context, value runtime.Value) (runtime.Int, error) {
|
||||
return ds.values.IndexOf(ctx, value)
|
||||
}
|
||||
|
||||
func (ds *DataSet) First(ctx context.Context) (runtime.Value, error) {
|
||||
return ds.values.First(ctx)
|
||||
}
|
||||
|
||||
func (ds *DataSet) Last(ctx context.Context) (runtime.Value, error) {
|
||||
return ds.values.Last(ctx)
|
||||
}
|
||||
|
||||
func (ds *DataSet) Slice(ctx context.Context, start, end runtime.Int) (runtime.List, error) {
|
||||
return ds.values.Slice(ctx, start, end)
|
||||
}
|
||||
|
||||
func (ds *DataSet) ForEach(ctx context.Context, predicate runtime.IndexedPredicate) error {
|
||||
return ds.values.ForEach(ctx, predicate)
|
||||
}
|
||||
|
||||
func (ds *DataSet) canAdd(_ context.Context, value runtime.Value) (bool, error) {
|
||||
if ds.uniqueness == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
hash := value.Hash()
|
||||
rnHash := runtime.Int(int64(hash))
|
||||
|
||||
_, err := ds.uniqueness.Get(ctx, rnHash)
|
||||
_, exists := ds.uniqueness[hash]
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, runtime.ErrNotFound) {
|
||||
return true, ds.uniqueness.Set(ctx, rnHash, value)
|
||||
}
|
||||
|
||||
return false, err
|
||||
if exists {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
ds.uniqueness[hash] = true
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
@@ -4,6 +4,8 @@ import (
|
||||
"encoding/binary"
|
||||
"hash/fnv"
|
||||
|
||||
"github.com/wI2L/jettison"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
)
|
||||
|
||||
@@ -20,7 +22,12 @@ func NewKV(key, value runtime.Value) *KV {
|
||||
}
|
||||
|
||||
func (p *KV) MarshalJSON() ([]byte, error) {
|
||||
return p.Value.MarshalJSON()
|
||||
pair := [2]runtime.Value{
|
||||
p.Key,
|
||||
p.Value,
|
||||
}
|
||||
|
||||
return jettison.MarshalOpts(pair, jettison.NoHTMLEscaping())
|
||||
}
|
||||
|
||||
func (p *KV) String() string {
|
||||
|
@@ -2,6 +2,7 @@ package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
)
|
||||
|
||||
|
@@ -13,12 +13,13 @@ import (
|
||||
func Contains(ctx context.Context, input runtime.Value, value runtime.Value) runtime.Boolean {
|
||||
switch val := input.(type) {
|
||||
case runtime.List:
|
||||
contains, err := val.Contains(ctx, value)
|
||||
idx, err := val.IndexOf(ctx, value)
|
||||
|
||||
if err != nil {
|
||||
return runtime.False
|
||||
}
|
||||
|
||||
return contains
|
||||
return idx > -1
|
||||
case runtime.Map:
|
||||
containsValue, err := val.ContainsValue(ctx, value)
|
||||
|
||||
@@ -34,7 +35,7 @@ func Contains(ctx context.Context, input runtime.Value, value runtime.Value) run
|
||||
}
|
||||
}
|
||||
|
||||
func Add(ctx context.Context, inputL, inputR runtime.Value) runtime.Value {
|
||||
func Add(_ context.Context, inputL, inputR runtime.Value) runtime.Value {
|
||||
left := runtime.ToNumberOrString(inputL)
|
||||
|
||||
switch leftVal := left.(type) {
|
||||
|
@@ -1,8 +1,9 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"time"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
)
|
||||
|
||||
type StreamValue struct {
|
||||
|
@@ -70,10 +70,9 @@ const (
|
||||
OpCollect // Groups a collection by a key (COLLECT R1, R2, R3 - groups a collection in R1 with a key from R2 and a value from R3)
|
||||
OpCollectMany
|
||||
|
||||
OpDataSet // Creates a new dataset
|
||||
OpDataSetAdd // Adds a value to a dataset
|
||||
OpDataSetAddKV // Adds a key-value pair to a dataset
|
||||
OpDataSetToList // TODO: Temporary. Remove when the final API is ready.
|
||||
OpDataSet // Creates a new dataset
|
||||
OpDataSetAdd // Adds a value to a dataset
|
||||
OpDataSetAddKV // Adds a key-value pair to a dataset
|
||||
|
||||
OpIter // Creates an iterator (ITER R2, R3 [, R4] - creates an iterator in R2 with a collection from R3 and optional params from R4)
|
||||
OpIterNext // Moves to the next element in the iterator (ITER R2, R3 - moves to the next element in the iterator in R2 with a collection from R3)
|
||||
|
@@ -366,9 +366,6 @@ loop:
|
||||
|
||||
ds := reg[dst].(*internal.DataSet)
|
||||
ds.AddKV(ctx, key, value)
|
||||
case OpDataSetToList:
|
||||
ds := reg[src1].(*internal.DataSet)
|
||||
reg[dst] = ds.ToList()
|
||||
case OpIter:
|
||||
input := reg[src1]
|
||||
|
||||
|
Reference in New Issue
Block a user