ac534053e8
* added builtin delete function and unit tests * added vm tests for builtin delete * added doc for builtin delete * update doc
534 lines
10 KiB
Go
534 lines
10 KiB
Go
package tengo
|
|
|
|
var builtinFuncs = []*BuiltinFunction{
|
|
{
|
|
Name: "len",
|
|
Value: builtinLen,
|
|
},
|
|
{
|
|
Name: "copy",
|
|
Value: builtinCopy,
|
|
},
|
|
{
|
|
Name: "append",
|
|
Value: builtinAppend,
|
|
},
|
|
{
|
|
Name: "delete",
|
|
Value: builtinDelete,
|
|
},
|
|
{
|
|
Name: "string",
|
|
Value: builtinString,
|
|
},
|
|
{
|
|
Name: "int",
|
|
Value: builtinInt,
|
|
},
|
|
{
|
|
Name: "bool",
|
|
Value: builtinBool,
|
|
},
|
|
{
|
|
Name: "float",
|
|
Value: builtinFloat,
|
|
},
|
|
{
|
|
Name: "char",
|
|
Value: builtinChar,
|
|
},
|
|
{
|
|
Name: "bytes",
|
|
Value: builtinBytes,
|
|
},
|
|
{
|
|
Name: "time",
|
|
Value: builtinTime,
|
|
},
|
|
{
|
|
Name: "is_int",
|
|
Value: builtinIsInt,
|
|
},
|
|
{
|
|
Name: "is_float",
|
|
Value: builtinIsFloat,
|
|
},
|
|
{
|
|
Name: "is_string",
|
|
Value: builtinIsString,
|
|
},
|
|
{
|
|
Name: "is_bool",
|
|
Value: builtinIsBool,
|
|
},
|
|
{
|
|
Name: "is_char",
|
|
Value: builtinIsChar,
|
|
},
|
|
{
|
|
Name: "is_bytes",
|
|
Value: builtinIsBytes,
|
|
},
|
|
{
|
|
Name: "is_array",
|
|
Value: builtinIsArray,
|
|
},
|
|
{
|
|
Name: "is_immutable_array",
|
|
Value: builtinIsImmutableArray,
|
|
},
|
|
{
|
|
Name: "is_map",
|
|
Value: builtinIsMap,
|
|
},
|
|
{
|
|
Name: "is_immutable_map",
|
|
Value: builtinIsImmutableMap,
|
|
},
|
|
{
|
|
Name: "is_iterable",
|
|
Value: builtinIsIterable,
|
|
},
|
|
{
|
|
Name: "is_time",
|
|
Value: builtinIsTime,
|
|
},
|
|
{
|
|
Name: "is_error",
|
|
Value: builtinIsError,
|
|
},
|
|
{
|
|
Name: "is_undefined",
|
|
Value: builtinIsUndefined,
|
|
},
|
|
{
|
|
Name: "is_function",
|
|
Value: builtinIsFunction,
|
|
},
|
|
{
|
|
Name: "is_callable",
|
|
Value: builtinIsCallable,
|
|
},
|
|
{
|
|
Name: "type_name",
|
|
Value: builtinTypeName,
|
|
},
|
|
{
|
|
Name: "format",
|
|
Value: builtinFormat,
|
|
},
|
|
}
|
|
|
|
// GetAllBuiltinFunctions returns all builtin function objects.
|
|
func GetAllBuiltinFunctions() []*BuiltinFunction {
|
|
return append([]*BuiltinFunction{}, builtinFuncs...)
|
|
}
|
|
|
|
func builtinTypeName(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
return &String{Value: args[0].TypeName()}, nil
|
|
}
|
|
|
|
func builtinIsString(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*String); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsInt(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Int); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsFloat(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Float); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsBool(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Bool); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsChar(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Char); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsBytes(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Bytes); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsArray(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Array); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsImmutableArray(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*ImmutableArray); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsMap(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Map); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsImmutableMap(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*ImmutableMap); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsTime(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Time); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsError(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Error); ok {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsUndefined(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if args[0] == UndefinedValue {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsFunction(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
switch args[0].(type) {
|
|
case *CompiledFunction:
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsCallable(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if args[0].CanCall() {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
func builtinIsIterable(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if args[0].CanIterate() {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
|
|
// len(obj object) => int
|
|
func builtinLen(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
switch arg := args[0].(type) {
|
|
case *Array:
|
|
return &Int{Value: int64(len(arg.Value))}, nil
|
|
case *ImmutableArray:
|
|
return &Int{Value: int64(len(arg.Value))}, nil
|
|
case *String:
|
|
return &Int{Value: int64(len(arg.Value))}, nil
|
|
case *Bytes:
|
|
return &Int{Value: int64(len(arg.Value))}, nil
|
|
case *Map:
|
|
return &Int{Value: int64(len(arg.Value))}, nil
|
|
case *ImmutableMap:
|
|
return &Int{Value: int64(len(arg.Value))}, nil
|
|
default:
|
|
return nil, ErrInvalidArgumentType{
|
|
Name: "first",
|
|
Expected: "array/string/bytes/map",
|
|
Found: arg.TypeName(),
|
|
}
|
|
}
|
|
}
|
|
|
|
func builtinFormat(args ...Object) (Object, error) {
|
|
numArgs := len(args)
|
|
if numArgs == 0 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
format, ok := args[0].(*String)
|
|
if !ok {
|
|
return nil, ErrInvalidArgumentType{
|
|
Name: "format",
|
|
Expected: "string",
|
|
Found: args[0].TypeName(),
|
|
}
|
|
}
|
|
if numArgs == 1 {
|
|
// okay to return 'format' directly as String is immutable
|
|
return format, nil
|
|
}
|
|
s, err := Format(format.Value, args[1:]...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &String{Value: s}, nil
|
|
}
|
|
|
|
func builtinCopy(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
return args[0].Copy(), nil
|
|
}
|
|
|
|
func builtinString(args ...Object) (Object, error) {
|
|
argsLen := len(args)
|
|
if !(argsLen == 1 || argsLen == 2) {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*String); ok {
|
|
return args[0], nil
|
|
}
|
|
v, ok := ToString(args[0])
|
|
if ok {
|
|
if len(v) > MaxStringLen {
|
|
return nil, ErrStringLimit
|
|
}
|
|
return &String{Value: v}, nil
|
|
}
|
|
if argsLen == 2 {
|
|
return args[1], nil
|
|
}
|
|
return UndefinedValue, nil
|
|
}
|
|
|
|
func builtinInt(args ...Object) (Object, error) {
|
|
argsLen := len(args)
|
|
if !(argsLen == 1 || argsLen == 2) {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Int); ok {
|
|
return args[0], nil
|
|
}
|
|
v, ok := ToInt64(args[0])
|
|
if ok {
|
|
return &Int{Value: v}, nil
|
|
}
|
|
if argsLen == 2 {
|
|
return args[1], nil
|
|
}
|
|
return UndefinedValue, nil
|
|
}
|
|
|
|
func builtinFloat(args ...Object) (Object, error) {
|
|
argsLen := len(args)
|
|
if !(argsLen == 1 || argsLen == 2) {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Float); ok {
|
|
return args[0], nil
|
|
}
|
|
v, ok := ToFloat64(args[0])
|
|
if ok {
|
|
return &Float{Value: v}, nil
|
|
}
|
|
if argsLen == 2 {
|
|
return args[1], nil
|
|
}
|
|
return UndefinedValue, nil
|
|
}
|
|
|
|
func builtinBool(args ...Object) (Object, error) {
|
|
if len(args) != 1 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Bool); ok {
|
|
return args[0], nil
|
|
}
|
|
v, ok := ToBool(args[0])
|
|
if ok {
|
|
if v {
|
|
return TrueValue, nil
|
|
}
|
|
return FalseValue, nil
|
|
}
|
|
return UndefinedValue, nil
|
|
}
|
|
|
|
func builtinChar(args ...Object) (Object, error) {
|
|
argsLen := len(args)
|
|
if !(argsLen == 1 || argsLen == 2) {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Char); ok {
|
|
return args[0], nil
|
|
}
|
|
v, ok := ToRune(args[0])
|
|
if ok {
|
|
return &Char{Value: v}, nil
|
|
}
|
|
if argsLen == 2 {
|
|
return args[1], nil
|
|
}
|
|
return UndefinedValue, nil
|
|
}
|
|
|
|
func builtinBytes(args ...Object) (Object, error) {
|
|
argsLen := len(args)
|
|
if !(argsLen == 1 || argsLen == 2) {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
|
|
// bytes(N) => create a new bytes with given size N
|
|
if n, ok := args[0].(*Int); ok {
|
|
if n.Value > int64(MaxBytesLen) {
|
|
return nil, ErrBytesLimit
|
|
}
|
|
return &Bytes{Value: make([]byte, int(n.Value))}, nil
|
|
}
|
|
v, ok := ToByteSlice(args[0])
|
|
if ok {
|
|
if len(v) > MaxBytesLen {
|
|
return nil, ErrBytesLimit
|
|
}
|
|
return &Bytes{Value: v}, nil
|
|
}
|
|
if argsLen == 2 {
|
|
return args[1], nil
|
|
}
|
|
return UndefinedValue, nil
|
|
}
|
|
|
|
func builtinTime(args ...Object) (Object, error) {
|
|
argsLen := len(args)
|
|
if !(argsLen == 1 || argsLen == 2) {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
if _, ok := args[0].(*Time); ok {
|
|
return args[0], nil
|
|
}
|
|
v, ok := ToTime(args[0])
|
|
if ok {
|
|
return &Time{Value: v}, nil
|
|
}
|
|
if argsLen == 2 {
|
|
return args[1], nil
|
|
}
|
|
return UndefinedValue, nil
|
|
}
|
|
|
|
// append(arr, items...)
|
|
func builtinAppend(args ...Object) (Object, error) {
|
|
if len(args) < 2 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
switch arg := args[0].(type) {
|
|
case *Array:
|
|
return &Array{Value: append(arg.Value, args[1:]...)}, nil
|
|
case *ImmutableArray:
|
|
return &Array{Value: append(arg.Value, args[1:]...)}, nil
|
|
default:
|
|
return nil, ErrInvalidArgumentType{
|
|
Name: "first",
|
|
Expected: "array",
|
|
Found: arg.TypeName(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// builtinDelete deletes Map keys
|
|
// usage: delete(map, "key")
|
|
// key must be a string
|
|
func builtinDelete(args ...Object) (Object, error) {
|
|
argsLen := len(args)
|
|
if argsLen != 2 {
|
|
return nil, ErrWrongNumArguments
|
|
}
|
|
switch arg := args[0].(type) {
|
|
case *Map:
|
|
if key, ok := args[1].(*String); ok {
|
|
delete(arg.Value, key.Value)
|
|
return UndefinedValue, nil
|
|
}
|
|
return nil, ErrInvalidArgumentType{
|
|
Name: "second",
|
|
Expected: "string",
|
|
Found: args[1].TypeName(),
|
|
}
|
|
default:
|
|
return nil, ErrInvalidArgumentType{
|
|
Name: "first",
|
|
Expected: "map",
|
|
Found: arg.TypeName(),
|
|
}
|
|
}
|
|
}
|