builtin splice (#254)
* added builtin splice * fix splice * remove splice negative index
This commit is contained in:
parent
ac534053e8
commit
73b5e6256e
4 changed files with 582 additions and 57 deletions
77
builtins.go
77
builtins.go
|
@ -17,6 +17,10 @@ var builtinFuncs = []*BuiltinFunction{
|
||||||
Name: "delete",
|
Name: "delete",
|
||||||
Value: builtinDelete,
|
Value: builtinDelete,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "splice",
|
||||||
|
Value: builtinSplice,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "string",
|
Name: "string",
|
||||||
Value: builtinString,
|
Value: builtinString,
|
||||||
|
@ -532,3 +536,76 @@ func builtinDelete(args ...Object) (Object, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// builtinSplice deletes and changes given Array, returns deleted items.
|
||||||
|
// usage:
|
||||||
|
// deleted_items := splice(array[,start[,delete_count[,item1[,item2[,...]]]])
|
||||||
|
func builtinSplice(args ...Object) (Object, error) {
|
||||||
|
argsLen := len(args)
|
||||||
|
if argsLen == 0 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
array, ok := args[0].(*Array)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrInvalidArgumentType{
|
||||||
|
Name: "first",
|
||||||
|
Expected: "array",
|
||||||
|
Found: args[0].TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arrayLen := len(array.Value)
|
||||||
|
|
||||||
|
var startIdx int
|
||||||
|
if argsLen > 1 {
|
||||||
|
arg1, ok := args[1].(*Int)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrInvalidArgumentType{
|
||||||
|
Name: "second",
|
||||||
|
Expected: "int",
|
||||||
|
Found: args[1].TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startIdx = int(arg1.Value)
|
||||||
|
if startIdx < 0 || startIdx > arrayLen {
|
||||||
|
return nil, ErrIndexOutOfBounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delCount := len(array.Value)
|
||||||
|
if argsLen > 2 {
|
||||||
|
arg2, ok := args[2].(*Int)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrInvalidArgumentType{
|
||||||
|
Name: "third",
|
||||||
|
Expected: "int",
|
||||||
|
Found: args[2].TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delCount = int(arg2.Value)
|
||||||
|
if delCount < 0 {
|
||||||
|
return nil, ErrIndexOutOfBounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if count of to be deleted items is bigger than expected, truncate it
|
||||||
|
if startIdx+delCount > arrayLen {
|
||||||
|
delCount = arrayLen - startIdx
|
||||||
|
}
|
||||||
|
// delete items
|
||||||
|
endIdx := startIdx + delCount
|
||||||
|
deleted := append([]Object{}, array.Value[startIdx:endIdx]...)
|
||||||
|
|
||||||
|
head := array.Value[:startIdx]
|
||||||
|
var items []Object
|
||||||
|
if argsLen > 3 {
|
||||||
|
items = make([]Object, 0, argsLen-3)
|
||||||
|
for i := 3; i < argsLen; i++ {
|
||||||
|
items = append(items, args[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items = append(items, array.Value[endIdx:]...)
|
||||||
|
array.Value = append(head, items...)
|
||||||
|
|
||||||
|
// return deleted items
|
||||||
|
return &Array{Value: deleted}, nil
|
||||||
|
}
|
||||||
|
|
338
builtins_test.go
338
builtins_test.go
|
@ -1,79 +1,113 @@
|
||||||
package tengo
|
package tengo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_builtinDelete(t *testing.T) {
|
func Test_builtinDelete(t *testing.T) {
|
||||||
|
var builtinDelete func(args ...tengo.Object) (tengo.Object, error)
|
||||||
|
for _, f := range tengo.GetAllBuiltinFunctions() {
|
||||||
|
if f.Name == "delete" {
|
||||||
|
builtinDelete = f.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if builtinDelete == nil {
|
||||||
|
t.Fatal("builtin delete not found")
|
||||||
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
args []Object
|
args []tengo.Object
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want Object
|
want tengo.Object
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantedErr error
|
wantedErr error
|
||||||
target interface{}
|
target interface{}
|
||||||
}{
|
}{
|
||||||
//Map
|
{name: "invalid-arg", args: args{[]tengo.Object{&tengo.String{},
|
||||||
{name: "invalid-arg", args: args{[]Object{&String{}, &String{}}}, wantErr: true,
|
&tengo.String{}}}, wantErr: true,
|
||||||
wantedErr: ErrInvalidArgumentType{Name: "first", Expected: "map", Found: "string"}},
|
wantedErr: tengo.ErrInvalidArgumentType{
|
||||||
{name: "no-args", wantErr: true, wantedErr: ErrWrongNumArguments},
|
Name: "first",
|
||||||
{name: "empty-args", args: args{[]Object{}}, wantErr: true, wantedErr: ErrWrongNumArguments},
|
Expected: "map",
|
||||||
{name: "3-args", args: args{[]Object{(*Map)(nil), (*String)(nil), (*String)(nil)}}, wantErr: true, wantedErr: ErrWrongNumArguments},
|
Found: "string"},
|
||||||
{name: "nil-map-empty-key", args: args{[]Object{&Map{}, &String{}}}, want: UndefinedValue},
|
},
|
||||||
{name: "nil-map-nonstr-key", args: args{[]Object{&Map{}, &Int{}}}, wantErr: true,
|
{name: "no-args",
|
||||||
wantedErr: ErrInvalidArgumentType{Name: "second", Expected: "string", Found: "int"}},
|
wantErr: true, wantedErr: tengo.ErrWrongNumArguments},
|
||||||
{name: "nil-map-no-key", args: args{[]Object{&Map{}}}, wantErr: true,
|
{name: "empty-args", args: args{[]tengo.Object{}}, wantErr: true,
|
||||||
wantedErr: ErrWrongNumArguments},
|
wantedErr: tengo.ErrWrongNumArguments,
|
||||||
|
},
|
||||||
|
{name: "3-args", args: args{[]tengo.Object{
|
||||||
|
(*tengo.Map)(nil), (*tengo.String)(nil), (*tengo.String)(nil)}},
|
||||||
|
wantErr: true, wantedErr: tengo.ErrWrongNumArguments,
|
||||||
|
},
|
||||||
|
{name: "nil-map-empty-key",
|
||||||
|
args: args{[]tengo.Object{&tengo.Map{}, &tengo.String{}}},
|
||||||
|
want: tengo.UndefinedValue,
|
||||||
|
},
|
||||||
|
{name: "nil-map-nonstr-key",
|
||||||
|
args: args{[]tengo.Object{
|
||||||
|
&tengo.Map{}, &tengo.Int{}}}, wantErr: true,
|
||||||
|
wantedErr: tengo.ErrInvalidArgumentType{
|
||||||
|
Name: "second", Expected: "string", Found: "int"},
|
||||||
|
},
|
||||||
|
{name: "nil-map-no-key",
|
||||||
|
args: args{[]tengo.Object{&tengo.Map{}}}, wantErr: true,
|
||||||
|
wantedErr: tengo.ErrWrongNumArguments,
|
||||||
|
},
|
||||||
{name: "map-missing-key",
|
{name: "map-missing-key",
|
||||||
args: args{
|
args: args{
|
||||||
[]Object{
|
[]tengo.Object{
|
||||||
&Map{Value: map[string]Object{
|
&tengo.Map{Value: map[string]tengo.Object{
|
||||||
"key": &String{Value: "value"},
|
"key": &tengo.String{Value: "value"},
|
||||||
}},
|
}},
|
||||||
&String{Value: "key1"},
|
&tengo.String{Value: "key1"}}},
|
||||||
}},
|
want: tengo.UndefinedValue,
|
||||||
want: UndefinedValue,
|
target: &tengo.Map{
|
||||||
target: &Map{Value: map[string]Object{"key": &String{Value: "value"}}},
|
Value: map[string]tengo.Object{
|
||||||
|
"key": &tengo.String{
|
||||||
|
Value: "value"}}},
|
||||||
},
|
},
|
||||||
{name: "map-emptied",
|
{name: "map-emptied",
|
||||||
args: args{
|
args: args{
|
||||||
[]Object{
|
[]tengo.Object{
|
||||||
&Map{Value: map[string]Object{
|
&tengo.Map{Value: map[string]tengo.Object{
|
||||||
"key": &String{Value: "value"},
|
"key": &tengo.String{Value: "value"},
|
||||||
}},
|
}},
|
||||||
&String{Value: "key"},
|
&tengo.String{Value: "key"}}},
|
||||||
}},
|
want: tengo.UndefinedValue,
|
||||||
want: UndefinedValue,
|
target: &tengo.Map{Value: map[string]tengo.Object{}},
|
||||||
target: &Map{Value: map[string]Object{}},
|
|
||||||
},
|
},
|
||||||
{name: "map-multi-keys",
|
{name: "map-multi-keys",
|
||||||
args: args{
|
args: args{
|
||||||
[]Object{
|
[]tengo.Object{
|
||||||
&Map{Value: map[string]Object{
|
&tengo.Map{Value: map[string]tengo.Object{
|
||||||
"key1": &String{Value: "value1"},
|
"key1": &tengo.String{Value: "value1"},
|
||||||
"key2": &Int{Value: 10},
|
"key2": &tengo.Int{Value: 10},
|
||||||
}},
|
}},
|
||||||
&String{Value: "key1"},
|
&tengo.String{Value: "key1"}}},
|
||||||
}},
|
want: tengo.UndefinedValue,
|
||||||
want: UndefinedValue,
|
target: &tengo.Map{Value: map[string]tengo.Object{
|
||||||
target: &Map{Value: map[string]Object{"key2": &Int{Value: 10}}},
|
"key2": &tengo.Int{Value: 10}}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := builtinDelete(tt.args.args...)
|
got, err := builtinDelete(tt.args.args...)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("builtinDelete() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("builtinDelete() error = %v, wantErr %v",
|
||||||
|
err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if tt.wantErr && !errors.Is(err, tt.wantedErr) {
|
if tt.wantErr && !errors.Is(err, tt.wantedErr) {
|
||||||
if err.Error() != tt.wantedErr.Error() {
|
if err.Error() != tt.wantedErr.Error() {
|
||||||
t.Errorf("builtinDelete() error = %v, wantedErr %v", err, tt.wantedErr)
|
t.Errorf("builtinDelete() error = %v, wantedErr %v",
|
||||||
|
err, tt.wantedErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,15 +117,237 @@ func Test_builtinDelete(t *testing.T) {
|
||||||
}
|
}
|
||||||
if !tt.wantErr && tt.target != nil {
|
if !tt.wantErr && tt.target != nil {
|
||||||
switch v := tt.args.args[0].(type) {
|
switch v := tt.args.args[0].(type) {
|
||||||
case *Map, *Array:
|
case *tengo.Map, *tengo.Array:
|
||||||
if !reflect.DeepEqual(tt.target, tt.args.args[0]) {
|
if !reflect.DeepEqual(tt.target, tt.args.args[0]) {
|
||||||
t.Errorf("builtinDelete() objects are not equal got: %+v, want: %+v", tt.args.args[0], tt.target)
|
t.Errorf("builtinDelete() objects are not equal "+
|
||||||
|
"got: %+v, want: %+v", tt.args.args[0], tt.target)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
t.Errorf("builtinDelete() unsuporrted arg[0] type %s", v.TypeName())
|
t.Errorf("builtinDelete() unsuporrted arg[0] type %s",
|
||||||
|
v.TypeName())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_builtinSplice(t *testing.T) {
|
||||||
|
var builtinSplice func(args ...tengo.Object) (tengo.Object, error)
|
||||||
|
for _, f := range tengo.GetAllBuiltinFunctions() {
|
||||||
|
if f.Name == "splice" {
|
||||||
|
builtinSplice = f.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if builtinSplice == nil {
|
||||||
|
t.Fatal("builtin splice not found")
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []tengo.Object
|
||||||
|
deleted tengo.Object
|
||||||
|
Array *tengo.Array
|
||||||
|
wantErr bool
|
||||||
|
wantedErr error
|
||||||
|
}{
|
||||||
|
{name: "no args", args: []tengo.Object{}, wantErr: true,
|
||||||
|
wantedErr: tengo.ErrWrongNumArguments,
|
||||||
|
},
|
||||||
|
{name: "invalid args", args: []tengo.Object{&tengo.Map{}},
|
||||||
|
wantErr: true,
|
||||||
|
wantedErr: tengo.ErrInvalidArgumentType{
|
||||||
|
Name: "first", Expected: "array", Found: "map"},
|
||||||
|
},
|
||||||
|
{name: "invalid args",
|
||||||
|
args: []tengo.Object{&tengo.Array{}, &tengo.String{}},
|
||||||
|
wantErr: true,
|
||||||
|
wantedErr: tengo.ErrInvalidArgumentType{
|
||||||
|
Name: "second", Expected: "int", Found: "string"},
|
||||||
|
},
|
||||||
|
{name: "negative index",
|
||||||
|
args: []tengo.Object{&tengo.Array{}, &tengo.Int{Value: -1}},
|
||||||
|
wantErr: true,
|
||||||
|
wantedErr: tengo.ErrIndexOutOfBounds},
|
||||||
|
{name: "non int count",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{}, &tengo.Int{Value: 0},
|
||||||
|
&tengo.String{Value: ""}},
|
||||||
|
wantErr: true,
|
||||||
|
wantedErr: tengo.ErrInvalidArgumentType{
|
||||||
|
Name: "third", Expected: "int", Found: "string"},
|
||||||
|
},
|
||||||
|
{name: "negative count",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: -1}},
|
||||||
|
wantErr: true,
|
||||||
|
wantedErr: tengo.ErrIndexOutOfBounds,
|
||||||
|
},
|
||||||
|
{name: "insert with zero count",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.String{Value: "b"}},
|
||||||
|
deleted: &tengo.Array{Value: []tengo.Object{}},
|
||||||
|
Array: &tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.String{Value: "b"},
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
},
|
||||||
|
{name: "insert",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.String{Value: "c"},
|
||||||
|
&tengo.String{Value: "d"}},
|
||||||
|
deleted: &tengo.Array{Value: []tengo.Object{}},
|
||||||
|
Array: &tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.String{Value: "c"},
|
||||||
|
&tengo.String{Value: "d"},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
},
|
||||||
|
{name: "insert with zero count",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.String{Value: "c"},
|
||||||
|
&tengo.String{Value: "d"}},
|
||||||
|
deleted: &tengo.Array{Value: []tengo.Object{}},
|
||||||
|
Array: &tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.String{Value: "c"},
|
||||||
|
&tengo.String{Value: "d"},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
},
|
||||||
|
{name: "insert with delete",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.String{Value: "c"},
|
||||||
|
&tengo.String{Value: "d"}},
|
||||||
|
deleted: &tengo.Array{
|
||||||
|
Value: []tengo.Object{&tengo.Int{Value: 1}}},
|
||||||
|
Array: &tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.String{Value: "c"},
|
||||||
|
&tengo.String{Value: "d"},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
},
|
||||||
|
{name: "insert with delete multi",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2},
|
||||||
|
&tengo.String{Value: "c"},
|
||||||
|
&tengo.String{Value: "d"}},
|
||||||
|
deleted: &tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
Array: &tengo.Array{
|
||||||
|
Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.String{Value: "c"},
|
||||||
|
&tengo.String{Value: "d"}}},
|
||||||
|
},
|
||||||
|
{name: "delete all with positive count",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 3}},
|
||||||
|
deleted: &tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
Array: &tengo.Array{Value: []tengo.Object{}},
|
||||||
|
},
|
||||||
|
{name: "delete all with big count",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 5}},
|
||||||
|
deleted: &tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
Array: &tengo.Array{Value: []tengo.Object{}},
|
||||||
|
},
|
||||||
|
{name: "nothing2",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}}},
|
||||||
|
Array: &tengo.Array{Value: []tengo.Object{}},
|
||||||
|
deleted: &tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
},
|
||||||
|
{name: "pop without count",
|
||||||
|
args: []tengo.Object{
|
||||||
|
&tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0},
|
||||||
|
&tengo.Int{Value: 1},
|
||||||
|
&tengo.Int{Value: 2}}},
|
||||||
|
&tengo.Int{Value: 2}},
|
||||||
|
deleted: &tengo.Array{Value: []tengo.Object{&tengo.Int{Value: 2}}},
|
||||||
|
Array: &tengo.Array{Value: []tengo.Object{
|
||||||
|
&tengo.Int{Value: 0}, &tengo.Int{Value: 1}}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := builtinSplice(tt.args...)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("builtinSplice() error = %v, wantErr %v",
|
||||||
|
err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.deleted) {
|
||||||
|
t.Errorf("builtinSplice() = %v, want %v", got, tt.deleted)
|
||||||
|
}
|
||||||
|
if tt.wantErr && tt.wantedErr.Error() != err.Error() {
|
||||||
|
t.Errorf("builtinSplice() error = %v, wantedErr %v",
|
||||||
|
err, tt.wantedErr)
|
||||||
|
}
|
||||||
|
if tt.Array != nil && !reflect.DeepEqual(tt.Array, tt.args[0]) {
|
||||||
|
t.Errorf("builtinSplice() arrays are not equal expected"+
|
||||||
|
" %s, got %s", tt.Array, tt.args[0].(*tengo.Array))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -67,6 +67,65 @@ delete({}) // runtime error, second argument is missing
|
||||||
delete({}, 1) // runtime error, second argument must be a string type
|
delete({}, 1) // runtime error, second argument must be a string type
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## splice
|
||||||
|
|
||||||
|
Deletes and/or changes the contents of a given array and returns
|
||||||
|
deleted items as a new array. `splice` is similar to
|
||||||
|
JS `Array.prototype.splice()` except splice is a builtin function and
|
||||||
|
first argument must an array. First argument must be an array, and
|
||||||
|
if second and third arguments are provided those must be integers
|
||||||
|
otherwise runtime error is returned.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
`deleted_items := splice(array[, start[, delete_count[, item1[, item2[, ...]]]])`
|
||||||
|
|
||||||
|
```golang
|
||||||
|
v := [1, 2, 3]
|
||||||
|
items := splice(v, 0) // items == [1, 2, 3], v == []
|
||||||
|
```
|
||||||
|
|
||||||
|
```golang
|
||||||
|
v := [1, 2, 3]
|
||||||
|
items := splice(v, 1) // items == [2, 3], v == [1]
|
||||||
|
```
|
||||||
|
|
||||||
|
```golang
|
||||||
|
v := [1, 2, 3]
|
||||||
|
items := splice(v, 0, 1) // items == [1], v == [2, 3]
|
||||||
|
```
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// deleting
|
||||||
|
v := ["a", "b", "c"]
|
||||||
|
items := splice(v, 1, 2) // items == ["b", "c"], v == ["a"]
|
||||||
|
// splice(v, 1, 3) or splice(v, 1, 99) has same effect for this example
|
||||||
|
```
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// appending
|
||||||
|
v := ["a", "b", "c"]
|
||||||
|
items := splice(v, 3, 0, "d", "e") // items == [], v == ["a", "b", "c", "d", "e"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// replacing
|
||||||
|
v := ["a", "b", "c"]
|
||||||
|
items := splice(v, 2, 1, "d") // items == ["c"], v == ["a", "b", "d"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// inserting
|
||||||
|
v := ["a", "b", "c"]
|
||||||
|
items := splice(v, 0, 0, "d", "e") // items == [], v == ["d", "e", "a", "b", "c"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// deleting and inserting
|
||||||
|
v := ["a", "b", "c"]
|
||||||
|
items := splice(v, 1, 1, "d", "e") // items == ["b"], v == ["a", "d", "e", "c"]
|
||||||
|
```
|
||||||
|
|
||||||
## type_name
|
## type_name
|
||||||
|
|
||||||
Returns the type_name of an object.
|
Returns the type_name of an object.
|
||||||
|
|
165
vm_test.go
165
vm_test.go
|
@ -735,31 +735,164 @@ func TestBuiltinFunction(t *testing.T) {
|
||||||
expectError(t, `delete(1, 1)`, nil, `invalid type for argument 'first'`)
|
expectError(t, `delete(1, 1)`, nil, `invalid type for argument 'first'`)
|
||||||
expectError(t, `delete(1.0, 1)`, nil, `invalid type for argument 'first'`)
|
expectError(t, `delete(1.0, 1)`, nil, `invalid type for argument 'first'`)
|
||||||
expectError(t, `delete("str", 1)`, nil, `invalid type for argument 'first'`)
|
expectError(t, `delete("str", 1)`, nil, `invalid type for argument 'first'`)
|
||||||
expectError(t, `delete(bytes("str"), 1)`, nil, `invalid type for argument 'first'`)
|
expectError(t, `delete(bytes("str"), 1)`, nil,
|
||||||
expectError(t, `delete(error("err"), 1)`, nil, `invalid type for argument 'first'`)
|
`invalid type for argument 'first'`)
|
||||||
|
expectError(t, `delete(error("err"), 1)`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
expectError(t, `delete(true, 1)`, nil, `invalid type for argument 'first'`)
|
expectError(t, `delete(true, 1)`, nil, `invalid type for argument 'first'`)
|
||||||
expectError(t, `delete(char('c'), 1)`, nil, `invalid type for argument 'first'`)
|
expectError(t, `delete(char('c'), 1)`, nil,
|
||||||
expectError(t, `delete(undefined, 1)`, nil, `invalid type for argument 'first'`)
|
`invalid type for argument 'first'`)
|
||||||
expectError(t, `delete(time(1257894000), 1)`, nil, `invalid type for argument 'first'`)
|
expectError(t, `delete(undefined, 1)`, nil,
|
||||||
expectError(t, `delete(immutable({}), "key")`, nil, `invalid type for argument 'first'`)
|
`invalid type for argument 'first'`)
|
||||||
expectError(t, `delete(immutable([]), "")`, nil, `invalid type for argument 'first'`)
|
expectError(t, `delete(time(1257894000), 1)`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
|
expectError(t, `delete(immutable({}), "key")`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
|
expectError(t, `delete(immutable([]), "")`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
expectError(t, `delete([], "")`, nil, `invalid type for argument 'first'`)
|
expectError(t, `delete([], "")`, nil, `invalid type for argument 'first'`)
|
||||||
expectError(t, `delete({}, 1)`, nil, `invalid type for argument 'second'`)
|
expectError(t, `delete({}, 1)`, nil, `invalid type for argument 'second'`)
|
||||||
expectError(t, `delete({}, 1.0)`, nil, `invalid type for argument 'second'`)
|
expectError(t, `delete({}, 1.0)`, nil, `invalid type for argument 'second'`)
|
||||||
expectError(t, `delete({}, undefined)`, nil, `invalid type for argument 'second'`)
|
expectError(t, `delete({}, undefined)`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
expectError(t, `delete({}, [])`, nil, `invalid type for argument 'second'`)
|
expectError(t, `delete({}, [])`, nil, `invalid type for argument 'second'`)
|
||||||
expectError(t, `delete({}, {})`, nil, `invalid type for argument 'second'`)
|
expectError(t, `delete({}, {})`, nil, `invalid type for argument 'second'`)
|
||||||
expectError(t, `delete({}, error("err"))`, nil, `invalid type for argument 'second'`)
|
expectError(t, `delete({}, error("err"))`, nil,
|
||||||
expectError(t, `delete({}, bytes("str"))`, nil, `invalid type for argument 'second'`)
|
`invalid type for argument 'second'`)
|
||||||
expectError(t, `delete({}, char(35))`, nil, `invalid type for argument 'second'`)
|
expectError(t, `delete({}, bytes("str"))`, nil,
|
||||||
expectError(t, `delete({}, time(1257894000))`, nil, `invalid type for argument 'second'`)
|
`invalid type for argument 'second'`)
|
||||||
expectError(t, `delete({}, immutable({}))`, nil, `invalid type for argument 'second'`)
|
expectError(t, `delete({}, char(35))`, nil,
|
||||||
expectError(t, `delete({}, immutable([]))`, nil, `invalid type for argument 'second'`)
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `delete({}, time(1257894000))`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `delete({}, immutable({}))`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `delete({}, immutable([]))`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
|
||||||
expectRun(t, `out = delete({}, "")`, nil, tengo.UndefinedValue)
|
expectRun(t, `out = delete({}, "")`, nil, tengo.UndefinedValue)
|
||||||
expectRun(t, `out = {key1: 1}; delete(out, "key1")`, nil, MAP{})
|
expectRun(t, `out = {key1: 1}; delete(out, "key1")`, nil, MAP{})
|
||||||
expectRun(t, `out = {key1: 1, key2: "2"}; delete(out, "key1")`, nil, MAP{"key2": "2"})
|
expectRun(t, `out = {key1: 1, key2: "2"}; delete(out, "key1")`, nil,
|
||||||
expectRun(t, `out = [1, "2", {a: "b", c: 10}]; delete(out[2], "c")`, nil, ARR{1, "2", MAP{"a": "b"}})
|
MAP{"key2": "2"})
|
||||||
|
expectRun(t, `out = [1, "2", {a: "b", c: 10}]; delete(out[2], "c")`, nil,
|
||||||
|
ARR{1, "2", MAP{"a": "b"}})
|
||||||
|
|
||||||
|
// splice
|
||||||
|
expectError(t, `splice()`, nil, tengo.ErrWrongNumArguments.Error())
|
||||||
|
expectError(t, `splice(1)`, nil, `invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice(1.0)`, nil, `invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice("str")`, nil, `invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice(bytes("str"))`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice(error("err"))`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice(true)`, nil, `invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice(char('c'))`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice(undefined)`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice(time(1257894000))`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice(immutable({}))`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice(immutable([]))`, nil,
|
||||||
|
`invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice({})`, nil, `invalid type for argument 'first'`)
|
||||||
|
expectError(t, `splice([], 1.0)`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], "str")`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], bytes("str"))`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], error("error"))`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], false)`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], char('d'))`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], undefined)`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], time(0))`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], [])`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], {})`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], immutable([]))`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], immutable({}))`, nil,
|
||||||
|
`invalid type for argument 'second'`)
|
||||||
|
expectError(t, `splice([], 0, 1.0)`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, "string")`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, bytes("string"))`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, error("string"))`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, true)`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, char('f'))`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, undefined)`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, time(0))`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, [])`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, {})`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, immutable([]))`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 0, immutable({}))`, nil,
|
||||||
|
`invalid type for argument 'third'`)
|
||||||
|
expectError(t, `splice([], 1)`, nil, tengo.ErrIndexOutOfBounds.Error())
|
||||||
|
expectError(t, `splice([1, 2, 3], 0, -1)`, nil,
|
||||||
|
tengo.ErrIndexOutOfBounds.Error())
|
||||||
|
expectError(t, `splice([1, 2, 3], 99, 0, "a", "b")`, nil,
|
||||||
|
tengo.ErrIndexOutOfBounds.Error())
|
||||||
|
expectRun(t, `out = []; splice(out)`, nil, ARR{})
|
||||||
|
expectRun(t, `out = ["a"]; splice(out, 1)`, nil, ARR{"a"})
|
||||||
|
expectRun(t, `out = ["a"]; out = splice(out, 1)`, nil, ARR{})
|
||||||
|
expectRun(t, `out = [1, 2, 3]; splice(out, 0, 1)`, nil, ARR{2, 3})
|
||||||
|
expectRun(t, `out = [1, 2, 3]; out = splice(out, 0, 1)`, nil, ARR{1})
|
||||||
|
expectRun(t, `out = [1, 2, 3]; splice(out, 0, 0, "a", "b")`, nil,
|
||||||
|
ARR{"a", "b", 1, 2, 3})
|
||||||
|
expectRun(t, `out = [1, 2, 3]; out = splice(out, 0, 0, "a", "b")`, nil,
|
||||||
|
ARR{})
|
||||||
|
expectRun(t, `out = [1, 2, 3]; splice(out, 1, 0, "a", "b")`, nil,
|
||||||
|
ARR{1, "a", "b", 2, 3})
|
||||||
|
expectRun(t, `out = [1, 2, 3]; out = splice(out, 1, 0, "a", "b")`, nil,
|
||||||
|
ARR{})
|
||||||
|
expectRun(t, `out = [1, 2, 3]; splice(out, 1, 0, "a", "b")`, nil,
|
||||||
|
ARR{1, "a", "b", 2, 3})
|
||||||
|
expectRun(t, `out = [1, 2, 3]; splice(out, 2, 0, "a", "b")`, nil,
|
||||||
|
ARR{1, 2, "a", "b", 3})
|
||||||
|
expectRun(t, `out = [1, 2, 3]; splice(out, 3, 0, "a", "b")`, nil,
|
||||||
|
ARR{1, 2, 3, "a", "b"})
|
||||||
|
expectRun(t, `array := [1, 2, 3]; deleted := splice(array, 1, 1, "a", "b");
|
||||||
|
out = [deleted, array]`, nil, ARR{ARR{2}, ARR{1, "a", "b", 3}})
|
||||||
|
expectRun(t, `array := [1, 2, 3]; deleted := splice(array, 1);
|
||||||
|
out = [deleted, array]`, nil, ARR{ARR{2, 3}, ARR{1}})
|
||||||
|
expectRun(t, `out = []; splice(out, 0, 0, "a", "b")`, nil, ARR{"a", "b"})
|
||||||
|
expectRun(t, `out = []; splice(out, 0, 1, "a", "b")`, nil, ARR{"a", "b"})
|
||||||
|
expectRun(t, `out = []; out = splice(out, 0, 0, "a", "b")`, nil, ARR{})
|
||||||
|
expectRun(t, `out = splice(splice([1, 2, 3], 0, 3), 1, 3)`, nil, ARR{2, 3})
|
||||||
|
// splice doc examples
|
||||||
|
expectRun(t, `v := [1, 2, 3]; deleted := splice(v, 0);
|
||||||
|
out = [deleted, v]`, nil, ARR{ARR{1, 2, 3}, ARR{}})
|
||||||
|
expectRun(t, `v := [1, 2, 3]; deleted := splice(v, 1);
|
||||||
|
out = [deleted, v]`, nil, ARR{ARR{2, 3}, ARR{1}})
|
||||||
|
expectRun(t, `v := [1, 2, 3]; deleted := splice(v, 0, 1);
|
||||||
|
out = [deleted, v]`, nil, ARR{ARR{1}, ARR{2, 3}})
|
||||||
|
expectRun(t, `v := ["a", "b", "c"]; deleted := splice(v, 1, 2);
|
||||||
|
out = [deleted, v]`, nil, ARR{ARR{"b", "c"}, ARR{"a"}})
|
||||||
|
expectRun(t, `v := ["a", "b", "c"]; deleted := splice(v, 2, 1, "d");
|
||||||
|
out = [deleted, v]`, nil, ARR{ARR{"c"}, ARR{"a", "b", "d"}})
|
||||||
|
expectRun(t, `v := ["a", "b", "c"]; deleted := splice(v, 0, 0, "d", "e");
|
||||||
|
out = [deleted, v]`, nil, ARR{ARR{}, ARR{"d", "e", "a", "b", "c"}})
|
||||||
|
expectRun(t, `v := ["a", "b", "c"]; deleted := splice(v, 1, 1, "d", "e");
|
||||||
|
out = [deleted, v]`, nil, ARR{ARR{"b"}, ARR{"a", "d", "e", "c"}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBytesN(t *testing.T) {
|
func TestBytesN(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue