From 73b5e6256ed1b26828a28b0a744acdde5f7d1fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ozan=20HACIBEK=C4=B0RO=C4=9ELU?= Date: Wed, 4 Mar 2020 08:16:15 +0200 Subject: [PATCH] builtin splice (#254) * added builtin splice * fix splice * remove splice negative index --- builtins.go | 77 +++++++++++ builtins_test.go | 338 +++++++++++++++++++++++++++++++++++++++++------ docs/builtins.md | 59 +++++++++ vm_test.go | 165 ++++++++++++++++++++--- 4 files changed, 582 insertions(+), 57 deletions(-) diff --git a/builtins.go b/builtins.go index a89e72f..fcda81c 100644 --- a/builtins.go +++ b/builtins.go @@ -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 +} diff --git a/builtins_test.go b/builtins_test.go index 4f0d837..2f2e47c 100644 --- a/builtins_test.go +++ b/builtins_test.go @@ -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)) + } + }) + } +} diff --git a/docs/builtins.md b/docs/builtins.md index 23db51e..fc15833 100644 --- a/docs/builtins.md +++ b/docs/builtins.md @@ -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. diff --git a/vm_test.go b/vm_test.go index b59806e..4ffaed7 100644 --- a/vm_test.go +++ b/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.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) {