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",
|
||||
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
|
||||
}
|
||||
|
|
338
builtins_test.go
338
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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
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.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) {
|
||||
|
|
Loading…
Reference in a new issue