package tengo var builtinFuncs = []*BuiltinFunction{ { Name: "len", Value: builtinLen, }, { Name: "copy", Value: builtinCopy, }, { Name: "append", Value: builtinAppend, }, { Name: "delete", Value: builtinDelete, }, { Name: "splice", Value: builtinSplice, }, { 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, }, { Name: "range", Value: builtinRange, }, } // 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(), } } } //range(start, stop[, step]) func builtinRange(args ...Object) (Object, error) { numArgs := len(args) if numArgs < 2 || numArgs > 3 { return nil, ErrWrongNumArguments } var start, stop Int step := Int{Value: 1} for i, arg := range args { v, ok := args[i].(Int) if !ok { var name string switch i { case 0: name = "start" case 1: name = "stop" case 2: name = "step" } return nil, ErrInvalidArgumentType{ Name: name, Expected: "int", Found: arg.TypeName(), } } if i == 2 && v.Value <= 0 { return nil, ErrInvalidRangeStep } switch i { case 0: start = v case 1: stop = v case 2: step = v } } return buildRange(start.Value, stop.Value, step.Value), nil } func buildRange(start, stop, step int64) *Array { array := &Array{} if start <= stop { for i := start; i < stop; i += step { array.Value = append(array.Value, Int{ Value: i, }) } } else { for i := start; i > stop; i -= step { array.Value = append(array.Value, Int{ Value: i, }) } } return array } 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(), } } } // 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 }