builtin splice (#254)

* added builtin splice

* fix splice

* remove splice negative index
This commit is contained in:
Ozan HACIBEKİROĞLU 2020-03-04 08:16:15 +02:00 committed by GitHub
parent ac534053e8
commit 73b5e6256e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 582 additions and 57 deletions

View file

@ -17,6 +17,10 @@ var builtinFuncs = []*BuiltinFunction{
Name: "delete",
Value: builtinDelete,
},
{
Name: "splice",
Value: builtinSplice,
},
{
Name: "string",
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
}

View file

@ -1,79 +1,113 @@
package tengo
package tengo_test
import (
"errors"
"reflect"
"testing"
"github.com/d5/tengo/v2"
)
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 {
args []Object
args []tengo.Object
}
tests := []struct {
name string
args args
want Object
want tengo.Object
wantErr bool
wantedErr error
target interface{}
}{
//Map
{name: "invalid-arg", args: args{[]Object{&String{}, &String{}}}, wantErr: true,
wantedErr: ErrInvalidArgumentType{Name: "first", Expected: "map", Found: "string"}},
{name: "no-args", wantErr: true, wantedErr: ErrWrongNumArguments},
{name: "empty-args", args: args{[]Object{}}, wantErr: true, wantedErr: ErrWrongNumArguments},
{name: "3-args", args: args{[]Object{(*Map)(nil), (*String)(nil), (*String)(nil)}}, wantErr: true, wantedErr: ErrWrongNumArguments},
{name: "nil-map-empty-key", args: args{[]Object{&Map{}, &String{}}}, want: UndefinedValue},
{name: "nil-map-nonstr-key", args: args{[]Object{&Map{}, &Int{}}}, wantErr: true,
wantedErr: ErrInvalidArgumentType{Name: "second", Expected: "string", Found: "int"}},
{name: "nil-map-no-key", args: args{[]Object{&Map{}}}, wantErr: true,
wantedErr: ErrWrongNumArguments},
{name: "invalid-arg", args: args{[]tengo.Object{&tengo.String{},
&tengo.String{}}}, wantErr: true,
wantedErr: tengo.ErrInvalidArgumentType{
Name: "first",
Expected: "map",
Found: "string"},
},
{name: "no-args",
wantErr: true, wantedErr: tengo.ErrWrongNumArguments},
{name: "empty-args", args: args{[]tengo.Object{}}, wantErr: true,
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",
args: args{
[]Object{
&Map{Value: map[string]Object{
"key": &String{Value: "value"},
[]tengo.Object{
&tengo.Map{Value: map[string]tengo.Object{
"key": &tengo.String{Value: "value"},
}},
&String{Value: "key1"},
}},
want: UndefinedValue,
target: &Map{Value: map[string]Object{"key": &String{Value: "value"}}},
&tengo.String{Value: "key1"}}},
want: tengo.UndefinedValue,
target: &tengo.Map{
Value: map[string]tengo.Object{
"key": &tengo.String{
Value: "value"}}},
},
{name: "map-emptied",
args: args{
[]Object{
&Map{Value: map[string]Object{
"key": &String{Value: "value"},
[]tengo.Object{
&tengo.Map{Value: map[string]tengo.Object{
"key": &tengo.String{Value: "value"},
}},
&String{Value: "key"},
}},
want: UndefinedValue,
target: &Map{Value: map[string]Object{}},
&tengo.String{Value: "key"}}},
want: tengo.UndefinedValue,
target: &tengo.Map{Value: map[string]tengo.Object{}},
},
{name: "map-multi-keys",
args: args{
[]Object{
&Map{Value: map[string]Object{
"key1": &String{Value: "value1"},
"key2": &Int{Value: 10},
[]tengo.Object{
&tengo.Map{Value: map[string]tengo.Object{
"key1": &tengo.String{Value: "value1"},
"key2": &tengo.Int{Value: 10},
}},
&String{Value: "key1"},
}},
want: UndefinedValue,
target: &Map{Value: map[string]Object{"key2": &Int{Value: 10}}},
&tengo.String{Value: "key1"}}},
want: tengo.UndefinedValue,
target: &tengo.Map{Value: map[string]tengo.Object{
"key2": &tengo.Int{Value: 10}}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := builtinDelete(tt.args.args...)
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
}
if tt.wantErr && !errors.Is(err, tt.wantedErr) {
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
}
}
@ -83,15 +117,237 @@ func Test_builtinDelete(t *testing.T) {
}
if !tt.wantErr && tt.target != nil {
switch v := tt.args.args[0].(type) {
case *Map, *Array:
case *tengo.Map, *tengo.Array:
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:
t.Errorf("builtinDelete() unsuporrted arg[0] type %s", v.TypeName())
t.Errorf("builtinDelete() unsuporrted arg[0] type %s",
v.TypeName())
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))
}
})
}
}

View file

@ -67,6 +67,65 @@ delete({}) // runtime error, second argument is missing
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
Returns the type_name of an object.

View file

@ -735,31 +735,164 @@ func TestBuiltinFunction(t *testing.T) {
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("str", 1)`, nil, `invalid type for argument 'first'`)
expectError(t, `delete(bytes("str"), 1)`, nil, `invalid type for argument 'first'`)
expectError(t, `delete(error("err"), 1)`, nil, `invalid type for argument 'first'`)
expectError(t, `delete(bytes("str"), 1)`, nil,
`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(char('c'), 1)`, nil, `invalid type for argument 'first'`)
expectError(t, `delete(undefined, 1)`, 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(char('c'), 1)`, nil,
`invalid type for argument 'first'`)
expectError(t, `delete(undefined, 1)`, 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({}, 1)`, 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({}, error("err"))`, nil, `invalid type for argument 'second'`)
expectError(t, `delete({}, bytes("str"))`, nil, `invalid type for argument 'second'`)
expectError(t, `delete({}, char(35))`, nil, `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'`)
expectError(t, `delete({}, error("err"))`, nil,
`invalid type for argument 'second'`)
expectError(t, `delete({}, bytes("str"))`, nil,
`invalid type for argument 'second'`)
expectError(t, `delete({}, char(35))`, nil,
`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 = {key1: 1}; delete(out, "key1")`, nil, MAP{})
expectRun(t, `out = {key1: 1, key2: "2"}; delete(out, "key1")`, nil, MAP{"key2": "2"})
expectRun(t, `out = [1, "2", {a: "b", c: 10}]; delete(out[2], "c")`, nil, ARR{1, "2", MAP{"a": "b"}})
expectRun(t, `out = {key1: 1, key2: "2"}; delete(out, "key1")`, nil,
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) {