2018-09-18 22:42:38 +02:00
|
|
|
package values
|
|
|
|
|
|
|
|
import (
|
2018-10-05 21:17:22 +02:00
|
|
|
"encoding/binary"
|
2018-09-18 22:42:38 +02:00
|
|
|
"encoding/json"
|
2018-10-11 15:54:20 +02:00
|
|
|
"hash/fnv"
|
2018-10-14 03:07:28 +02:00
|
|
|
"sort"
|
2018-10-11 15:54:20 +02:00
|
|
|
|
2018-09-18 22:42:38 +02:00
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
ArrayPredicate = func(value core.Value, idx int) bool
|
2018-10-28 07:45:26 +02:00
|
|
|
|
|
|
|
ArraySorter = func(first, second core.Value) bool
|
|
|
|
|
|
|
|
Array struct {
|
|
|
|
items []core.Value
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func NewArray(size int) *Array {
|
2018-10-28 07:45:26 +02:00
|
|
|
return &Array{items: make([]core.Value, 0, size)}
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewArrayWith(values ...core.Value) *Array {
|
2018-10-28 07:45:26 +02:00
|
|
|
return &Array{items: values}
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) MarshalJSON() ([]byte, error) {
|
2018-10-28 07:45:26 +02:00
|
|
|
return json.Marshal(t.items)
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) Type() core.Type {
|
|
|
|
return core.ArrayType
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) String() string {
|
|
|
|
marshaled, err := t.MarshalJSON()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return "[]"
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(marshaled)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) Compare(other core.Value) int {
|
|
|
|
switch other.Type() {
|
|
|
|
case core.ArrayType:
|
2018-10-11 15:54:20 +02:00
|
|
|
other := other.(*Array)
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-11 15:54:20 +02:00
|
|
|
if t.Length() == 0 && other.Length() == 0 {
|
2018-09-18 22:42:38 +02:00
|
|
|
return 0
|
|
|
|
}
|
2018-10-11 15:54:20 +02:00
|
|
|
if t.Length() < other.Length() {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
if t.Length() > other.Length() {
|
|
|
|
return 1
|
|
|
|
}
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-11 15:54:20 +02:00
|
|
|
var res = 0
|
|
|
|
var val core.Value
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-11 15:54:20 +02:00
|
|
|
other.ForEach(func(otherVal core.Value, idx int) bool {
|
|
|
|
val = t.Get(NewInt(idx))
|
|
|
|
res = val.Compare(otherVal)
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-11 15:54:20 +02:00
|
|
|
return res == 0
|
|
|
|
})
|
2018-09-18 22:42:38 +02:00
|
|
|
|
|
|
|
return res
|
|
|
|
case core.ObjectType:
|
|
|
|
return -1
|
|
|
|
default:
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) Unwrap() interface{} {
|
|
|
|
arr := make([]interface{}, t.Length())
|
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
for idx, val := range t.items {
|
2018-09-18 22:42:38 +02:00
|
|
|
arr[idx] = val.Unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
return arr
|
|
|
|
}
|
|
|
|
|
2018-10-05 21:17:22 +02:00
|
|
|
func (t *Array) Hash() uint64 {
|
|
|
|
h := fnv.New64a()
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-05 21:17:22 +02:00
|
|
|
h.Write([]byte(t.Type().String()))
|
|
|
|
h.Write([]byte(":"))
|
|
|
|
h.Write([]byte("["))
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
endIndex := len(t.items) - 1
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
for i, el := range t.items {
|
2018-10-05 21:17:22 +02:00
|
|
|
bytes := make([]byte, 8)
|
|
|
|
binary.LittleEndian.PutUint64(bytes, el.Hash())
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-05 21:17:22 +02:00
|
|
|
h.Write(bytes)
|
|
|
|
|
|
|
|
if i != endIndex {
|
|
|
|
h.Write([]byte(","))
|
|
|
|
}
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-05 21:17:22 +02:00
|
|
|
h.Write([]byte("]"))
|
|
|
|
|
|
|
|
return h.Sum64()
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-12 17:58:08 +02:00
|
|
|
func (t *Array) Copy() core.Value {
|
2018-10-28 07:45:26 +02:00
|
|
|
c := NewArray(len(t.items))
|
2018-09-27 17:53:26 +02:00
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
for _, el := range t.items {
|
2018-09-27 17:53:26 +02:00
|
|
|
c.Push(el)
|
|
|
|
}
|
|
|
|
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2018-09-18 22:42:38 +02:00
|
|
|
func (t *Array) Length() Int {
|
2018-10-28 07:45:26 +02:00
|
|
|
return Int(len(t.items))
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) ForEach(predicate ArrayPredicate) {
|
2018-10-28 07:45:26 +02:00
|
|
|
for idx, val := range t.items {
|
2018-09-18 22:42:38 +02:00
|
|
|
if predicate(val, idx) == false {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) Get(idx Int) core.Value {
|
2018-10-28 07:45:26 +02:00
|
|
|
l := len(t.items) - 1
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-06 03:27:34 +02:00
|
|
|
if l < 0 {
|
|
|
|
return None
|
|
|
|
}
|
|
|
|
|
2018-09-18 22:42:38 +02:00
|
|
|
if int(idx) > l {
|
|
|
|
return None
|
|
|
|
}
|
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
return t.items[idx]
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) Set(idx Int, value core.Value) error {
|
2018-10-28 07:45:26 +02:00
|
|
|
last := len(t.items) - 1
|
2018-09-18 22:42:38 +02:00
|
|
|
|
|
|
|
if last >= int(idx) {
|
2018-10-28 07:45:26 +02:00
|
|
|
t.items[idx] = value
|
2018-09-18 22:42:38 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
return core.Error(core.ErrInvalidOperation, "out of bounds")
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) Push(item core.Value) {
|
2018-10-28 07:45:26 +02:00
|
|
|
t.items = append(t.items, item)
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-06 03:27:34 +02:00
|
|
|
func (t *Array) Slice(from, to Int) *Array {
|
|
|
|
length := t.Length()
|
|
|
|
|
|
|
|
if from >= length {
|
|
|
|
return NewArray(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
if to > length {
|
|
|
|
to = length
|
|
|
|
}
|
|
|
|
|
|
|
|
result := new(Array)
|
2018-10-28 07:45:26 +02:00
|
|
|
result.items = t.items[from:to]
|
2018-10-06 03:27:34 +02:00
|
|
|
|
|
|
|
return result
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
2018-09-23 03:06:19 +02:00
|
|
|
|
|
|
|
func (t *Array) IndexOf(item core.Value) Int {
|
|
|
|
res := Int(-1)
|
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
for idx, el := range t.items {
|
2018-09-23 03:06:19 +02:00
|
|
|
if el.Compare(item) == 0 {
|
|
|
|
res = Int(idx)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
|
|
|
}
|
2018-09-27 06:26:56 +02:00
|
|
|
|
|
|
|
func (t *Array) Insert(idx Int, value core.Value) {
|
2018-10-28 07:45:26 +02:00
|
|
|
t.items = append(t.items[:idx], append([]core.Value{value}, t.items[idx:]...)...)
|
2018-09-27 06:26:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) RemoveAt(idx Int) {
|
|
|
|
i := int(idx)
|
2018-10-28 07:45:26 +02:00
|
|
|
max := len(t.items) - 1
|
2018-09-27 06:26:56 +02:00
|
|
|
|
|
|
|
if i > max {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
t.items = append(t.items[:i], t.items[i+1:]...)
|
2018-09-27 06:26:56 +02:00
|
|
|
}
|
2018-10-12 17:58:08 +02:00
|
|
|
|
|
|
|
func (t *Array) Clone() core.Cloneable {
|
|
|
|
cloned := NewArray(0)
|
|
|
|
|
|
|
|
var value core.Value
|
|
|
|
for idx := NewInt(0); idx < t.Length(); idx++ {
|
|
|
|
value = t.Get(idx)
|
|
|
|
if IsCloneable(value) {
|
|
|
|
value = value.(core.Cloneable).Clone()
|
|
|
|
}
|
|
|
|
cloned.Push(value)
|
|
|
|
}
|
|
|
|
|
|
|
|
return cloned
|
|
|
|
}
|
2018-10-14 03:07:28 +02:00
|
|
|
|
|
|
|
func (t *Array) Sort() *Array {
|
2018-10-28 07:45:26 +02:00
|
|
|
return t.SortWith(func(first, second core.Value) bool {
|
|
|
|
return first.Compare(second) == -1
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Array) SortWith(sorter ArraySorter) *Array {
|
|
|
|
c := make([]core.Value, len(t.items))
|
|
|
|
copy(c, t.items)
|
2018-10-14 03:07:28 +02:00
|
|
|
|
|
|
|
sort.SliceStable(c, func(i, j int) bool {
|
2018-10-28 07:45:26 +02:00
|
|
|
return sorter(c[i], c[j])
|
2018-10-14 03:07:28 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
res := new(Array)
|
2018-10-28 07:45:26 +02:00
|
|
|
res.items = c
|
2018-10-14 03:07:28 +02:00
|
|
|
|
|
|
|
return res
|
|
|
|
}
|