From 9d84d6dd643c4017074e81465671cd9b25f9539a Mon Sep 17 00:00:00 2001 From: Wim Date: Thu, 9 Jan 2020 21:52:19 +0100 Subject: Update to tengo v2 (#976) --- vendor/github.com/d5/tengo/.gitignore | 1 - vendor/github.com/d5/tengo/.goreleaser.yml | 29 - vendor/github.com/d5/tengo/.travis.yml | 17 - vendor/github.com/d5/tengo/LICENSE | 21 - vendor/github.com/d5/tengo/Makefile | 14 - vendor/github.com/d5/tengo/README.md | 77 - .../github.com/d5/tengo/compiler/ast/array_lit.go | 35 - .../d5/tengo/compiler/ast/assign_stmt.go | 40 - vendor/github.com/d5/tengo/compiler/ast/ast.go | 5 - .../github.com/d5/tengo/compiler/ast/bad_expr.go | 25 - .../github.com/d5/tengo/compiler/ast/bad_stmt.go | 25 - .../d5/tengo/compiler/ast/binary_expr.go | 30 - .../github.com/d5/tengo/compiler/ast/block_stmt.go | 35 - .../github.com/d5/tengo/compiler/ast/bool_lit.go | 26 - .../d5/tengo/compiler/ast/branch_stmt.go | 38 - .../github.com/d5/tengo/compiler/ast/call_expr.go | 36 - .../github.com/d5/tengo/compiler/ast/char_lit.go | 26 - .../github.com/d5/tengo/compiler/ast/cond_expr.go | 30 - .../github.com/d5/tengo/compiler/ast/empty_stmt.go | 29 - .../github.com/d5/tengo/compiler/ast/error_expr.go | 29 - .../d5/tengo/compiler/ast/export_stmt.go | 27 - vendor/github.com/d5/tengo/compiler/ast/expr.go | 7 - .../github.com/d5/tengo/compiler/ast/expr_stmt.go | 24 - vendor/github.com/d5/tengo/compiler/ast/file.go | 32 - .../github.com/d5/tengo/compiler/ast/float_lit.go | 26 - .../d5/tengo/compiler/ast/for_in_stmt.go | 32 - .../github.com/d5/tengo/compiler/ast/for_stmt.go | 43 - .../github.com/d5/tengo/compiler/ast/func_lit.go | 25 - .../github.com/d5/tengo/compiler/ast/func_type.go | 25 - vendor/github.com/d5/tengo/compiler/ast/ident.go | 29 - .../github.com/d5/tengo/compiler/ast/ident_list.go | 63 - vendor/github.com/d5/tengo/compiler/ast/if_stmt.go | 40 - .../d5/tengo/compiler/ast/immutable_expr.go | 29 - .../d5/tengo/compiler/ast/import_expr.go | 29 - .../d5/tengo/compiler/ast/inc_dec_stmt.go | 29 - .../github.com/d5/tengo/compiler/ast/index_expr.go | 32 - vendor/github.com/d5/tengo/compiler/ast/int_lit.go | 26 - .../d5/tengo/compiler/ast/map_element_lit.go | 27 - vendor/github.com/d5/tengo/compiler/ast/map_lit.go | 35 - vendor/github.com/d5/tengo/compiler/ast/node.go | 13 - .../github.com/d5/tengo/compiler/ast/paren_expr.go | 26 - .../d5/tengo/compiler/ast/return_stmt.go | 35 - .../d5/tengo/compiler/ast/selector_expr.go | 25 - .../github.com/d5/tengo/compiler/ast/slice_expr.go | 36 - vendor/github.com/d5/tengo/compiler/ast/stmt.go | 7 - .../github.com/d5/tengo/compiler/ast/string_lit.go | 26 - .../github.com/d5/tengo/compiler/ast/unary_expr.go | 29 - .../d5/tengo/compiler/ast/undefined_lit.go | 24 - vendor/github.com/d5/tengo/compiler/bytecode.go | 90 -- .../d5/tengo/compiler/bytecode_decode.go | 97 -- .../d5/tengo/compiler/bytecode_optimize.go | 129 -- .../d5/tengo/compiler/compilation_scope.go | 11 - vendor/github.com/d5/tengo/compiler/compiler.go | 846 ----------- .../d5/tengo/compiler/compiler_assign.go | 133 -- .../github.com/d5/tengo/compiler/compiler_for.go | 181 --- .../d5/tengo/compiler/compiler_logical.go | 30 - .../github.com/d5/tengo/compiler/compiler_loops.go | 31 - .../d5/tengo/compiler/compiler_module.go | 79 - .../d5/tengo/compiler/compiler_scopes.go | 43 - .../d5/tengo/compiler/emitted_instruction.go | 8 - vendor/github.com/d5/tengo/compiler/error.go | 20 - .../github.com/d5/tengo/compiler/instructions.go | 72 - vendor/github.com/d5/tengo/compiler/loop.go | 8 - .../github.com/d5/tengo/compiler/module_loader.go | 4 - vendor/github.com/d5/tengo/compiler/opcodes.go | 155 -- .../github.com/d5/tengo/compiler/parser/error.go | 21 - .../d5/tengo/compiler/parser/error_list.go | 68 - .../d5/tengo/compiler/parser/parse_source.go | 17 - .../github.com/d5/tengo/compiler/parser/parser.go | 1216 --------------- vendor/github.com/d5/tengo/compiler/parser/sync.go | 12 - .../d5/tengo/compiler/scanner/error_handler.go | 6 - .../github.com/d5/tengo/compiler/scanner/mode.go | 10 - .../d5/tengo/compiler/scanner/scanner.go | 680 --------- vendor/github.com/d5/tengo/compiler/source/file.go | 110 -- .../d5/tengo/compiler/source/file_pos.go | 47 - .../d5/tengo/compiler/source/file_set.go | 96 -- vendor/github.com/d5/tengo/compiler/source/pos.go | 12 - vendor/github.com/d5/tengo/compiler/symbol.go | 9 - .../github.com/d5/tengo/compiler/symbol_scopes.go | 12 - .../github.com/d5/tengo/compiler/symbol_table.go | 159 -- .../github.com/d5/tengo/compiler/token/keywords.go | 19 - .../github.com/d5/tengo/compiler/token/tokens.go | 208 --- vendor/github.com/d5/tengo/go.mod | 3 - vendor/github.com/d5/tengo/go.sum | 0 vendor/github.com/d5/tengo/objects/array.go | 130 -- .../github.com/d5/tengo/objects/array_iterator.go | 57 - vendor/github.com/d5/tengo/objects/bool.go | 64 - .../github.com/d5/tengo/objects/builtin_append.go | 21 - .../github.com/d5/tengo/objects/builtin_convert.go | 169 --- vendor/github.com/d5/tengo/objects/builtin_copy.go | 9 - .../github.com/d5/tengo/objects/builtin_format.go | 27 - .../d5/tengo/objects/builtin_function.go | 47 - vendor/github.com/d5/tengo/objects/builtin_len.go | 29 - .../github.com/d5/tengo/objects/builtin_module.go | 23 - vendor/github.com/d5/tengo/objects/builtin_type.go | 9 - .../d5/tengo/objects/builtin_type_checks.go | 195 --- vendor/github.com/d5/tengo/objects/builtins.go | 118 -- vendor/github.com/d5/tengo/objects/bytes.go | 89 -- .../github.com/d5/tengo/objects/bytes_iterator.go | 57 - vendor/github.com/d5/tengo/objects/callable.go | 9 - .../github.com/d5/tengo/objects/callable_func.go | 4 - vendor/github.com/d5/tengo/objects/char.go | 119 -- vendor/github.com/d5/tengo/objects/closure.go | 45 - .../d5/tengo/objects/compiled_function.go | 62 - vendor/github.com/d5/tengo/objects/conversion.go | 276 ---- .../github.com/d5/tengo/objects/count_objects.go | 31 - vendor/github.com/d5/tengo/objects/error.go | 47 - vendor/github.com/d5/tengo/objects/errors.go | 38 - vendor/github.com/d5/tengo/objects/float.go | 146 -- vendor/github.com/d5/tengo/objects/formatter.go | 1212 --------------- .../github.com/d5/tengo/objects/immutable_array.go | 109 -- .../github.com/d5/tengo/objects/immutable_map.go | 105 -- vendor/github.com/d5/tengo/objects/importable.go | 7 - .../d5/tengo/objects/index_assignable.go | 9 - vendor/github.com/d5/tengo/objects/indexable.go | 9 - vendor/github.com/d5/tengo/objects/int.go | 198 --- vendor/github.com/d5/tengo/objects/iterable.go | 7 - vendor/github.com/d5/tengo/objects/iterator.go | 15 - vendor/github.com/d5/tengo/objects/map.go | 118 -- vendor/github.com/d5/tengo/objects/map_iterator.go | 62 - vendor/github.com/d5/tengo/objects/module_map.go | 77 - vendor/github.com/d5/tengo/objects/object.go | 30 - vendor/github.com/d5/tengo/objects/object_ptr.go | 41 - vendor/github.com/d5/tengo/objects/objects.go | 12 - .../github.com/d5/tengo/objects/source_module.go | 11 - vendor/github.com/d5/tengo/objects/string.go | 103 -- .../github.com/d5/tengo/objects/string_iterator.go | 57 - vendor/github.com/d5/tengo/objects/time.go | 89 -- vendor/github.com/d5/tengo/objects/undefined.go | 62 - .../github.com/d5/tengo/objects/user_function.go | 48 - vendor/github.com/d5/tengo/runtime/errors.go | 11 - vendor/github.com/d5/tengo/runtime/frame.go | 13 - vendor/github.com/d5/tengo/runtime/vm.go | 1092 -------------- vendor/github.com/d5/tengo/script/compiled.go | 159 -- vendor/github.com/d5/tengo/script/script.go | 185 --- vendor/github.com/d5/tengo/script/variable.go | 149 -- vendor/github.com/d5/tengo/stdlib/base64.go | 20 - .../github.com/d5/tengo/stdlib/builtin_modules.go | 16 - vendor/github.com/d5/tengo/stdlib/errors.go | 11 - vendor/github.com/d5/tengo/stdlib/fmt.go | 110 -- vendor/github.com/d5/tengo/stdlib/func_typedefs.go | 1178 --------------- vendor/github.com/d5/tengo/stdlib/hex.go | 11 - vendor/github.com/d5/tengo/stdlib/json.go | 126 -- vendor/github.com/d5/tengo/stdlib/json/decode.go | 374 ----- vendor/github.com/d5/tengo/stdlib/json/encode.go | 147 -- vendor/github.com/d5/tengo/stdlib/json/scanner.go | 559 ------- vendor/github.com/d5/tengo/stdlib/math.go | 74 - vendor/github.com/d5/tengo/stdlib/os.go | 492 ------ vendor/github.com/d5/tengo/stdlib/os_exec.go | 113 -- vendor/github.com/d5/tengo/stdlib/os_file.go | 96 -- vendor/github.com/d5/tengo/stdlib/os_process.go | 62 - vendor/github.com/d5/tengo/stdlib/rand.go | 102 -- .../github.com/d5/tengo/stdlib/source_modules.go | 8 - .../github.com/d5/tengo/stdlib/srcmod_enum.tengo | 128 -- vendor/github.com/d5/tengo/stdlib/stdlib.go | 34 - vendor/github.com/d5/tengo/stdlib/text.go | 930 ------------ vendor/github.com/d5/tengo/stdlib/text_regexp.go | 228 --- vendor/github.com/d5/tengo/stdlib/times.go | 989 ------------ vendor/github.com/d5/tengo/tengo.go | 11 - vendor/github.com/d5/tengo/v2/.gitignore | 1 + vendor/github.com/d5/tengo/v2/.goreleaser.yml | 20 + vendor/github.com/d5/tengo/v2/LICENSE | 21 + vendor/github.com/d5/tengo/v2/Makefile | 11 + vendor/github.com/d5/tengo/v2/README.md | 135 ++ vendor/github.com/d5/tengo/v2/builtins.go | 502 +++++++ vendor/github.com/d5/tengo/v2/bytecode.go | 292 ++++ vendor/github.com/d5/tengo/v2/compiler.go | 1312 ++++++++++++++++ vendor/github.com/d5/tengo/v2/doc.go | 3 + vendor/github.com/d5/tengo/v2/errors.go | 64 + vendor/github.com/d5/tengo/v2/formatter.go | 1245 +++++++++++++++ vendor/github.com/d5/tengo/v2/go.mod | 3 + vendor/github.com/d5/tengo/v2/go.sum | 0 vendor/github.com/d5/tengo/v2/instructions.go | 61 + vendor/github.com/d5/tengo/v2/iterator.go | 209 +++ vendor/github.com/d5/tengo/v2/modules.go | 93 ++ vendor/github.com/d5/tengo/v2/objects.go | 1581 ++++++++++++++++++++ vendor/github.com/d5/tengo/v2/parser/ast.go | 69 + vendor/github.com/d5/tengo/v2/parser/expr.go | 597 ++++++++ vendor/github.com/d5/tengo/v2/parser/file.go | 29 + vendor/github.com/d5/tengo/v2/parser/opcodes.go | 156 ++ vendor/github.com/d5/tengo/v2/parser/parser.go | 1196 +++++++++++++++ vendor/github.com/d5/tengo/v2/parser/pos.go | 12 + vendor/github.com/d5/tengo/v2/parser/scanner.go | 689 +++++++++ .../github.com/d5/tengo/v2/parser/source_file.go | 231 +++ vendor/github.com/d5/tengo/v2/parser/stmt.go | 349 +++++ vendor/github.com/d5/tengo/v2/script.go | 313 ++++ vendor/github.com/d5/tengo/v2/stdlib/base64.go | 34 + .../d5/tengo/v2/stdlib/builtin_modules.go | 18 + vendor/github.com/d5/tengo/v2/stdlib/errors.go | 12 + vendor/github.com/d5/tengo/v2/stdlib/fmt.go | 101 ++ .../github.com/d5/tengo/v2/stdlib/func_typedefs.go | 1049 +++++++++++++ vendor/github.com/d5/tengo/v2/stdlib/hex.go | 12 + vendor/github.com/d5/tengo/v2/stdlib/json.go | 146 ++ .../github.com/d5/tengo/v2/stdlib/json/decode.go | 358 +++++ .../github.com/d5/tengo/v2/stdlib/json/encode.go | 146 ++ .../github.com/d5/tengo/v2/stdlib/json/scanner.go | 562 +++++++ vendor/github.com/d5/tengo/v2/stdlib/math.go | 233 +++ vendor/github.com/d5/tengo/v2/stdlib/os.go | 564 +++++++ vendor/github.com/d5/tengo/v2/stdlib/os_exec.go | 119 ++ vendor/github.com/d5/tengo/v2/stdlib/os_file.go | 117 ++ vendor/github.com/d5/tengo/v2/stdlib/os_process.go | 76 + vendor/github.com/d5/tengo/v2/stdlib/rand.go | 138 ++ .../d5/tengo/v2/stdlib/source_modules.go | 8 + .../d5/tengo/v2/stdlib/srcmod_enum.tengo | 128 ++ vendor/github.com/d5/tengo/v2/stdlib/stdlib.go | 34 + vendor/github.com/d5/tengo/v2/stdlib/text.go | 1072 +++++++++++++ .../github.com/d5/tengo/v2/stdlib/text_regexp.go | 251 ++++ vendor/github.com/d5/tengo/v2/stdlib/times.go | 1135 ++++++++++++++ vendor/github.com/d5/tengo/v2/symbol_table.go | 165 ++ vendor/github.com/d5/tengo/v2/tengo.go | 306 ++++ vendor/github.com/d5/tengo/v2/token/token.go | 225 +++ vendor/github.com/d5/tengo/v2/variable.go | 136 ++ vendor/github.com/d5/tengo/v2/vm.go | 883 +++++++++++ vendor/modules.txt | 19 +- 214 files changed, 17228 insertions(+), 17654 deletions(-) delete mode 100644 vendor/github.com/d5/tengo/.gitignore delete mode 100644 vendor/github.com/d5/tengo/.goreleaser.yml delete mode 100644 vendor/github.com/d5/tengo/.travis.yml delete mode 100644 vendor/github.com/d5/tengo/LICENSE delete mode 100644 vendor/github.com/d5/tengo/Makefile delete mode 100644 vendor/github.com/d5/tengo/README.md delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/array_lit.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/assign_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/ast.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/bad_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/bad_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/binary_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/block_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/bool_lit.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/branch_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/call_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/char_lit.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/cond_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/empty_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/error_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/export_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/expr_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/file.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/float_lit.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/for_in_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/for_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/func_lit.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/func_type.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/ident.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/ident_list.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/if_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/immutable_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/import_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/inc_dec_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/index_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/int_lit.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/map_element_lit.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/map_lit.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/node.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/paren_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/return_stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/selector_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/slice_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/stmt.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/string_lit.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/unary_expr.go delete mode 100644 vendor/github.com/d5/tengo/compiler/ast/undefined_lit.go delete mode 100644 vendor/github.com/d5/tengo/compiler/bytecode.go delete mode 100644 vendor/github.com/d5/tengo/compiler/bytecode_decode.go delete mode 100644 vendor/github.com/d5/tengo/compiler/bytecode_optimize.go delete mode 100644 vendor/github.com/d5/tengo/compiler/compilation_scope.go delete mode 100644 vendor/github.com/d5/tengo/compiler/compiler.go delete mode 100644 vendor/github.com/d5/tengo/compiler/compiler_assign.go delete mode 100644 vendor/github.com/d5/tengo/compiler/compiler_for.go delete mode 100644 vendor/github.com/d5/tengo/compiler/compiler_logical.go delete mode 100644 vendor/github.com/d5/tengo/compiler/compiler_loops.go delete mode 100644 vendor/github.com/d5/tengo/compiler/compiler_module.go delete mode 100644 vendor/github.com/d5/tengo/compiler/compiler_scopes.go delete mode 100644 vendor/github.com/d5/tengo/compiler/emitted_instruction.go delete mode 100644 vendor/github.com/d5/tengo/compiler/error.go delete mode 100644 vendor/github.com/d5/tengo/compiler/instructions.go delete mode 100644 vendor/github.com/d5/tengo/compiler/loop.go delete mode 100644 vendor/github.com/d5/tengo/compiler/module_loader.go delete mode 100644 vendor/github.com/d5/tengo/compiler/opcodes.go delete mode 100644 vendor/github.com/d5/tengo/compiler/parser/error.go delete mode 100644 vendor/github.com/d5/tengo/compiler/parser/error_list.go delete mode 100644 vendor/github.com/d5/tengo/compiler/parser/parse_source.go delete mode 100644 vendor/github.com/d5/tengo/compiler/parser/parser.go delete mode 100644 vendor/github.com/d5/tengo/compiler/parser/sync.go delete mode 100644 vendor/github.com/d5/tengo/compiler/scanner/error_handler.go delete mode 100644 vendor/github.com/d5/tengo/compiler/scanner/mode.go delete mode 100644 vendor/github.com/d5/tengo/compiler/scanner/scanner.go delete mode 100644 vendor/github.com/d5/tengo/compiler/source/file.go delete mode 100644 vendor/github.com/d5/tengo/compiler/source/file_pos.go delete mode 100644 vendor/github.com/d5/tengo/compiler/source/file_set.go delete mode 100644 vendor/github.com/d5/tengo/compiler/source/pos.go delete mode 100644 vendor/github.com/d5/tengo/compiler/symbol.go delete mode 100644 vendor/github.com/d5/tengo/compiler/symbol_scopes.go delete mode 100644 vendor/github.com/d5/tengo/compiler/symbol_table.go delete mode 100644 vendor/github.com/d5/tengo/compiler/token/keywords.go delete mode 100644 vendor/github.com/d5/tengo/compiler/token/tokens.go delete mode 100644 vendor/github.com/d5/tengo/go.mod delete mode 100644 vendor/github.com/d5/tengo/go.sum delete mode 100644 vendor/github.com/d5/tengo/objects/array.go delete mode 100644 vendor/github.com/d5/tengo/objects/array_iterator.go delete mode 100644 vendor/github.com/d5/tengo/objects/bool.go delete mode 100644 vendor/github.com/d5/tengo/objects/builtin_append.go delete mode 100644 vendor/github.com/d5/tengo/objects/builtin_convert.go delete mode 100644 vendor/github.com/d5/tengo/objects/builtin_copy.go delete mode 100644 vendor/github.com/d5/tengo/objects/builtin_format.go delete mode 100644 vendor/github.com/d5/tengo/objects/builtin_function.go delete mode 100644 vendor/github.com/d5/tengo/objects/builtin_len.go delete mode 100644 vendor/github.com/d5/tengo/objects/builtin_module.go delete mode 100644 vendor/github.com/d5/tengo/objects/builtin_type.go delete mode 100644 vendor/github.com/d5/tengo/objects/builtin_type_checks.go delete mode 100644 vendor/github.com/d5/tengo/objects/builtins.go delete mode 100644 vendor/github.com/d5/tengo/objects/bytes.go delete mode 100644 vendor/github.com/d5/tengo/objects/bytes_iterator.go delete mode 100644 vendor/github.com/d5/tengo/objects/callable.go delete mode 100644 vendor/github.com/d5/tengo/objects/callable_func.go delete mode 100644 vendor/github.com/d5/tengo/objects/char.go delete mode 100644 vendor/github.com/d5/tengo/objects/closure.go delete mode 100644 vendor/github.com/d5/tengo/objects/compiled_function.go delete mode 100644 vendor/github.com/d5/tengo/objects/conversion.go delete mode 100644 vendor/github.com/d5/tengo/objects/count_objects.go delete mode 100644 vendor/github.com/d5/tengo/objects/error.go delete mode 100644 vendor/github.com/d5/tengo/objects/errors.go delete mode 100644 vendor/github.com/d5/tengo/objects/float.go delete mode 100644 vendor/github.com/d5/tengo/objects/formatter.go delete mode 100644 vendor/github.com/d5/tengo/objects/immutable_array.go delete mode 100644 vendor/github.com/d5/tengo/objects/immutable_map.go delete mode 100644 vendor/github.com/d5/tengo/objects/importable.go delete mode 100644 vendor/github.com/d5/tengo/objects/index_assignable.go delete mode 100644 vendor/github.com/d5/tengo/objects/indexable.go delete mode 100644 vendor/github.com/d5/tengo/objects/int.go delete mode 100644 vendor/github.com/d5/tengo/objects/iterable.go delete mode 100644 vendor/github.com/d5/tengo/objects/iterator.go delete mode 100644 vendor/github.com/d5/tengo/objects/map.go delete mode 100644 vendor/github.com/d5/tengo/objects/map_iterator.go delete mode 100644 vendor/github.com/d5/tengo/objects/module_map.go delete mode 100644 vendor/github.com/d5/tengo/objects/object.go delete mode 100644 vendor/github.com/d5/tengo/objects/object_ptr.go delete mode 100644 vendor/github.com/d5/tengo/objects/objects.go delete mode 100644 vendor/github.com/d5/tengo/objects/source_module.go delete mode 100644 vendor/github.com/d5/tengo/objects/string.go delete mode 100644 vendor/github.com/d5/tengo/objects/string_iterator.go delete mode 100644 vendor/github.com/d5/tengo/objects/time.go delete mode 100644 vendor/github.com/d5/tengo/objects/undefined.go delete mode 100644 vendor/github.com/d5/tengo/objects/user_function.go delete mode 100644 vendor/github.com/d5/tengo/runtime/errors.go delete mode 100644 vendor/github.com/d5/tengo/runtime/frame.go delete mode 100644 vendor/github.com/d5/tengo/runtime/vm.go delete mode 100644 vendor/github.com/d5/tengo/script/compiled.go delete mode 100644 vendor/github.com/d5/tengo/script/script.go delete mode 100644 vendor/github.com/d5/tengo/script/variable.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/base64.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/builtin_modules.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/errors.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/fmt.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/func_typedefs.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/hex.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/json.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/json/decode.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/json/encode.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/json/scanner.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/math.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/os.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/os_exec.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/os_file.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/os_process.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/rand.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/source_modules.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/srcmod_enum.tengo delete mode 100644 vendor/github.com/d5/tengo/stdlib/stdlib.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/text.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/text_regexp.go delete mode 100644 vendor/github.com/d5/tengo/stdlib/times.go delete mode 100644 vendor/github.com/d5/tengo/tengo.go create mode 100644 vendor/github.com/d5/tengo/v2/.gitignore create mode 100644 vendor/github.com/d5/tengo/v2/.goreleaser.yml create mode 100644 vendor/github.com/d5/tengo/v2/LICENSE create mode 100644 vendor/github.com/d5/tengo/v2/Makefile create mode 100644 vendor/github.com/d5/tengo/v2/README.md create mode 100644 vendor/github.com/d5/tengo/v2/builtins.go create mode 100644 vendor/github.com/d5/tengo/v2/bytecode.go create mode 100644 vendor/github.com/d5/tengo/v2/compiler.go create mode 100644 vendor/github.com/d5/tengo/v2/doc.go create mode 100644 vendor/github.com/d5/tengo/v2/errors.go create mode 100644 vendor/github.com/d5/tengo/v2/formatter.go create mode 100644 vendor/github.com/d5/tengo/v2/go.mod create mode 100644 vendor/github.com/d5/tengo/v2/go.sum create mode 100644 vendor/github.com/d5/tengo/v2/instructions.go create mode 100644 vendor/github.com/d5/tengo/v2/iterator.go create mode 100644 vendor/github.com/d5/tengo/v2/modules.go create mode 100644 vendor/github.com/d5/tengo/v2/objects.go create mode 100644 vendor/github.com/d5/tengo/v2/parser/ast.go create mode 100644 vendor/github.com/d5/tengo/v2/parser/expr.go create mode 100644 vendor/github.com/d5/tengo/v2/parser/file.go create mode 100644 vendor/github.com/d5/tengo/v2/parser/opcodes.go create mode 100644 vendor/github.com/d5/tengo/v2/parser/parser.go create mode 100644 vendor/github.com/d5/tengo/v2/parser/pos.go create mode 100644 vendor/github.com/d5/tengo/v2/parser/scanner.go create mode 100644 vendor/github.com/d5/tengo/v2/parser/source_file.go create mode 100644 vendor/github.com/d5/tengo/v2/parser/stmt.go create mode 100644 vendor/github.com/d5/tengo/v2/script.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/base64.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/builtin_modules.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/errors.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/fmt.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/func_typedefs.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/hex.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/json.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/json/decode.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/json/encode.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/json/scanner.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/math.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/os.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/os_exec.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/os_file.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/os_process.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/rand.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/source_modules.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/srcmod_enum.tengo create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/stdlib.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/text.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/text_regexp.go create mode 100644 vendor/github.com/d5/tengo/v2/stdlib/times.go create mode 100644 vendor/github.com/d5/tengo/v2/symbol_table.go create mode 100644 vendor/github.com/d5/tengo/v2/tengo.go create mode 100644 vendor/github.com/d5/tengo/v2/token/token.go create mode 100644 vendor/github.com/d5/tengo/v2/variable.go create mode 100644 vendor/github.com/d5/tengo/v2/vm.go (limited to 'vendor') diff --git a/vendor/github.com/d5/tengo/.gitignore b/vendor/github.com/d5/tengo/.gitignore deleted file mode 100644 index 77738287..00000000 --- a/vendor/github.com/d5/tengo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist/ \ No newline at end of file diff --git a/vendor/github.com/d5/tengo/.goreleaser.yml b/vendor/github.com/d5/tengo/.goreleaser.yml deleted file mode 100644 index 9b1dffec..00000000 --- a/vendor/github.com/d5/tengo/.goreleaser.yml +++ /dev/null @@ -1,29 +0,0 @@ -env: - - GO111MODULE=on -before: - hooks: - - go mod tidy -builds: - - env: - - CGO_ENABLED=0 - main: ./cmd/tengo/main.go - goos: - - darwin - - linux - - windows - - env: - - CGO_ENABLED=0 - main: ./cmd/tengomin/main.go - id: tengomin - binary: tengomin - goos: - - darwin - - linux - - windows -archive: - files: - - none* -checksum: - name_template: 'checksums.txt' -changelog: - sort: asc diff --git a/vendor/github.com/d5/tengo/.travis.yml b/vendor/github.com/d5/tengo/.travis.yml deleted file mode 100644 index 57986ae9..00000000 --- a/vendor/github.com/d5/tengo/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: go - -go: - - "1.12" - -install: - - env GO111MODULE=on go get -u golang.org/x/lint/golint - -script: - - env GO111MODULE=on make test - -deploy: - - provider: script - skip_cleanup: true - script: curl -sL https://git.io/goreleaser | bash - on: - tags: true \ No newline at end of file diff --git a/vendor/github.com/d5/tengo/LICENSE b/vendor/github.com/d5/tengo/LICENSE deleted file mode 100644 index 2516341a..00000000 --- a/vendor/github.com/d5/tengo/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Daniel Kang - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/d5/tengo/Makefile b/vendor/github.com/d5/tengo/Makefile deleted file mode 100644 index 99daafd1..00000000 --- a/vendor/github.com/d5/tengo/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -vet: - go vet ./... - -generate: - go generate ./... - -lint: - golint -set_exit_status ./... - -test: generate vet lint - go test -race -cover ./... - -fmt: - go fmt ./... diff --git a/vendor/github.com/d5/tengo/README.md b/vendor/github.com/d5/tengo/README.md deleted file mode 100644 index 6a35cfd1..00000000 --- a/vendor/github.com/d5/tengo/README.md +++ /dev/null @@ -1,77 +0,0 @@ -

- -

- -# The Tengo Language - -[![GoDoc](https://godoc.org/github.com/d5/tengo?status.svg)](https://godoc.org/github.com/d5/tengo/script) -[![Go Report Card](https://goreportcard.com/badge/github.com/d5/tengo)](https://goreportcard.com/report/github.com/d5/tengo) -[![Build Status](https://travis-ci.org/d5/tengo.svg?branch=master)](https://travis-ci.org/d5/tengo) -[![Sourcegraph](https://sourcegraph.com/github.com/d5/tengo/-/badge.svg)](https://sourcegraph.com/github.com/d5/tengo?badge) - -**Tengo is a small, dynamic, fast, secure script language for Go.** - -Tengo is **[fast](#benchmark)** and secure because it's compiled/executed as bytecode on stack-based VM that's written in native Go. - -```golang -/* The Tengo Language */ - -fmt := import("fmt") - -each := func(seq, fn) { - for x in seq { fn(x) } -} - -sum := func(init, seq) { - each(seq, func(x) { init += x }) - return init -} - -fmt.println(sum(0, [1, 2, 3])) // "6" -fmt.println(sum("", [1, 2, 3])) // "123" -``` - -> Run this code in the [Playground](https://tengolang.com/?s=0c8d5d0d88f2795a7093d7f35ae12c3afa17bea3) - -## Features - -- Simple and highly readable [Syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md) - - Dynamic typing with type coercion - - Higher-order functions and closures - - Immutable values - - Garbage collection -- [Securely Embeddable](https://github.com/d5/tengo/blob/master/docs/interoperability.md) and [Extensible](https://github.com/d5/tengo/blob/master/docs/objects.md) -- Compiler/runtime written in native Go _(no external deps or cgo)_ -- Executable as a [standalone](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) language / REPL -- Use cases: rules engine, [state machine](https://github.com/d5/go-fsm), [gaming](https://github.com/d5/pbr), data pipeline, [transpiler](https://github.com/d5/tengo2lua) - -## Benchmark - -| | fib(35) | fibt(35) | Type | -| :--- | ---: | ---: | :---: | -| Go | `48ms` | `3ms` | Go (native) | -| [**Tengo**](https://github.com/d5/tengo) | `2,349ms` | `5ms` | VM on Go | -| Lua | `1,416ms` | `3ms` | Lua (native) | -| [go-lua](https://github.com/Shopify/go-lua) | `4,402ms` | `5ms` | Lua VM on Go | -| [GopherLua](https://github.com/yuin/gopher-lua) | `4,023ms` | `5ms` | Lua VM on Go | -| Python | `2,588ms` | `26ms` | Python (native) | -| [starlark-go](https://github.com/google/starlark-go) | `11,126ms` | `6ms` | Python-like Interpreter on Go | -| [gpython](https://github.com/go-python/gpython) | `15,035ms` | `4ms` | Python Interpreter on Go | -| [goja](https://github.com/dop251/goja) | `5,089ms` | `5ms` | JS VM on Go | -| [otto](https://github.com/robertkrimen/otto) | `68,377ms` | `11ms` | JS Interpreter on Go | -| [Anko](https://github.com/mattn/anko) | `92,579ms` | `18ms` | Interpreter on Go | - -_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): Fibonacci(35)_ -_* [fibt(35)](https://github.com/d5/tengobench/blob/master/code/fibtc.tengo): [tail-call](https://en.wikipedia.org/wiki/Tail_call) version of Fibonacci(35)_ -_* **Go** does not read the source code from file, while all other cases do_ -_* See [here](https://github.com/d5/tengobench) for commands/codes used_ - -## References - -- [Language Syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md) -- [Object Types](https://github.com/d5/tengo/blob/master/docs/objects.md) -- [Runtime Types](https://github.com/d5/tengo/blob/master/docs/runtime-types.md) and [Operators](https://github.com/d5/tengo/blob/master/docs/operators.md) -- [Builtin Functions](https://github.com/d5/tengo/blob/master/docs/builtins.md) -- [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md) -- [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) -- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md) diff --git a/vendor/github.com/d5/tengo/compiler/ast/array_lit.go b/vendor/github.com/d5/tengo/compiler/ast/array_lit.go deleted file mode 100644 index 9fb4ed67..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/array_lit.go +++ /dev/null @@ -1,35 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" -) - -// ArrayLit represents an array literal. -type ArrayLit struct { - Elements []Expr - LBrack source.Pos - RBrack source.Pos -} - -func (e *ArrayLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *ArrayLit) Pos() source.Pos { - return e.LBrack -} - -// End returns the position of first character immediately after the node. -func (e *ArrayLit) End() source.Pos { - return e.RBrack + 1 -} - -func (e *ArrayLit) String() string { - var elements []string - for _, m := range e.Elements { - elements = append(elements, m.String()) - } - - return "[" + strings.Join(elements, ", ") + "]" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/assign_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/assign_stmt.go deleted file mode 100644 index e129114e..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/assign_stmt.go +++ /dev/null @@ -1,40 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// AssignStmt represents an assignment statement. -type AssignStmt struct { - LHS []Expr - RHS []Expr - Token token.Token - TokenPos source.Pos -} - -func (s *AssignStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *AssignStmt) Pos() source.Pos { - return s.LHS[0].Pos() -} - -// End returns the position of first character immediately after the node. -func (s *AssignStmt) End() source.Pos { - return s.RHS[len(s.RHS)-1].End() -} - -func (s *AssignStmt) String() string { - var lhs, rhs []string - for _, e := range s.LHS { - lhs = append(lhs, e.String()) - } - for _, e := range s.RHS { - rhs = append(rhs, e.String()) - } - - return strings.Join(lhs, ", ") + " " + s.Token.String() + " " + strings.Join(rhs, ", ") -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/ast.go b/vendor/github.com/d5/tengo/compiler/ast/ast.go deleted file mode 100644 index 9fd06728..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/ast.go +++ /dev/null @@ -1,5 +0,0 @@ -package ast - -const ( - nullRep = "" -) diff --git a/vendor/github.com/d5/tengo/compiler/ast/bad_expr.go b/vendor/github.com/d5/tengo/compiler/ast/bad_expr.go deleted file mode 100644 index 771f26fd..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/bad_expr.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// BadExpr represents a bad expression. -type BadExpr struct { - From source.Pos - To source.Pos -} - -func (e *BadExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *BadExpr) Pos() source.Pos { - return e.From -} - -// End returns the position of first character immediately after the node. -func (e *BadExpr) End() source.Pos { - return e.To -} - -func (e *BadExpr) String() string { - return "" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/bad_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/bad_stmt.go deleted file mode 100644 index c2d0ae9a..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/bad_stmt.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// BadStmt represents a bad statement. -type BadStmt struct { - From source.Pos - To source.Pos -} - -func (s *BadStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *BadStmt) Pos() source.Pos { - return s.From -} - -// End returns the position of first character immediately after the node. -func (s *BadStmt) End() source.Pos { - return s.To -} - -func (s *BadStmt) String() string { - return "" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/binary_expr.go b/vendor/github.com/d5/tengo/compiler/ast/binary_expr.go deleted file mode 100644 index 0cc5bba8..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/binary_expr.go +++ /dev/null @@ -1,30 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// BinaryExpr represents a binary operator expression. -type BinaryExpr struct { - LHS Expr - RHS Expr - Token token.Token - TokenPos source.Pos -} - -func (e *BinaryExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *BinaryExpr) Pos() source.Pos { - return e.LHS.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *BinaryExpr) End() source.Pos { - return e.RHS.End() -} - -func (e *BinaryExpr) String() string { - return "(" + e.LHS.String() + " " + e.Token.String() + " " + e.RHS.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/block_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/block_stmt.go deleted file mode 100644 index 9bde9fa3..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/block_stmt.go +++ /dev/null @@ -1,35 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" -) - -// BlockStmt represents a block statement. -type BlockStmt struct { - Stmts []Stmt - LBrace source.Pos - RBrace source.Pos -} - -func (s *BlockStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *BlockStmt) Pos() source.Pos { - return s.LBrace -} - -// End returns the position of first character immediately after the node. -func (s *BlockStmt) End() source.Pos { - return s.RBrace + 1 -} - -func (s *BlockStmt) String() string { - var list []string - for _, e := range s.Stmts { - list = append(list, e.String()) - } - - return "{" + strings.Join(list, "; ") + "}" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/bool_lit.go b/vendor/github.com/d5/tengo/compiler/ast/bool_lit.go deleted file mode 100644 index e667a5c8..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/bool_lit.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// BoolLit represents a boolean literal. -type BoolLit struct { - Value bool - ValuePos source.Pos - Literal string -} - -func (e *BoolLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *BoolLit) Pos() source.Pos { - return e.ValuePos -} - -// End returns the position of first character immediately after the node. -func (e *BoolLit) End() source.Pos { - return source.Pos(int(e.ValuePos) + len(e.Literal)) -} - -func (e *BoolLit) String() string { - return e.Literal -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/branch_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/branch_stmt.go deleted file mode 100644 index f6c7fdea..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/branch_stmt.go +++ /dev/null @@ -1,38 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// BranchStmt represents a branch statement. -type BranchStmt struct { - Token token.Token - TokenPos source.Pos - Label *Ident -} - -func (s *BranchStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *BranchStmt) Pos() source.Pos { - return s.TokenPos -} - -// End returns the position of first character immediately after the node. -func (s *BranchStmt) End() source.Pos { - if s.Label != nil { - return s.Label.End() - } - - return source.Pos(int(s.TokenPos) + len(s.Token.String())) -} - -func (s *BranchStmt) String() string { - var label string - if s.Label != nil { - label = " " + s.Label.Name - } - - return s.Token.String() + label -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/call_expr.go b/vendor/github.com/d5/tengo/compiler/ast/call_expr.go deleted file mode 100644 index 0219d7c9..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/call_expr.go +++ /dev/null @@ -1,36 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" -) - -// CallExpr represents a function call expression. -type CallExpr struct { - Func Expr - LParen source.Pos - Args []Expr - RParen source.Pos -} - -func (e *CallExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *CallExpr) Pos() source.Pos { - return e.Func.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *CallExpr) End() source.Pos { - return e.RParen + 1 -} - -func (e *CallExpr) String() string { - var args []string - for _, e := range e.Args { - args = append(args, e.String()) - } - - return e.Func.String() + "(" + strings.Join(args, ", ") + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/char_lit.go b/vendor/github.com/d5/tengo/compiler/ast/char_lit.go deleted file mode 100644 index 592f8744..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/char_lit.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// CharLit represents a character literal. -type CharLit struct { - Value rune - ValuePos source.Pos - Literal string -} - -func (e *CharLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *CharLit) Pos() source.Pos { - return e.ValuePos -} - -// End returns the position of first character immediately after the node. -func (e *CharLit) End() source.Pos { - return source.Pos(int(e.ValuePos) + len(e.Literal)) -} - -func (e *CharLit) String() string { - return e.Literal -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/cond_expr.go b/vendor/github.com/d5/tengo/compiler/ast/cond_expr.go deleted file mode 100644 index bb1db849..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/cond_expr.go +++ /dev/null @@ -1,30 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" -) - -// CondExpr represents a ternary conditional expression. -type CondExpr struct { - Cond Expr - True Expr - False Expr - QuestionPos source.Pos - ColonPos source.Pos -} - -func (e *CondExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *CondExpr) Pos() source.Pos { - return e.Cond.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *CondExpr) End() source.Pos { - return e.False.End() -} - -func (e *CondExpr) String() string { - return "(" + e.Cond.String() + " ? " + e.True.String() + " : " + e.False.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/empty_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/empty_stmt.go deleted file mode 100644 index a2ac6ffe..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/empty_stmt.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// EmptyStmt represents an empty statement. -type EmptyStmt struct { - Semicolon source.Pos - Implicit bool -} - -func (s *EmptyStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *EmptyStmt) Pos() source.Pos { - return s.Semicolon -} - -// End returns the position of first character immediately after the node. -func (s *EmptyStmt) End() source.Pos { - if s.Implicit { - return s.Semicolon - } - - return s.Semicolon + 1 -} - -func (s *EmptyStmt) String() string { - return ";" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/error_expr.go b/vendor/github.com/d5/tengo/compiler/ast/error_expr.go deleted file mode 100644 index 7ce5667e..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/error_expr.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" -) - -// ErrorExpr represents an error expression -type ErrorExpr struct { - Expr Expr - ErrorPos source.Pos - LParen source.Pos - RParen source.Pos -} - -func (e *ErrorExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *ErrorExpr) Pos() source.Pos { - return e.ErrorPos -} - -// End returns the position of first character immediately after the node. -func (e *ErrorExpr) End() source.Pos { - return e.RParen -} - -func (e *ErrorExpr) String() string { - return "error(" + e.Expr.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/export_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/export_stmt.go deleted file mode 100644 index 64eb7606..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/export_stmt.go +++ /dev/null @@ -1,27 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" -) - -// ExportStmt represents an export statement. -type ExportStmt struct { - ExportPos source.Pos - Result Expr -} - -func (s *ExportStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *ExportStmt) Pos() source.Pos { - return s.ExportPos -} - -// End returns the position of first character immediately after the node. -func (s *ExportStmt) End() source.Pos { - return s.Result.End() -} - -func (s *ExportStmt) String() string { - return "export " + s.Result.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/expr.go b/vendor/github.com/d5/tengo/compiler/ast/expr.go deleted file mode 100644 index 764bacec..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/expr.go +++ /dev/null @@ -1,7 +0,0 @@ -package ast - -// Expr represents an expression node in the AST. -type Expr interface { - Node - exprNode() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/expr_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/expr_stmt.go deleted file mode 100644 index 095a3ad5..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/expr_stmt.go +++ /dev/null @@ -1,24 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// ExprStmt represents an expression statement. -type ExprStmt struct { - Expr Expr -} - -func (s *ExprStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *ExprStmt) Pos() source.Pos { - return s.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (s *ExprStmt) End() source.Pos { - return s.Expr.End() -} - -func (s *ExprStmt) String() string { - return s.Expr.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/file.go b/vendor/github.com/d5/tengo/compiler/ast/file.go deleted file mode 100644 index fc18b2d7..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/file.go +++ /dev/null @@ -1,32 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" -) - -// File represents a file unit. -type File struct { - InputFile *source.File - Stmts []Stmt -} - -// Pos returns the position of first character belonging to the node. -func (n *File) Pos() source.Pos { - return source.Pos(n.InputFile.Base) -} - -// End returns the position of first character immediately after the node. -func (n *File) End() source.Pos { - return source.Pos(n.InputFile.Base + n.InputFile.Size) -} - -func (n *File) String() string { - var stmts []string - for _, e := range n.Stmts { - stmts = append(stmts, e.String()) - } - - return strings.Join(stmts, "; ") -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/float_lit.go b/vendor/github.com/d5/tengo/compiler/ast/float_lit.go deleted file mode 100644 index 670f744b..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/float_lit.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// FloatLit represents a floating point literal. -type FloatLit struct { - Value float64 - ValuePos source.Pos - Literal string -} - -func (e *FloatLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *FloatLit) Pos() source.Pos { - return e.ValuePos -} - -// End returns the position of first character immediately after the node. -func (e *FloatLit) End() source.Pos { - return source.Pos(int(e.ValuePos) + len(e.Literal)) -} - -func (e *FloatLit) String() string { - return e.Literal -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/for_in_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/for_in_stmt.go deleted file mode 100644 index 18020b56..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/for_in_stmt.go +++ /dev/null @@ -1,32 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// ForInStmt represents a for-in statement. -type ForInStmt struct { - ForPos source.Pos - Key *Ident - Value *Ident - Iterable Expr - Body *BlockStmt -} - -func (s *ForInStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *ForInStmt) Pos() source.Pos { - return s.ForPos -} - -// End returns the position of first character immediately after the node. -func (s *ForInStmt) End() source.Pos { - return s.Body.End() -} - -func (s *ForInStmt) String() string { - if s.Value != nil { - return "for " + s.Key.String() + ", " + s.Value.String() + " in " + s.Iterable.String() + " " + s.Body.String() - } - - return "for " + s.Key.String() + " in " + s.Iterable.String() + " " + s.Body.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/for_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/for_stmt.go deleted file mode 100644 index 4b5a0a17..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/for_stmt.go +++ /dev/null @@ -1,43 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// ForStmt represents a for statement. -type ForStmt struct { - ForPos source.Pos - Init Stmt - Cond Expr - Post Stmt - Body *BlockStmt -} - -func (s *ForStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *ForStmt) Pos() source.Pos { - return s.ForPos -} - -// End returns the position of first character immediately after the node. -func (s *ForStmt) End() source.Pos { - return s.Body.End() -} - -func (s *ForStmt) String() string { - var init, cond, post string - if s.Init != nil { - init = s.Init.String() - } - if s.Cond != nil { - cond = s.Cond.String() + " " - } - if s.Post != nil { - post = s.Post.String() - } - - if init != "" || post != "" { - return "for " + init + " ; " + cond + " ; " + post + s.Body.String() - } - - return "for " + cond + s.Body.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/func_lit.go b/vendor/github.com/d5/tengo/compiler/ast/func_lit.go deleted file mode 100644 index 2e90ed2b..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/func_lit.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// FuncLit represents a function literal. -type FuncLit struct { - Type *FuncType - Body *BlockStmt -} - -func (e *FuncLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *FuncLit) Pos() source.Pos { - return e.Type.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *FuncLit) End() source.Pos { - return e.Body.End() -} - -func (e *FuncLit) String() string { - return "func" + e.Type.Params.String() + " " + e.Body.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/func_type.go b/vendor/github.com/d5/tengo/compiler/ast/func_type.go deleted file mode 100644 index 2afaabb1..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/func_type.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// FuncType represents a function type definition. -type FuncType struct { - FuncPos source.Pos - Params *IdentList -} - -func (e *FuncType) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *FuncType) Pos() source.Pos { - return e.FuncPos -} - -// End returns the position of first character immediately after the node. -func (e *FuncType) End() source.Pos { - return e.Params.End() -} - -func (e *FuncType) String() string { - return "func" + e.Params.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/ident.go b/vendor/github.com/d5/tengo/compiler/ast/ident.go deleted file mode 100644 index 33b7ff76..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/ident.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// Ident represents an identifier. -type Ident struct { - Name string - NamePos source.Pos -} - -func (e *Ident) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *Ident) Pos() source.Pos { - return e.NamePos -} - -// End returns the position of first character immediately after the node. -func (e *Ident) End() source.Pos { - return source.Pos(int(e.NamePos) + len(e.Name)) -} - -func (e *Ident) String() string { - if e != nil { - return e.Name - } - - return nullRep -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/ident_list.go b/vendor/github.com/d5/tengo/compiler/ast/ident_list.go deleted file mode 100644 index 8dd6d307..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/ident_list.go +++ /dev/null @@ -1,63 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" -) - -// IdentList represents a list of identifiers. -type IdentList struct { - LParen source.Pos - VarArgs bool - List []*Ident - RParen source.Pos -} - -// Pos returns the position of first character belonging to the node. -func (n *IdentList) Pos() source.Pos { - if n.LParen.IsValid() { - return n.LParen - } - - if len(n.List) > 0 { - return n.List[0].Pos() - } - - return source.NoPos -} - -// End returns the position of first character immediately after the node. -func (n *IdentList) End() source.Pos { - if n.RParen.IsValid() { - return n.RParen + 1 - } - - if l := len(n.List); l > 0 { - return n.List[l-1].End() - } - - return source.NoPos -} - -// NumFields returns the number of fields. -func (n *IdentList) NumFields() int { - if n == nil { - return 0 - } - - return len(n.List) -} - -func (n *IdentList) String() string { - var list []string - for i, e := range n.List { - if n.VarArgs && i == len(n.List)-1 { - list = append(list, "..."+e.String()) - } else { - list = append(list, e.String()) - } - } - - return "(" + strings.Join(list, ", ") + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/if_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/if_stmt.go deleted file mode 100644 index b3d65606..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/if_stmt.go +++ /dev/null @@ -1,40 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// IfStmt represents an if statement. -type IfStmt struct { - IfPos source.Pos - Init Stmt - Cond Expr - Body *BlockStmt - Else Stmt // else branch; or nil -} - -func (s *IfStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *IfStmt) Pos() source.Pos { - return s.IfPos -} - -// End returns the position of first character immediately after the node. -func (s *IfStmt) End() source.Pos { - if s.Else != nil { - return s.Else.End() - } - - return s.Body.End() -} - -func (s *IfStmt) String() string { - var initStmt, elseStmt string - if s.Init != nil { - initStmt = s.Init.String() + "; " - } - if s.Else != nil { - elseStmt = " else " + s.Else.String() - } - - return "if " + initStmt + s.Cond.String() + " " + s.Body.String() + elseStmt -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/immutable_expr.go b/vendor/github.com/d5/tengo/compiler/ast/immutable_expr.go deleted file mode 100644 index f9843b50..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/immutable_expr.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" -) - -// ImmutableExpr represents an immutable expression -type ImmutableExpr struct { - Expr Expr - ErrorPos source.Pos - LParen source.Pos - RParen source.Pos -} - -func (e *ImmutableExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *ImmutableExpr) Pos() source.Pos { - return e.ErrorPos -} - -// End returns the position of first character immediately after the node. -func (e *ImmutableExpr) End() source.Pos { - return e.RParen -} - -func (e *ImmutableExpr) String() string { - return "immutable(" + e.Expr.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/import_expr.go b/vendor/github.com/d5/tengo/compiler/ast/import_expr.go deleted file mode 100644 index 6eff74a9..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/import_expr.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// ImportExpr represents an import expression -type ImportExpr struct { - ModuleName string - Token token.Token - TokenPos source.Pos -} - -func (e *ImportExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *ImportExpr) Pos() source.Pos { - return e.TokenPos -} - -// End returns the position of first character immediately after the node. -func (e *ImportExpr) End() source.Pos { - return source.Pos(int(e.TokenPos) + 10 + len(e.ModuleName)) // import("moduleName") -} - -func (e *ImportExpr) String() string { - return `import("` + e.ModuleName + `")"` -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/inc_dec_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/inc_dec_stmt.go deleted file mode 100644 index e4e7f92c..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/inc_dec_stmt.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// IncDecStmt represents increment or decrement statement. -type IncDecStmt struct { - Expr Expr - Token token.Token - TokenPos source.Pos -} - -func (s *IncDecStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *IncDecStmt) Pos() source.Pos { - return s.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (s *IncDecStmt) End() source.Pos { - return source.Pos(int(s.TokenPos) + 2) -} - -func (s *IncDecStmt) String() string { - return s.Expr.String() + s.Token.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/index_expr.go b/vendor/github.com/d5/tengo/compiler/ast/index_expr.go deleted file mode 100644 index bc0992a3..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/index_expr.go +++ /dev/null @@ -1,32 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// IndexExpr represents an index expression. -type IndexExpr struct { - Expr Expr - LBrack source.Pos - Index Expr - RBrack source.Pos -} - -func (e *IndexExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *IndexExpr) Pos() source.Pos { - return e.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *IndexExpr) End() source.Pos { - return e.RBrack + 1 -} - -func (e *IndexExpr) String() string { - var index string - if e.Index != nil { - index = e.Index.String() - } - - return e.Expr.String() + "[" + index + "]" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/int_lit.go b/vendor/github.com/d5/tengo/compiler/ast/int_lit.go deleted file mode 100644 index 3e1fd98b..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/int_lit.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// IntLit represents an integer literal. -type IntLit struct { - Value int64 - ValuePos source.Pos - Literal string -} - -func (e *IntLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *IntLit) Pos() source.Pos { - return e.ValuePos -} - -// End returns the position of first character immediately after the node. -func (e *IntLit) End() source.Pos { - return source.Pos(int(e.ValuePos) + len(e.Literal)) -} - -func (e *IntLit) String() string { - return e.Literal -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/map_element_lit.go b/vendor/github.com/d5/tengo/compiler/ast/map_element_lit.go deleted file mode 100644 index 3d7fca9e..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/map_element_lit.go +++ /dev/null @@ -1,27 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// MapElementLit represents a map element. -type MapElementLit struct { - Key string - KeyPos source.Pos - ColonPos source.Pos - Value Expr -} - -func (e *MapElementLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *MapElementLit) Pos() source.Pos { - return e.KeyPos -} - -// End returns the position of first character immediately after the node. -func (e *MapElementLit) End() source.Pos { - return e.Value.End() -} - -func (e *MapElementLit) String() string { - return e.Key + ": " + e.Value.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/map_lit.go b/vendor/github.com/d5/tengo/compiler/ast/map_lit.go deleted file mode 100644 index a228224d..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/map_lit.go +++ /dev/null @@ -1,35 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" -) - -// MapLit represents a map literal. -type MapLit struct { - LBrace source.Pos - Elements []*MapElementLit - RBrace source.Pos -} - -func (e *MapLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *MapLit) Pos() source.Pos { - return e.LBrace -} - -// End returns the position of first character immediately after the node. -func (e *MapLit) End() source.Pos { - return e.RBrace + 1 -} - -func (e *MapLit) String() string { - var elements []string - for _, m := range e.Elements { - elements = append(elements, m.String()) - } - - return "{" + strings.Join(elements, ", ") + "}" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/node.go b/vendor/github.com/d5/tengo/compiler/ast/node.go deleted file mode 100644 index 44677b47..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/node.go +++ /dev/null @@ -1,13 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// Node represents a node in the AST. -type Node interface { - // Pos returns the position of first character belonging to the node. - Pos() source.Pos - // End returns the position of first character immediately after the node. - End() source.Pos - // String returns a string representation of the node. - String() string -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/paren_expr.go b/vendor/github.com/d5/tengo/compiler/ast/paren_expr.go deleted file mode 100644 index 8db4ac02..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/paren_expr.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// ParenExpr represents a parenthesis wrapped expression. -type ParenExpr struct { - Expr Expr - LParen source.Pos - RParen source.Pos -} - -func (e *ParenExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *ParenExpr) Pos() source.Pos { - return e.LParen -} - -// End returns the position of first character immediately after the node. -func (e *ParenExpr) End() source.Pos { - return e.RParen + 1 -} - -func (e *ParenExpr) String() string { - return "(" + e.Expr.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/return_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/return_stmt.go deleted file mode 100644 index 592d45b8..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/return_stmt.go +++ /dev/null @@ -1,35 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" -) - -// ReturnStmt represents a return statement. -type ReturnStmt struct { - ReturnPos source.Pos - Result Expr -} - -func (s *ReturnStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *ReturnStmt) Pos() source.Pos { - return s.ReturnPos -} - -// End returns the position of first character immediately after the node. -func (s *ReturnStmt) End() source.Pos { - if s.Result != nil { - return s.Result.End() - } - - return s.ReturnPos + 6 -} - -func (s *ReturnStmt) String() string { - if s.Result != nil { - return "return " + s.Result.String() - } - - return "return" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/selector_expr.go b/vendor/github.com/d5/tengo/compiler/ast/selector_expr.go deleted file mode 100644 index 31d2e6d1..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/selector_expr.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// SelectorExpr represents a selector expression. -type SelectorExpr struct { - Expr Expr - Sel Expr -} - -func (e *SelectorExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *SelectorExpr) Pos() source.Pos { - return e.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *SelectorExpr) End() source.Pos { - return e.Sel.End() -} - -func (e *SelectorExpr) String() string { - return e.Expr.String() + "." + e.Sel.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/slice_expr.go b/vendor/github.com/d5/tengo/compiler/ast/slice_expr.go deleted file mode 100644 index e7e2e05b..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/slice_expr.go +++ /dev/null @@ -1,36 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// SliceExpr represents a slice expression. -type SliceExpr struct { - Expr Expr - LBrack source.Pos - Low Expr - High Expr - RBrack source.Pos -} - -func (e *SliceExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *SliceExpr) Pos() source.Pos { - return e.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *SliceExpr) End() source.Pos { - return e.RBrack + 1 -} - -func (e *SliceExpr) String() string { - var low, high string - if e.Low != nil { - low = e.Low.String() - } - if e.High != nil { - high = e.High.String() - } - - return e.Expr.String() + "[" + low + ":" + high + "]" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/stmt.go b/vendor/github.com/d5/tengo/compiler/ast/stmt.go deleted file mode 100644 index 6b26ba88..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/stmt.go +++ /dev/null @@ -1,7 +0,0 @@ -package ast - -// Stmt represents a statement in the AST. -type Stmt interface { - Node - stmtNode() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/string_lit.go b/vendor/github.com/d5/tengo/compiler/ast/string_lit.go deleted file mode 100644 index 2119d34b..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/string_lit.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// StringLit represents a string literal. -type StringLit struct { - Value string - ValuePos source.Pos - Literal string -} - -func (e *StringLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *StringLit) Pos() source.Pos { - return e.ValuePos -} - -// End returns the position of first character immediately after the node. -func (e *StringLit) End() source.Pos { - return source.Pos(int(e.ValuePos) + len(e.Literal)) -} - -func (e *StringLit) String() string { - return e.Literal -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/unary_expr.go b/vendor/github.com/d5/tengo/compiler/ast/unary_expr.go deleted file mode 100644 index 53236146..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/unary_expr.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// UnaryExpr represents an unary operator expression. -type UnaryExpr struct { - Expr Expr - Token token.Token - TokenPos source.Pos -} - -func (e *UnaryExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *UnaryExpr) Pos() source.Pos { - return e.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *UnaryExpr) End() source.Pos { - return e.Expr.End() -} - -func (e *UnaryExpr) String() string { - return "(" + e.Token.String() + e.Expr.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/undefined_lit.go b/vendor/github.com/d5/tengo/compiler/ast/undefined_lit.go deleted file mode 100644 index 8e51b113..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/undefined_lit.go +++ /dev/null @@ -1,24 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// UndefinedLit represents an undefined literal. -type UndefinedLit struct { - TokenPos source.Pos -} - -func (e *UndefinedLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *UndefinedLit) Pos() source.Pos { - return e.TokenPos -} - -// End returns the position of first character immediately after the node. -func (e *UndefinedLit) End() source.Pos { - return e.TokenPos + 9 // len(undefined) == 9 -} - -func (e *UndefinedLit) String() string { - return "undefined" -} diff --git a/vendor/github.com/d5/tengo/compiler/bytecode.go b/vendor/github.com/d5/tengo/compiler/bytecode.go deleted file mode 100644 index 35f36c0a..00000000 --- a/vendor/github.com/d5/tengo/compiler/bytecode.go +++ /dev/null @@ -1,90 +0,0 @@ -package compiler - -import ( - "encoding/gob" - "fmt" - "io" - "reflect" - - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/objects" -) - -// Bytecode is a compiled instructions and constants. -type Bytecode struct { - FileSet *source.FileSet - MainFunction *objects.CompiledFunction - Constants []objects.Object -} - -// Encode writes Bytecode data to the writer. -func (b *Bytecode) Encode(w io.Writer) error { - enc := gob.NewEncoder(w) - - if err := enc.Encode(b.FileSet); err != nil { - return err - } - - if err := enc.Encode(b.MainFunction); err != nil { - return err - } - - // constants - return enc.Encode(b.Constants) -} - -// CountObjects returns the number of objects found in Constants. -func (b *Bytecode) CountObjects() int { - n := 0 - - for _, c := range b.Constants { - n += objects.CountObjects(c) - } - - return n -} - -// FormatInstructions returns human readable string representations of -// compiled instructions. -func (b *Bytecode) FormatInstructions() []string { - return FormatInstructions(b.MainFunction.Instructions, 0) -} - -// FormatConstants returns human readable string representations of -// compiled constants. -func (b *Bytecode) FormatConstants() (output []string) { - for cidx, cn := range b.Constants { - switch cn := cn.(type) { - case *objects.CompiledFunction: - output = append(output, fmt.Sprintf("[% 3d] (Compiled Function|%p)", cidx, &cn)) - for _, l := range FormatInstructions(cn.Instructions, 0) { - output = append(output, fmt.Sprintf(" %s", l)) - } - default: - output = append(output, fmt.Sprintf("[% 3d] %s (%s|%p)", cidx, cn, reflect.TypeOf(cn).Elem().Name(), &cn)) - } - } - - return -} - -func init() { - gob.Register(&source.FileSet{}) - gob.Register(&source.File{}) - gob.Register(&objects.Array{}) - gob.Register(&objects.Bool{}) - gob.Register(&objects.Bytes{}) - gob.Register(&objects.Char{}) - gob.Register(&objects.Closure{}) - gob.Register(&objects.CompiledFunction{}) - gob.Register(&objects.Error{}) - gob.Register(&objects.Float{}) - gob.Register(&objects.ImmutableArray{}) - gob.Register(&objects.ImmutableMap{}) - gob.Register(&objects.Int{}) - gob.Register(&objects.Map{}) - gob.Register(&objects.String{}) - gob.Register(&objects.Time{}) - gob.Register(&objects.Undefined{}) - gob.Register(&objects.UserFunction{}) -} diff --git a/vendor/github.com/d5/tengo/compiler/bytecode_decode.go b/vendor/github.com/d5/tengo/compiler/bytecode_decode.go deleted file mode 100644 index b3b32a64..00000000 --- a/vendor/github.com/d5/tengo/compiler/bytecode_decode.go +++ /dev/null @@ -1,97 +0,0 @@ -package compiler - -import ( - "encoding/gob" - "fmt" - "io" - - "github.com/d5/tengo/objects" -) - -// Decode reads Bytecode data from the reader. -func (b *Bytecode) Decode(r io.Reader, modules *objects.ModuleMap) error { - if modules == nil { - modules = objects.NewModuleMap() - } - - dec := gob.NewDecoder(r) - - if err := dec.Decode(&b.FileSet); err != nil { - return err - } - // TODO: files in b.FileSet.File does not have their 'set' field properly set to b.FileSet - // as it's private field and not serialized by gob encoder/decoder. - - if err := dec.Decode(&b.MainFunction); err != nil { - return err - } - - if err := dec.Decode(&b.Constants); err != nil { - return err - } - for i, v := range b.Constants { - fv, err := fixDecoded(v, modules) - if err != nil { - return err - } - b.Constants[i] = fv - } - - return nil -} - -func fixDecoded(o objects.Object, modules *objects.ModuleMap) (objects.Object, error) { - switch o := o.(type) { - case *objects.Bool: - if o.IsFalsy() { - return objects.FalseValue, nil - } - return objects.TrueValue, nil - case *objects.Undefined: - return objects.UndefinedValue, nil - case *objects.Array: - for i, v := range o.Value { - fv, err := fixDecoded(v, modules) - if err != nil { - return nil, err - } - o.Value[i] = fv - } - case *objects.ImmutableArray: - for i, v := range o.Value { - fv, err := fixDecoded(v, modules) - if err != nil { - return nil, err - } - o.Value[i] = fv - } - case *objects.Map: - for k, v := range o.Value { - fv, err := fixDecoded(v, modules) - if err != nil { - return nil, err - } - o.Value[k] = fv - } - case *objects.ImmutableMap: - modName := moduleName(o) - if mod := modules.GetBuiltinModule(modName); mod != nil { - return mod.AsImmutableMap(modName), nil - } - - for k, v := range o.Value { - // encoding of user function not supported - if _, isUserFunction := v.(*objects.UserFunction); isUserFunction { - return nil, fmt.Errorf("user function not decodable") - } - - fv, err := fixDecoded(v, modules) - if err != nil { - return nil, err - } - o.Value[k] = fv - } - } - - return o, nil -} diff --git a/vendor/github.com/d5/tengo/compiler/bytecode_optimize.go b/vendor/github.com/d5/tengo/compiler/bytecode_optimize.go deleted file mode 100644 index 9526de2e..00000000 --- a/vendor/github.com/d5/tengo/compiler/bytecode_optimize.go +++ /dev/null @@ -1,129 +0,0 @@ -package compiler - -import ( - "fmt" - - "github.com/d5/tengo/objects" -) - -// RemoveDuplicates finds and remove the duplicate values in Constants. -// Note this function mutates Bytecode. -func (b *Bytecode) RemoveDuplicates() { - var deduped []objects.Object - - indexMap := make(map[int]int) // mapping from old constant index to new index - ints := make(map[int64]int) - strings := make(map[string]int) - floats := make(map[float64]int) - chars := make(map[rune]int) - immutableMaps := make(map[string]int) // for modules - - for curIdx, c := range b.Constants { - switch c := c.(type) { - case *objects.CompiledFunction: - // add to deduped list - indexMap[curIdx] = len(deduped) - deduped = append(deduped, c) - case *objects.ImmutableMap: - modName := moduleName(c) - newIdx, ok := immutableMaps[modName] - if modName != "" && ok { - indexMap[curIdx] = newIdx - } else { - newIdx = len(deduped) - immutableMaps[modName] = newIdx - indexMap[curIdx] = newIdx - deduped = append(deduped, c) - } - case *objects.Int: - if newIdx, ok := ints[c.Value]; ok { - indexMap[curIdx] = newIdx - } else { - newIdx = len(deduped) - ints[c.Value] = newIdx - indexMap[curIdx] = newIdx - deduped = append(deduped, c) - } - case *objects.String: - if newIdx, ok := strings[c.Value]; ok { - indexMap[curIdx] = newIdx - } else { - newIdx = len(deduped) - strings[c.Value] = newIdx - indexMap[curIdx] = newIdx - deduped = append(deduped, c) - } - case *objects.Float: - if newIdx, ok := floats[c.Value]; ok { - indexMap[curIdx] = newIdx - } else { - newIdx = len(deduped) - floats[c.Value] = newIdx - indexMap[curIdx] = newIdx - deduped = append(deduped, c) - } - case *objects.Char: - if newIdx, ok := chars[c.Value]; ok { - indexMap[curIdx] = newIdx - } else { - newIdx = len(deduped) - chars[c.Value] = newIdx - indexMap[curIdx] = newIdx - deduped = append(deduped, c) - } - default: - panic(fmt.Errorf("unsupported top-level constant type: %s", c.TypeName())) - } - } - - // replace with de-duplicated constants - b.Constants = deduped - - // update CONST instructions with new indexes - // main function - updateConstIndexes(b.MainFunction.Instructions, indexMap) - // other compiled functions in constants - for _, c := range b.Constants { - switch c := c.(type) { - case *objects.CompiledFunction: - updateConstIndexes(c.Instructions, indexMap) - } - } -} - -func updateConstIndexes(insts []byte, indexMap map[int]int) { - i := 0 - for i < len(insts) { - op := insts[i] - numOperands := OpcodeOperands[op] - _, read := ReadOperands(numOperands, insts[i+1:]) - - switch op { - case OpConstant: - curIdx := int(insts[i+2]) | int(insts[i+1])<<8 - newIdx, ok := indexMap[curIdx] - if !ok { - panic(fmt.Errorf("constant index not found: %d", curIdx)) - } - copy(insts[i:], MakeInstruction(op, newIdx)) - case OpClosure: - curIdx := int(insts[i+2]) | int(insts[i+1])<<8 - numFree := int(insts[i+3]) - newIdx, ok := indexMap[curIdx] - if !ok { - panic(fmt.Errorf("constant index not found: %d", curIdx)) - } - copy(insts[i:], MakeInstruction(op, newIdx, numFree)) - } - - i += 1 + read - } -} - -func moduleName(mod *objects.ImmutableMap) string { - if modName, ok := mod.Value["__module_name__"].(*objects.String); ok { - return modName.Value - } - - return "" -} diff --git a/vendor/github.com/d5/tengo/compiler/compilation_scope.go b/vendor/github.com/d5/tengo/compiler/compilation_scope.go deleted file mode 100644 index 41e7876b..00000000 --- a/vendor/github.com/d5/tengo/compiler/compilation_scope.go +++ /dev/null @@ -1,11 +0,0 @@ -package compiler - -import "github.com/d5/tengo/compiler/source" - -// CompilationScope represents a compiled instructions -// and the last two instructions that were emitted. -type CompilationScope struct { - instructions []byte - symbolInit map[string]bool - sourceMap map[int]source.Pos -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler.go b/vendor/github.com/d5/tengo/compiler/compiler.go deleted file mode 100644 index 8bde5dc9..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler.go +++ /dev/null @@ -1,846 +0,0 @@ -package compiler - -import ( - "fmt" - "io" - "io/ioutil" - "path/filepath" - "reflect" - "strings" - - "github.com/d5/tengo" - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" - "github.com/d5/tengo/objects" -) - -// Compiler compiles the AST into a bytecode. -type Compiler struct { - file *source.File - parent *Compiler - modulePath string - constants []objects.Object - symbolTable *SymbolTable - scopes []CompilationScope - scopeIndex int - modules *objects.ModuleMap - compiledModules map[string]*objects.CompiledFunction - allowFileImport bool - loops []*Loop - loopIndex int - trace io.Writer - indent int -} - -// NewCompiler creates a Compiler. -func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []objects.Object, modules *objects.ModuleMap, trace io.Writer) *Compiler { - mainScope := CompilationScope{ - symbolInit: make(map[string]bool), - sourceMap: make(map[int]source.Pos), - } - - // symbol table - if symbolTable == nil { - symbolTable = NewSymbolTable() - } - - // add builtin functions to the symbol table - for idx, fn := range objects.Builtins { - symbolTable.DefineBuiltin(idx, fn.Name) - } - - // builtin modules - if modules == nil { - modules = objects.NewModuleMap() - } - - return &Compiler{ - file: file, - symbolTable: symbolTable, - constants: constants, - scopes: []CompilationScope{mainScope}, - scopeIndex: 0, - loopIndex: -1, - trace: trace, - modules: modules, - compiledModules: make(map[string]*objects.CompiledFunction), - } -} - -// Compile compiles the AST node. -func (c *Compiler) Compile(node ast.Node) error { - if c.trace != nil { - if node != nil { - defer un(trace(c, fmt.Sprintf("%s (%s)", node.String(), reflect.TypeOf(node).Elem().Name()))) - } else { - defer un(trace(c, "")) - } - } - - switch node := node.(type) { - case *ast.File: - for _, stmt := range node.Stmts { - if err := c.Compile(stmt); err != nil { - return err - } - } - - case *ast.ExprStmt: - if err := c.Compile(node.Expr); err != nil { - return err - } - c.emit(node, OpPop) - - case *ast.IncDecStmt: - op := token.AddAssign - if node.Token == token.Dec { - op = token.SubAssign - } - - return c.compileAssign(node, []ast.Expr{node.Expr}, []ast.Expr{&ast.IntLit{Value: 1}}, op) - - case *ast.ParenExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - case *ast.BinaryExpr: - if node.Token == token.LAnd || node.Token == token.LOr { - return c.compileLogical(node) - } - - if node.Token == token.Less { - if err := c.Compile(node.RHS); err != nil { - return err - } - - if err := c.Compile(node.LHS); err != nil { - return err - } - - c.emit(node, OpBinaryOp, int(token.Greater)) - - return nil - } else if node.Token == token.LessEq { - if err := c.Compile(node.RHS); err != nil { - return err - } - if err := c.Compile(node.LHS); err != nil { - return err - } - - c.emit(node, OpBinaryOp, int(token.GreaterEq)) - - return nil - } - - if err := c.Compile(node.LHS); err != nil { - return err - } - if err := c.Compile(node.RHS); err != nil { - return err - } - - switch node.Token { - case token.Add: - c.emit(node, OpBinaryOp, int(token.Add)) - case token.Sub: - c.emit(node, OpBinaryOp, int(token.Sub)) - case token.Mul: - c.emit(node, OpBinaryOp, int(token.Mul)) - case token.Quo: - c.emit(node, OpBinaryOp, int(token.Quo)) - case token.Rem: - c.emit(node, OpBinaryOp, int(token.Rem)) - case token.Greater: - c.emit(node, OpBinaryOp, int(token.Greater)) - case token.GreaterEq: - c.emit(node, OpBinaryOp, int(token.GreaterEq)) - case token.Equal: - c.emit(node, OpEqual) - case token.NotEqual: - c.emit(node, OpNotEqual) - case token.And: - c.emit(node, OpBinaryOp, int(token.And)) - case token.Or: - c.emit(node, OpBinaryOp, int(token.Or)) - case token.Xor: - c.emit(node, OpBinaryOp, int(token.Xor)) - case token.AndNot: - c.emit(node, OpBinaryOp, int(token.AndNot)) - case token.Shl: - c.emit(node, OpBinaryOp, int(token.Shl)) - case token.Shr: - c.emit(node, OpBinaryOp, int(token.Shr)) - default: - return c.errorf(node, "invalid binary operator: %s", node.Token.String()) - } - - case *ast.IntLit: - c.emit(node, OpConstant, c.addConstant(&objects.Int{Value: node.Value})) - - case *ast.FloatLit: - c.emit(node, OpConstant, c.addConstant(&objects.Float{Value: node.Value})) - - case *ast.BoolLit: - if node.Value { - c.emit(node, OpTrue) - } else { - c.emit(node, OpFalse) - } - - case *ast.StringLit: - if len(node.Value) > tengo.MaxStringLen { - return c.error(node, objects.ErrStringLimit) - } - - c.emit(node, OpConstant, c.addConstant(&objects.String{Value: node.Value})) - - case *ast.CharLit: - c.emit(node, OpConstant, c.addConstant(&objects.Char{Value: node.Value})) - - case *ast.UndefinedLit: - c.emit(node, OpNull) - - case *ast.UnaryExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - switch node.Token { - case token.Not: - c.emit(node, OpLNot) - case token.Sub: - c.emit(node, OpMinus) - case token.Xor: - c.emit(node, OpBComplement) - case token.Add: - // do nothing? - default: - return c.errorf(node, "invalid unary operator: %s", node.Token.String()) - } - - case *ast.IfStmt: - // open new symbol table for the statement - c.symbolTable = c.symbolTable.Fork(true) - defer func() { - c.symbolTable = c.symbolTable.Parent(false) - }() - - if node.Init != nil { - if err := c.Compile(node.Init); err != nil { - return err - } - } - - if err := c.Compile(node.Cond); err != nil { - return err - } - - // first jump placeholder - jumpPos1 := c.emit(node, OpJumpFalsy, 0) - - if err := c.Compile(node.Body); err != nil { - return err - } - - if node.Else != nil { - // second jump placeholder - jumpPos2 := c.emit(node, OpJump, 0) - - // update first jump offset - curPos := len(c.currentInstructions()) - c.changeOperand(jumpPos1, curPos) - - if err := c.Compile(node.Else); err != nil { - return err - } - - // update second jump offset - curPos = len(c.currentInstructions()) - c.changeOperand(jumpPos2, curPos) - } else { - // update first jump offset - curPos := len(c.currentInstructions()) - c.changeOperand(jumpPos1, curPos) - } - - case *ast.ForStmt: - return c.compileForStmt(node) - - case *ast.ForInStmt: - return c.compileForInStmt(node) - - case *ast.BranchStmt: - if node.Token == token.Break { - curLoop := c.currentLoop() - if curLoop == nil { - return c.errorf(node, "break not allowed outside loop") - } - pos := c.emit(node, OpJump, 0) - curLoop.Breaks = append(curLoop.Breaks, pos) - } else if node.Token == token.Continue { - curLoop := c.currentLoop() - if curLoop == nil { - return c.errorf(node, "continue not allowed outside loop") - } - pos := c.emit(node, OpJump, 0) - curLoop.Continues = append(curLoop.Continues, pos) - } else { - panic(fmt.Errorf("invalid branch statement: %s", node.Token.String())) - } - - case *ast.BlockStmt: - if len(node.Stmts) == 0 { - return nil - } - - c.symbolTable = c.symbolTable.Fork(true) - defer func() { - c.symbolTable = c.symbolTable.Parent(false) - }() - - for _, stmt := range node.Stmts { - if err := c.Compile(stmt); err != nil { - return err - } - } - - case *ast.AssignStmt: - if err := c.compileAssign(node, node.LHS, node.RHS, node.Token); err != nil { - return err - } - - case *ast.Ident: - symbol, _, ok := c.symbolTable.Resolve(node.Name) - if !ok { - return c.errorf(node, "unresolved reference '%s'", node.Name) - } - - switch symbol.Scope { - case ScopeGlobal: - c.emit(node, OpGetGlobal, symbol.Index) - case ScopeLocal: - c.emit(node, OpGetLocal, symbol.Index) - case ScopeBuiltin: - c.emit(node, OpGetBuiltin, symbol.Index) - case ScopeFree: - c.emit(node, OpGetFree, symbol.Index) - } - - case *ast.ArrayLit: - for _, elem := range node.Elements { - if err := c.Compile(elem); err != nil { - return err - } - } - - c.emit(node, OpArray, len(node.Elements)) - - case *ast.MapLit: - for _, elt := range node.Elements { - // key - if len(elt.Key) > tengo.MaxStringLen { - return c.error(node, objects.ErrStringLimit) - } - c.emit(node, OpConstant, c.addConstant(&objects.String{Value: elt.Key})) - - // value - if err := c.Compile(elt.Value); err != nil { - return err - } - } - - c.emit(node, OpMap, len(node.Elements)*2) - - case *ast.SelectorExpr: // selector on RHS side - if err := c.Compile(node.Expr); err != nil { - return err - } - - if err := c.Compile(node.Sel); err != nil { - return err - } - - c.emit(node, OpIndex) - - case *ast.IndexExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - if err := c.Compile(node.Index); err != nil { - return err - } - - c.emit(node, OpIndex) - - case *ast.SliceExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - if node.Low != nil { - if err := c.Compile(node.Low); err != nil { - return err - } - } else { - c.emit(node, OpNull) - } - - if node.High != nil { - if err := c.Compile(node.High); err != nil { - return err - } - } else { - c.emit(node, OpNull) - } - - c.emit(node, OpSliceIndex) - - case *ast.FuncLit: - c.enterScope() - - for _, p := range node.Type.Params.List { - s := c.symbolTable.Define(p.Name) - - // function arguments is not assigned directly. - s.LocalAssigned = true - } - - if err := c.Compile(node.Body); err != nil { - return err - } - - // code optimization - c.optimizeFunc(node) - - freeSymbols := c.symbolTable.FreeSymbols() - numLocals := c.symbolTable.MaxSymbols() - instructions, sourceMap := c.leaveScope() - - for _, s := range freeSymbols { - switch s.Scope { - case ScopeLocal: - if !s.LocalAssigned { - // Here, the closure is capturing a local variable that's not yet assigned its value. - // One example is a local recursive function: - // - // func() { - // foo := func(x) { - // // .. - // return foo(x-1) - // } - // } - // - // which translate into - // - // 0000 GETL 0 - // 0002 CLOSURE ? 1 - // 0006 DEFL 0 - // - // . So the local variable (0) is being captured before it's assigned the value. - // - // Solution is to transform the code into something like this: - // - // func() { - // foo := undefined - // foo = func(x) { - // // .. - // return foo(x-1) - // } - // } - // - // that is equivalent to - // - // 0000 NULL - // 0001 DEFL 0 - // 0003 GETL 0 - // 0005 CLOSURE ? 1 - // 0009 SETL 0 - // - - c.emit(node, OpNull) - c.emit(node, OpDefineLocal, s.Index) - - s.LocalAssigned = true - } - - c.emit(node, OpGetLocalPtr, s.Index) - case ScopeFree: - c.emit(node, OpGetFreePtr, s.Index) - } - } - - compiledFunction := &objects.CompiledFunction{ - Instructions: instructions, - NumLocals: numLocals, - NumParameters: len(node.Type.Params.List), - VarArgs: node.Type.Params.VarArgs, - SourceMap: sourceMap, - } - - if len(freeSymbols) > 0 { - c.emit(node, OpClosure, c.addConstant(compiledFunction), len(freeSymbols)) - } else { - c.emit(node, OpConstant, c.addConstant(compiledFunction)) - } - - case *ast.ReturnStmt: - if c.symbolTable.Parent(true) == nil { - // outside the function - return c.errorf(node, "return not allowed outside function") - } - - if node.Result == nil { - c.emit(node, OpReturn, 0) - } else { - if err := c.Compile(node.Result); err != nil { - return err - } - - c.emit(node, OpReturn, 1) - } - - case *ast.CallExpr: - if err := c.Compile(node.Func); err != nil { - return err - } - - for _, arg := range node.Args { - if err := c.Compile(arg); err != nil { - return err - } - } - - c.emit(node, OpCall, len(node.Args)) - - case *ast.ImportExpr: - if node.ModuleName == "" { - return c.errorf(node, "empty module name") - } - - if mod := c.modules.Get(node.ModuleName); mod != nil { - v, err := mod.Import(node.ModuleName) - if err != nil { - return err - } - - switch v := v.(type) { - case []byte: // module written in Tengo - compiled, err := c.compileModule(node, node.ModuleName, node.ModuleName, v) - if err != nil { - return err - } - c.emit(node, OpConstant, c.addConstant(compiled)) - c.emit(node, OpCall, 0) - case objects.Object: // builtin module - c.emit(node, OpConstant, c.addConstant(v)) - default: - panic(fmt.Errorf("invalid import value type: %T", v)) - } - } else if c.allowFileImport { - moduleName := node.ModuleName - if !strings.HasSuffix(moduleName, ".tengo") { - moduleName += ".tengo" - } - - modulePath, err := filepath.Abs(moduleName) - if err != nil { - return c.errorf(node, "module file path error: %s", err.Error()) - } - - if err := c.checkCyclicImports(node, modulePath); err != nil { - return err - } - - moduleSrc, err := ioutil.ReadFile(moduleName) - if err != nil { - return c.errorf(node, "module file read error: %s", err.Error()) - } - - compiled, err := c.compileModule(node, moduleName, modulePath, moduleSrc) - if err != nil { - return err - } - c.emit(node, OpConstant, c.addConstant(compiled)) - c.emit(node, OpCall, 0) - } else { - return c.errorf(node, "module '%s' not found", node.ModuleName) - } - - case *ast.ExportStmt: - // export statement must be in top-level scope - if c.scopeIndex != 0 { - return c.errorf(node, "export not allowed inside function") - } - - // export statement is simply ignore when compiling non-module code - if c.parent == nil { - break - } - - if err := c.Compile(node.Result); err != nil { - return err - } - - c.emit(node, OpImmutable) - c.emit(node, OpReturn, 1) - - case *ast.ErrorExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - c.emit(node, OpError) - - case *ast.ImmutableExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - c.emit(node, OpImmutable) - - case *ast.CondExpr: - if err := c.Compile(node.Cond); err != nil { - return err - } - - // first jump placeholder - jumpPos1 := c.emit(node, OpJumpFalsy, 0) - - if err := c.Compile(node.True); err != nil { - return err - } - - // second jump placeholder - jumpPos2 := c.emit(node, OpJump, 0) - - // update first jump offset - curPos := len(c.currentInstructions()) - c.changeOperand(jumpPos1, curPos) - - if err := c.Compile(node.False); err != nil { - return err - } - - // update second jump offset - curPos = len(c.currentInstructions()) - c.changeOperand(jumpPos2, curPos) - } - - return nil -} - -// Bytecode returns a compiled bytecode. -func (c *Compiler) Bytecode() *Bytecode { - return &Bytecode{ - FileSet: c.file.Set(), - MainFunction: &objects.CompiledFunction{ - Instructions: c.currentInstructions(), - SourceMap: c.currentSourceMap(), - }, - Constants: c.constants, - } -} - -// EnableFileImport enables or disables module loading from local files. -// Local file modules are disabled by default. -func (c *Compiler) EnableFileImport(enable bool) { - c.allowFileImport = enable -} - -func (c *Compiler) fork(file *source.File, modulePath string, symbolTable *SymbolTable) *Compiler { - child := NewCompiler(file, symbolTable, nil, c.modules, c.trace) - child.modulePath = modulePath // module file path - child.parent = c // parent to set to current compiler - - return child -} - -func (c *Compiler) error(node ast.Node, err error) error { - return &Error{ - fileSet: c.file.Set(), - node: node, - error: err, - } -} - -func (c *Compiler) errorf(node ast.Node, format string, args ...interface{}) error { - return &Error{ - fileSet: c.file.Set(), - node: node, - error: fmt.Errorf(format, args...), - } -} - -func (c *Compiler) addConstant(o objects.Object) int { - if c.parent != nil { - // module compilers will use their parent's constants array - return c.parent.addConstant(o) - } - - c.constants = append(c.constants, o) - - if c.trace != nil { - c.printTrace(fmt.Sprintf("CONST %04d %s", len(c.constants)-1, o)) - } - - return len(c.constants) - 1 -} - -func (c *Compiler) addInstruction(b []byte) int { - posNewIns := len(c.currentInstructions()) - - c.scopes[c.scopeIndex].instructions = append(c.currentInstructions(), b...) - - return posNewIns -} - -func (c *Compiler) replaceInstruction(pos int, inst []byte) { - copy(c.currentInstructions()[pos:], inst) - - if c.trace != nil { - c.printTrace(fmt.Sprintf("REPLC %s", - FormatInstructions(c.scopes[c.scopeIndex].instructions[pos:], pos)[0])) - } -} - -func (c *Compiler) changeOperand(opPos int, operand ...int) { - op := Opcode(c.currentInstructions()[opPos]) - inst := MakeInstruction(op, operand...) - - c.replaceInstruction(opPos, inst) -} - -// optimizeFunc performs some code-level optimization for the current function instructions -// it removes unreachable (dead code) instructions and adds "returns" instruction if needed. -func (c *Compiler) optimizeFunc(node ast.Node) { - // any instructions between RETURN and the function end - // or instructions between RETURN and jump target position - // are considered as unreachable. - - // pass 1. identify all jump destinations - dsts := make(map[int]bool) - iterateInstructions(c.scopes[c.scopeIndex].instructions, func(pos int, opcode Opcode, operands []int) bool { - switch opcode { - case OpJump, OpJumpFalsy, OpAndJump, OpOrJump: - dsts[operands[0]] = true - } - - return true - }) - - var newInsts []byte - - // pass 2. eliminate dead code - posMap := make(map[int]int) // old position to new position - var dstIdx int - var deadCode bool - iterateInstructions(c.scopes[c.scopeIndex].instructions, func(pos int, opcode Opcode, operands []int) bool { - switch { - case opcode == OpReturn: - if deadCode { - return true - } - deadCode = true - case dsts[pos]: - dstIdx++ - deadCode = false - case deadCode: - return true - } - - posMap[pos] = len(newInsts) - newInsts = append(newInsts, MakeInstruction(opcode, operands...)...) - return true - }) - - // pass 3. update jump positions - var lastOp Opcode - var appendReturn bool - endPos := len(c.scopes[c.scopeIndex].instructions) - iterateInstructions(newInsts, func(pos int, opcode Opcode, operands []int) bool { - switch opcode { - case OpJump, OpJumpFalsy, OpAndJump, OpOrJump: - newDst, ok := posMap[operands[0]] - if ok { - copy(newInsts[pos:], MakeInstruction(opcode, newDst)) - } else if endPos == operands[0] { - // there's a jump instruction that jumps to the end of function - // compiler should append "return". - appendReturn = true - } else { - panic(fmt.Errorf("invalid jump position: %d", newDst)) - } - } - lastOp = opcode - return true - }) - if lastOp != OpReturn { - appendReturn = true - } - - // pass 4. update source map - newSourceMap := make(map[int]source.Pos) - for pos, srcPos := range c.scopes[c.scopeIndex].sourceMap { - newPos, ok := posMap[pos] - if ok { - newSourceMap[newPos] = srcPos - } - } - - c.scopes[c.scopeIndex].instructions = newInsts - c.scopes[c.scopeIndex].sourceMap = newSourceMap - - // append "return" - if appendReturn { - c.emit(node, OpReturn, 0) - } -} - -func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int { - filePos := source.NoPos - if node != nil { - filePos = node.Pos() - } - - inst := MakeInstruction(opcode, operands...) - pos := c.addInstruction(inst) - c.scopes[c.scopeIndex].sourceMap[pos] = filePos - - if c.trace != nil { - c.printTrace(fmt.Sprintf("EMIT %s", - FormatInstructions(c.scopes[c.scopeIndex].instructions[pos:], pos)[0])) - } - - return pos -} - -func (c *Compiler) printTrace(a ...interface{}) { - const ( - dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " - n = len(dots) - ) - - i := 2 * c.indent - for i > n { - _, _ = fmt.Fprint(c.trace, dots) - i -= n - } - _, _ = fmt.Fprint(c.trace, dots[0:i]) - _, _ = fmt.Fprintln(c.trace, a...) -} - -func trace(c *Compiler, msg string) *Compiler { - c.printTrace(msg, "{") - c.indent++ - - return c -} - -func un(c *Compiler) { - c.indent-- - c.printTrace("}") -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_assign.go b/vendor/github.com/d5/tengo/compiler/compiler_assign.go deleted file mode 100644 index 59296f6f..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_assign.go +++ /dev/null @@ -1,133 +0,0 @@ -package compiler - -import ( - "fmt" - - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/token" -) - -func (c *Compiler) compileAssign(node ast.Node, lhs, rhs []ast.Expr, op token.Token) error { - numLHS, numRHS := len(lhs), len(rhs) - if numLHS > 1 || numRHS > 1 { - return c.errorf(node, "tuple assignment not allowed") - } - - // resolve and compile left-hand side - ident, selectors := resolveAssignLHS(lhs[0]) - numSel := len(selectors) - - if op == token.Define && numSel > 0 { - // using selector on new variable does not make sense - return c.errorf(node, "operator ':=' not allowed with selector") - } - - symbol, depth, exists := c.symbolTable.Resolve(ident) - if op == token.Define { - if depth == 0 && exists { - return c.errorf(node, "'%s' redeclared in this block", ident) - } - - symbol = c.symbolTable.Define(ident) - } else { - if !exists { - return c.errorf(node, "unresolved reference '%s'", ident) - } - } - - // +=, -=, *=, /= - if op != token.Assign && op != token.Define { - if err := c.Compile(lhs[0]); err != nil { - return err - } - } - - // compile RHSs - for _, expr := range rhs { - if err := c.Compile(expr); err != nil { - return err - } - } - - switch op { - case token.AddAssign: - c.emit(node, OpBinaryOp, int(token.Add)) - case token.SubAssign: - c.emit(node, OpBinaryOp, int(token.Sub)) - case token.MulAssign: - c.emit(node, OpBinaryOp, int(token.Mul)) - case token.QuoAssign: - c.emit(node, OpBinaryOp, int(token.Quo)) - case token.RemAssign: - c.emit(node, OpBinaryOp, int(token.Rem)) - case token.AndAssign: - c.emit(node, OpBinaryOp, int(token.And)) - case token.OrAssign: - c.emit(node, OpBinaryOp, int(token.Or)) - case token.AndNotAssign: - c.emit(node, OpBinaryOp, int(token.AndNot)) - case token.XorAssign: - c.emit(node, OpBinaryOp, int(token.Xor)) - case token.ShlAssign: - c.emit(node, OpBinaryOp, int(token.Shl)) - case token.ShrAssign: - c.emit(node, OpBinaryOp, int(token.Shr)) - } - - // compile selector expressions (right to left) - for i := numSel - 1; i >= 0; i-- { - if err := c.Compile(selectors[i]); err != nil { - return err - } - } - - switch symbol.Scope { - case ScopeGlobal: - if numSel > 0 { - c.emit(node, OpSetSelGlobal, symbol.Index, numSel) - } else { - c.emit(node, OpSetGlobal, symbol.Index) - } - case ScopeLocal: - if numSel > 0 { - c.emit(node, OpSetSelLocal, symbol.Index, numSel) - } else { - if op == token.Define && !symbol.LocalAssigned { - c.emit(node, OpDefineLocal, symbol.Index) - } else { - c.emit(node, OpSetLocal, symbol.Index) - } - } - - // mark the symbol as local-assigned - symbol.LocalAssigned = true - case ScopeFree: - if numSel > 0 { - c.emit(node, OpSetSelFree, symbol.Index, numSel) - } else { - c.emit(node, OpSetFree, symbol.Index) - } - default: - panic(fmt.Errorf("invalid assignment variable scope: %s", symbol.Scope)) - } - - return nil -} - -func resolveAssignLHS(expr ast.Expr) (name string, selectors []ast.Expr) { - switch term := expr.(type) { - case *ast.SelectorExpr: - name, selectors = resolveAssignLHS(term.Expr) - selectors = append(selectors, term.Sel) - return - - case *ast.IndexExpr: - name, selectors = resolveAssignLHS(term.Expr) - selectors = append(selectors, term.Index) - - case *ast.Ident: - name = term.Name - } - - return -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_for.go b/vendor/github.com/d5/tengo/compiler/compiler_for.go deleted file mode 100644 index e7b7b5f1..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_for.go +++ /dev/null @@ -1,181 +0,0 @@ -package compiler - -import ( - "github.com/d5/tengo/compiler/ast" -) - -func (c *Compiler) compileForStmt(stmt *ast.ForStmt) error { - c.symbolTable = c.symbolTable.Fork(true) - defer func() { - c.symbolTable = c.symbolTable.Parent(false) - }() - - // init statement - if stmt.Init != nil { - if err := c.Compile(stmt.Init); err != nil { - return err - } - } - - // pre-condition position - preCondPos := len(c.currentInstructions()) - - // condition expression - postCondPos := -1 - if stmt.Cond != nil { - if err := c.Compile(stmt.Cond); err != nil { - return err - } - // condition jump position - postCondPos = c.emit(stmt, OpJumpFalsy, 0) - } - - // enter loop - loop := c.enterLoop() - - // body statement - if err := c.Compile(stmt.Body); err != nil { - c.leaveLoop() - return err - } - - c.leaveLoop() - - // post-body position - postBodyPos := len(c.currentInstructions()) - - // post statement - if stmt.Post != nil { - if err := c.Compile(stmt.Post); err != nil { - return err - } - } - - // back to condition - c.emit(stmt, OpJump, preCondPos) - - // post-statement position - postStmtPos := len(c.currentInstructions()) - if postCondPos >= 0 { - c.changeOperand(postCondPos, postStmtPos) - } - - // update all break/continue jump positions - for _, pos := range loop.Breaks { - c.changeOperand(pos, postStmtPos) - } - for _, pos := range loop.Continues { - c.changeOperand(pos, postBodyPos) - } - - return nil -} - -func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error { - c.symbolTable = c.symbolTable.Fork(true) - defer func() { - c.symbolTable = c.symbolTable.Parent(false) - }() - - // for-in statement is compiled like following: - // - // for :it := iterator(iterable); :it.next(); { - // k, v := :it.get() // DEFINE operator - // - // ... body ... - // } - // - // ":it" is a local variable but will be conflict with other user variables - // because character ":" is not allowed. - - // init - // :it = iterator(iterable) - itSymbol := c.symbolTable.Define(":it") - if err := c.Compile(stmt.Iterable); err != nil { - return err - } - c.emit(stmt, OpIteratorInit) - if itSymbol.Scope == ScopeGlobal { - c.emit(stmt, OpSetGlobal, itSymbol.Index) - } else { - c.emit(stmt, OpDefineLocal, itSymbol.Index) - } - - // pre-condition position - preCondPos := len(c.currentInstructions()) - - // condition - // :it.HasMore() - if itSymbol.Scope == ScopeGlobal { - c.emit(stmt, OpGetGlobal, itSymbol.Index) - } else { - c.emit(stmt, OpGetLocal, itSymbol.Index) - } - c.emit(stmt, OpIteratorNext) - - // condition jump position - postCondPos := c.emit(stmt, OpJumpFalsy, 0) - - // enter loop - loop := c.enterLoop() - - // assign key variable - if stmt.Key.Name != "_" { - keySymbol := c.symbolTable.Define(stmt.Key.Name) - if itSymbol.Scope == ScopeGlobal { - c.emit(stmt, OpGetGlobal, itSymbol.Index) - } else { - c.emit(stmt, OpGetLocal, itSymbol.Index) - } - c.emit(stmt, OpIteratorKey) - if keySymbol.Scope == ScopeGlobal { - c.emit(stmt, OpSetGlobal, keySymbol.Index) - } else { - c.emit(stmt, OpDefineLocal, keySymbol.Index) - } - } - - // assign value variable - if stmt.Value.Name != "_" { - valueSymbol := c.symbolTable.Define(stmt.Value.Name) - if itSymbol.Scope == ScopeGlobal { - c.emit(stmt, OpGetGlobal, itSymbol.Index) - } else { - c.emit(stmt, OpGetLocal, itSymbol.Index) - } - c.emit(stmt, OpIteratorValue) - if valueSymbol.Scope == ScopeGlobal { - c.emit(stmt, OpSetGlobal, valueSymbol.Index) - } else { - c.emit(stmt, OpDefineLocal, valueSymbol.Index) - } - } - - // body statement - if err := c.Compile(stmt.Body); err != nil { - c.leaveLoop() - return err - } - - c.leaveLoop() - - // post-body position - postBodyPos := len(c.currentInstructions()) - - // back to condition - c.emit(stmt, OpJump, preCondPos) - - // post-statement position - postStmtPos := len(c.currentInstructions()) - c.changeOperand(postCondPos, postStmtPos) - - // update all break/continue jump positions - for _, pos := range loop.Breaks { - c.changeOperand(pos, postStmtPos) - } - for _, pos := range loop.Continues { - c.changeOperand(pos, postBodyPos) - } - - return nil -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_logical.go b/vendor/github.com/d5/tengo/compiler/compiler_logical.go deleted file mode 100644 index 68c96759..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_logical.go +++ /dev/null @@ -1,30 +0,0 @@ -package compiler - -import ( - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/token" -) - -func (c *Compiler) compileLogical(node *ast.BinaryExpr) error { - // left side term - if err := c.Compile(node.LHS); err != nil { - return err - } - - // jump position - var jumpPos int - if node.Token == token.LAnd { - jumpPos = c.emit(node, OpAndJump, 0) - } else { - jumpPos = c.emit(node, OpOrJump, 0) - } - - // right side term - if err := c.Compile(node.RHS); err != nil { - return err - } - - c.changeOperand(jumpPos, len(c.currentInstructions())) - - return nil -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_loops.go b/vendor/github.com/d5/tengo/compiler/compiler_loops.go deleted file mode 100644 index 0659ce73..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_loops.go +++ /dev/null @@ -1,31 +0,0 @@ -package compiler - -func (c *Compiler) enterLoop() *Loop { - loop := &Loop{} - - c.loops = append(c.loops, loop) - c.loopIndex++ - - if c.trace != nil { - c.printTrace("LOOPE", c.loopIndex) - } - - return loop -} - -func (c *Compiler) leaveLoop() { - if c.trace != nil { - c.printTrace("LOOPL", c.loopIndex) - } - - c.loops = c.loops[:len(c.loops)-1] - c.loopIndex-- -} - -func (c *Compiler) currentLoop() *Loop { - if c.loopIndex >= 0 { - return c.loops[c.loopIndex] - } - - return nil -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_module.go b/vendor/github.com/d5/tengo/compiler/compiler_module.go deleted file mode 100644 index 8a3671ce..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_module.go +++ /dev/null @@ -1,79 +0,0 @@ -package compiler - -import ( - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/parser" - "github.com/d5/tengo/objects" -) - -func (c *Compiler) checkCyclicImports(node ast.Node, modulePath string) error { - if c.modulePath == modulePath { - return c.errorf(node, "cyclic module import: %s", modulePath) - } else if c.parent != nil { - return c.parent.checkCyclicImports(node, modulePath) - } - - return nil -} - -func (c *Compiler) compileModule(node ast.Node, moduleName, modulePath string, src []byte) (*objects.CompiledFunction, error) { - if err := c.checkCyclicImports(node, modulePath); err != nil { - return nil, err - } - - compiledModule, exists := c.loadCompiledModule(modulePath) - if exists { - return compiledModule, nil - } - - modFile := c.file.Set().AddFile(moduleName, -1, len(src)) - p := parser.NewParser(modFile, src, nil) - file, err := p.ParseFile() - if err != nil { - return nil, err - } - - symbolTable := NewSymbolTable() - - // inherit builtin functions - for _, sym := range c.symbolTable.BuiltinSymbols() { - symbolTable.DefineBuiltin(sym.Index, sym.Name) - } - - // no global scope for the module - symbolTable = symbolTable.Fork(false) - - // compile module - moduleCompiler := c.fork(modFile, modulePath, symbolTable) - if err := moduleCompiler.Compile(file); err != nil { - return nil, err - } - - // code optimization - moduleCompiler.optimizeFunc(node) - - compiledFunc := moduleCompiler.Bytecode().MainFunction - compiledFunc.NumLocals = symbolTable.MaxSymbols() - - c.storeCompiledModule(modulePath, compiledFunc) - - return compiledFunc, nil -} - -func (c *Compiler) loadCompiledModule(modulePath string) (mod *objects.CompiledFunction, ok bool) { - if c.parent != nil { - return c.parent.loadCompiledModule(modulePath) - } - - mod, ok = c.compiledModules[modulePath] - - return -} - -func (c *Compiler) storeCompiledModule(modulePath string, module *objects.CompiledFunction) { - if c.parent != nil { - c.parent.storeCompiledModule(modulePath, module) - } - - c.compiledModules[modulePath] = module -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_scopes.go b/vendor/github.com/d5/tengo/compiler/compiler_scopes.go deleted file mode 100644 index b63f915a..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_scopes.go +++ /dev/null @@ -1,43 +0,0 @@ -package compiler - -import "github.com/d5/tengo/compiler/source" - -func (c *Compiler) currentInstructions() []byte { - return c.scopes[c.scopeIndex].instructions -} - -func (c *Compiler) currentSourceMap() map[int]source.Pos { - return c.scopes[c.scopeIndex].sourceMap -} - -func (c *Compiler) enterScope() { - scope := CompilationScope{ - symbolInit: make(map[string]bool), - sourceMap: make(map[int]source.Pos), - } - - c.scopes = append(c.scopes, scope) - c.scopeIndex++ - - c.symbolTable = c.symbolTable.Fork(false) - - if c.trace != nil { - c.printTrace("SCOPE", c.scopeIndex) - } -} - -func (c *Compiler) leaveScope() (instructions []byte, sourceMap map[int]source.Pos) { - instructions = c.currentInstructions() - sourceMap = c.currentSourceMap() - - c.scopes = c.scopes[:len(c.scopes)-1] - c.scopeIndex-- - - c.symbolTable = c.symbolTable.Parent(true) - - if c.trace != nil { - c.printTrace("SCOPL", c.scopeIndex) - } - - return -} diff --git a/vendor/github.com/d5/tengo/compiler/emitted_instruction.go b/vendor/github.com/d5/tengo/compiler/emitted_instruction.go deleted file mode 100644 index 8572fb0a..00000000 --- a/vendor/github.com/d5/tengo/compiler/emitted_instruction.go +++ /dev/null @@ -1,8 +0,0 @@ -package compiler - -// EmittedInstruction represents an opcode -// with its emitted position. -type EmittedInstruction struct { - Opcode Opcode - Position int -} diff --git a/vendor/github.com/d5/tengo/compiler/error.go b/vendor/github.com/d5/tengo/compiler/error.go deleted file mode 100644 index 6ac33d42..00000000 --- a/vendor/github.com/d5/tengo/compiler/error.go +++ /dev/null @@ -1,20 +0,0 @@ -package compiler - -import ( - "fmt" - - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/source" -) - -// Error represents a compiler error. -type Error struct { - fileSet *source.FileSet - node ast.Node - error error -} - -func (e *Error) Error() string { - filePos := e.fileSet.Position(e.node.Pos()) - return fmt.Sprintf("Compile Error: %s\n\tat %s", e.error.Error(), filePos) -} diff --git a/vendor/github.com/d5/tengo/compiler/instructions.go b/vendor/github.com/d5/tengo/compiler/instructions.go deleted file mode 100644 index 14dde1d8..00000000 --- a/vendor/github.com/d5/tengo/compiler/instructions.go +++ /dev/null @@ -1,72 +0,0 @@ -package compiler - -import ( - "fmt" -) - -// MakeInstruction returns a bytecode for an opcode and the operands. -func MakeInstruction(opcode Opcode, operands ...int) []byte { - numOperands := OpcodeOperands[opcode] - - totalLen := 1 - for _, w := range numOperands { - totalLen += w - } - - instruction := make([]byte, totalLen) - instruction[0] = byte(opcode) - - offset := 1 - for i, o := range operands { - width := numOperands[i] - switch width { - case 1: - instruction[offset] = byte(o) - case 2: - n := uint16(o) - instruction[offset] = byte(n >> 8) - instruction[offset+1] = byte(n) - } - offset += width - } - - return instruction -} - -// FormatInstructions returns string representation of -// bytecode instructions. -func FormatInstructions(b []byte, posOffset int) []string { - var out []string - - i := 0 - for i < len(b) { - numOperands := OpcodeOperands[Opcode(b[i])] - operands, read := ReadOperands(numOperands, b[i+1:]) - - switch len(numOperands) { - case 0: - out = append(out, fmt.Sprintf("%04d %-7s", posOffset+i, OpcodeNames[Opcode(b[i])])) - case 1: - out = append(out, fmt.Sprintf("%04d %-7s %-5d", posOffset+i, OpcodeNames[Opcode(b[i])], operands[0])) - case 2: - out = append(out, fmt.Sprintf("%04d %-7s %-5d %-5d", posOffset+i, OpcodeNames[Opcode(b[i])], operands[0], operands[1])) - } - - i += 1 + read - } - - return out -} - -func iterateInstructions(b []byte, fn func(pos int, opcode Opcode, operands []int) bool) { - for i := 0; i < len(b); i++ { - numOperands := OpcodeOperands[Opcode(b[i])] - operands, read := ReadOperands(numOperands, b[i+1:]) - - if !fn(i, b[i], operands) { - break - } - - i += read - } -} diff --git a/vendor/github.com/d5/tengo/compiler/loop.go b/vendor/github.com/d5/tengo/compiler/loop.go deleted file mode 100644 index e27cb096..00000000 --- a/vendor/github.com/d5/tengo/compiler/loop.go +++ /dev/null @@ -1,8 +0,0 @@ -package compiler - -// Loop represents a loop construct that -// the compiler uses to track the current loop. -type Loop struct { - Continues []int - Breaks []int -} diff --git a/vendor/github.com/d5/tengo/compiler/module_loader.go b/vendor/github.com/d5/tengo/compiler/module_loader.go deleted file mode 100644 index b050474d..00000000 --- a/vendor/github.com/d5/tengo/compiler/module_loader.go +++ /dev/null @@ -1,4 +0,0 @@ -package compiler - -// ModuleLoader should take a module name and return the module data. -type ModuleLoader func(moduleName string) ([]byte, error) diff --git a/vendor/github.com/d5/tengo/compiler/opcodes.go b/vendor/github.com/d5/tengo/compiler/opcodes.go deleted file mode 100644 index d832ee17..00000000 --- a/vendor/github.com/d5/tengo/compiler/opcodes.go +++ /dev/null @@ -1,155 +0,0 @@ -package compiler - -// Opcode represents a single byte operation code. -type Opcode = byte - -// List of opcodes -const ( - OpConstant Opcode = iota // Load constant - OpBComplement // bitwise complement - OpPop // Pop - OpTrue // Push true - OpFalse // Push false - OpEqual // Equal == - OpNotEqual // Not equal != - OpMinus // Minus - - OpLNot // Logical not ! - OpJumpFalsy // Jump if falsy - OpAndJump // Logical AND jump - OpOrJump // Logical OR jump - OpJump // Jump - OpNull // Push null - OpArray // Array object - OpMap // Map object - OpError // Error object - OpImmutable // Immutable object - OpIndex // Index operation - OpSliceIndex // Slice operation - OpCall // Call function - OpReturn // Return - OpGetGlobal // Get global variable - OpSetGlobal // Set global variable - OpSetSelGlobal // Set global variable using selectors - OpGetLocal // Get local variable - OpSetLocal // Set local variable - OpDefineLocal // Define local variable - OpSetSelLocal // Set local variable using selectors - OpGetFreePtr // Get free variable pointer object - OpGetFree // Get free variables - OpSetFree // Set free variables - OpGetLocalPtr // Get local variable as a pointer - OpSetSelFree // Set free variables using selectors - OpGetBuiltin // Get builtin function - OpClosure // Push closure - OpIteratorInit // Iterator init - OpIteratorNext // Iterator next - OpIteratorKey // Iterator key - OpIteratorValue // Iterator value - OpBinaryOp // Binary Operation -) - -// OpcodeNames is opcode names. -var OpcodeNames = [...]string{ - OpConstant: "CONST", - OpPop: "POP", - OpTrue: "TRUE", - OpFalse: "FALSE", - OpBComplement: "NEG", - OpEqual: "EQL", - OpNotEqual: "NEQ", - OpMinus: "NEG", - OpLNot: "NOT", - OpJumpFalsy: "JMPF", - OpAndJump: "ANDJMP", - OpOrJump: "ORJMP", - OpJump: "JMP", - OpNull: "NULL", - OpGetGlobal: "GETG", - OpSetGlobal: "SETG", - OpSetSelGlobal: "SETSG", - OpArray: "ARR", - OpMap: "MAP", - OpError: "ERROR", - OpImmutable: "IMMUT", - OpIndex: "INDEX", - OpSliceIndex: "SLICE", - OpCall: "CALL", - OpReturn: "RET", - OpGetLocal: "GETL", - OpSetLocal: "SETL", - OpDefineLocal: "DEFL", - OpSetSelLocal: "SETSL", - OpGetBuiltin: "BUILTIN", - OpClosure: "CLOSURE", - OpGetFreePtr: "GETFP", - OpGetFree: "GETF", - OpSetFree: "SETF", - OpGetLocalPtr: "GETLP", - OpSetSelFree: "SETSF", - OpIteratorInit: "ITER", - OpIteratorNext: "ITNXT", - OpIteratorKey: "ITKEY", - OpIteratorValue: "ITVAL", - OpBinaryOp: "BINARYOP", -} - -// OpcodeOperands is the number of operands. -var OpcodeOperands = [...][]int{ - OpConstant: {2}, - OpPop: {}, - OpTrue: {}, - OpFalse: {}, - OpBComplement: {}, - OpEqual: {}, - OpNotEqual: {}, - OpMinus: {}, - OpLNot: {}, - OpJumpFalsy: {2}, - OpAndJump: {2}, - OpOrJump: {2}, - OpJump: {2}, - OpNull: {}, - OpGetGlobal: {2}, - OpSetGlobal: {2}, - OpSetSelGlobal: {2, 1}, - OpArray: {2}, - OpMap: {2}, - OpError: {}, - OpImmutable: {}, - OpIndex: {}, - OpSliceIndex: {}, - OpCall: {1}, - OpReturn: {1}, - OpGetLocal: {1}, - OpSetLocal: {1}, - OpDefineLocal: {1}, - OpSetSelLocal: {1, 1}, - OpGetBuiltin: {1}, - OpClosure: {2, 1}, - OpGetFreePtr: {1}, - OpGetFree: {1}, - OpSetFree: {1}, - OpGetLocalPtr: {1}, - OpSetSelFree: {1, 1}, - OpIteratorInit: {}, - OpIteratorNext: {}, - OpIteratorKey: {}, - OpIteratorValue: {}, - OpBinaryOp: {1}, -} - -// ReadOperands reads operands from the bytecode. -func ReadOperands(numOperands []int, ins []byte) (operands []int, offset int) { - for _, width := range numOperands { - switch width { - case 1: - operands = append(operands, int(ins[offset])) - case 2: - operands = append(operands, int(ins[offset+1])|int(ins[offset])<<8) - } - - offset += width - } - - return -} diff --git a/vendor/github.com/d5/tengo/compiler/parser/error.go b/vendor/github.com/d5/tengo/compiler/parser/error.go deleted file mode 100644 index 80544fbd..00000000 --- a/vendor/github.com/d5/tengo/compiler/parser/error.go +++ /dev/null @@ -1,21 +0,0 @@ -package parser - -import ( - "fmt" - - "github.com/d5/tengo/compiler/source" -) - -// Error represents a parser error. -type Error struct { - Pos source.FilePos - Msg string -} - -func (e Error) Error() string { - if e.Pos.Filename != "" || e.Pos.IsValid() { - return fmt.Sprintf("Parse Error: %s\n\tat %s", e.Msg, e.Pos) - } - - return fmt.Sprintf("Parse Error: %s", e.Msg) -} diff --git a/vendor/github.com/d5/tengo/compiler/parser/error_list.go b/vendor/github.com/d5/tengo/compiler/parser/error_list.go deleted file mode 100644 index 599eaf7d..00000000 --- a/vendor/github.com/d5/tengo/compiler/parser/error_list.go +++ /dev/null @@ -1,68 +0,0 @@ -package parser - -import ( - "fmt" - "sort" - - "github.com/d5/tengo/compiler/source" -) - -// ErrorList is a collection of parser errors. -type ErrorList []*Error - -// Add adds a new parser error to the collection. -func (p *ErrorList) Add(pos source.FilePos, msg string) { - *p = append(*p, &Error{pos, msg}) -} - -// Len returns the number of elements in the collection. -func (p ErrorList) Len() int { - return len(p) -} - -func (p ErrorList) Swap(i, j int) { - p[i], p[j] = p[j], p[i] -} - -func (p ErrorList) Less(i, j int) bool { - e := &p[i].Pos - f := &p[j].Pos - - if e.Filename != f.Filename { - return e.Filename < f.Filename - } - - if e.Line != f.Line { - return e.Line < f.Line - } - - if e.Column != f.Column { - return e.Column < f.Column - } - - return p[i].Msg < p[j].Msg -} - -// Sort sorts the collection. -func (p ErrorList) Sort() { - sort.Sort(p) -} - -func (p ErrorList) Error() string { - switch len(p) { - case 0: - return "no errors" - case 1: - return p[0].Error() - } - return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1) -} - -// Err returns an error. -func (p ErrorList) Err() error { - if len(p) == 0 { - return nil - } - - return p -} diff --git a/vendor/github.com/d5/tengo/compiler/parser/parse_source.go b/vendor/github.com/d5/tengo/compiler/parser/parse_source.go deleted file mode 100644 index 5c71436d..00000000 --- a/vendor/github.com/d5/tengo/compiler/parser/parse_source.go +++ /dev/null @@ -1,17 +0,0 @@ -package parser - -import ( - "io" - - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/source" -) - -// ParseSource parses source code 'src' and builds an AST. -func ParseSource(filename string, src []byte, trace io.Writer) (res *ast.File, err error) { - fileSet := source.NewFileSet() - file := fileSet.AddFile(filename, -1, len(src)) - - p := NewParser(file, src, trace) - return p.ParseFile() -} diff --git a/vendor/github.com/d5/tengo/compiler/parser/parser.go b/vendor/github.com/d5/tengo/compiler/parser/parser.go deleted file mode 100644 index 27dd48f0..00000000 --- a/vendor/github.com/d5/tengo/compiler/parser/parser.go +++ /dev/null @@ -1,1216 +0,0 @@ -/* - Parser parses the Tengo source files. - - Parser is a modified version of Go's parser implementation. - - Copyright 2009 The Go Authors. All rights reserved. - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. -*/ - -package parser - -import ( - "fmt" - "io" - "strconv" - - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/scanner" - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -type bailout struct{} - -// Parser parses the Tengo source files. -type Parser struct { - file *source.File - errors ErrorList - scanner *scanner.Scanner - pos source.Pos - token token.Token - tokenLit string - exprLevel int // < 0: in control clause, >= 0: in expression - syncPos source.Pos // last sync position - syncCount int // number of advance calls without progress - trace bool - indent int - traceOut io.Writer -} - -// NewParser creates a Parser. -func NewParser(file *source.File, src []byte, trace io.Writer) *Parser { - p := &Parser{ - file: file, - trace: trace != nil, - traceOut: trace, - } - - p.scanner = scanner.NewScanner(p.file, src, func(pos source.FilePos, msg string) { - p.errors.Add(pos, msg) - }, 0) - - p.next() - - return p -} - -// ParseFile parses the source and returns an AST file unit. -func (p *Parser) ParseFile() (file *ast.File, err error) { - defer func() { - if e := recover(); e != nil { - if _, ok := e.(bailout); !ok { - panic(e) - } - } - - p.errors.Sort() - err = p.errors.Err() - }() - - if p.trace { - defer un(trace(p, "File")) - } - - if p.errors.Len() > 0 { - return nil, p.errors.Err() - } - - stmts := p.parseStmtList() - if p.errors.Len() > 0 { - return nil, p.errors.Err() - } - - file = &ast.File{ - InputFile: p.file, - Stmts: stmts, - } - - return -} - -func (p *Parser) parseExpr() ast.Expr { - if p.trace { - defer un(trace(p, "Expression")) - } - - expr := p.parseBinaryExpr(token.LowestPrec + 1) - - // ternary conditional expression - if p.token == token.Question { - return p.parseCondExpr(expr) - } - - return expr -} - -func (p *Parser) parseBinaryExpr(prec1 int) ast.Expr { - if p.trace { - defer un(trace(p, "BinaryExpression")) - } - - x := p.parseUnaryExpr() - - for { - op, prec := p.token, p.token.Precedence() - if prec < prec1 { - return x - } - - pos := p.expect(op) - - y := p.parseBinaryExpr(prec + 1) - - x = &ast.BinaryExpr{ - LHS: x, - RHS: y, - Token: op, - TokenPos: pos, - } - } -} - -func (p *Parser) parseCondExpr(cond ast.Expr) ast.Expr { - questionPos := p.expect(token.Question) - - trueExpr := p.parseExpr() - - colonPos := p.expect(token.Colon) - - falseExpr := p.parseExpr() - - return &ast.CondExpr{ - Cond: cond, - True: trueExpr, - False: falseExpr, - QuestionPos: questionPos, - ColonPos: colonPos, - } -} - -func (p *Parser) parseUnaryExpr() ast.Expr { - if p.trace { - defer un(trace(p, "UnaryExpression")) - } - - switch p.token { - case token.Add, token.Sub, token.Not, token.Xor: - pos, op := p.pos, p.token - p.next() - x := p.parseUnaryExpr() - return &ast.UnaryExpr{ - Token: op, - TokenPos: pos, - Expr: x, - } - } - - return p.parsePrimaryExpr() -} - -func (p *Parser) parsePrimaryExpr() ast.Expr { - if p.trace { - defer un(trace(p, "PrimaryExpression")) - } - - x := p.parseOperand() - -L: - for { - switch p.token { - case token.Period: - p.next() - - switch p.token { - case token.Ident: - x = p.parseSelector(x) - default: - pos := p.pos - p.errorExpected(pos, "selector") - p.advance(stmtStart) - return &ast.BadExpr{From: pos, To: p.pos} - } - case token.LBrack: - x = p.parseIndexOrSlice(x) - case token.LParen: - x = p.parseCall(x) - default: - break L - } - } - - return x -} - -func (p *Parser) parseCall(x ast.Expr) *ast.CallExpr { - if p.trace { - defer un(trace(p, "Call")) - } - - lparen := p.expect(token.LParen) - p.exprLevel++ - - var list []ast.Expr - for p.token != token.RParen && p.token != token.EOF { - list = append(list, p.parseExpr()) - - if !p.expectComma(token.RParen, "call argument") { - break - } - } - - p.exprLevel-- - rparen := p.expect(token.RParen) - - return &ast.CallExpr{ - Func: x, - LParen: lparen, - RParen: rparen, - Args: list, - } -} - -func (p *Parser) expectComma(closing token.Token, want string) bool { - if p.token == token.Comma { - p.next() - - if p.token == closing { - p.errorExpected(p.pos, want) - return false - } - - return true - } - - if p.token == token.Semicolon && p.tokenLit == "\n" { - p.next() - } - - return false -} - -func (p *Parser) parseIndexOrSlice(x ast.Expr) ast.Expr { - if p.trace { - defer un(trace(p, "IndexOrSlice")) - } - - lbrack := p.expect(token.LBrack) - p.exprLevel++ - - var index [2]ast.Expr - if p.token != token.Colon { - index[0] = p.parseExpr() - } - numColons := 0 - if p.token == token.Colon { - numColons++ - p.next() - - if p.token != token.RBrack && p.token != token.EOF { - index[1] = p.parseExpr() - } - } - - p.exprLevel-- - rbrack := p.expect(token.RBrack) - - if numColons > 0 { - // slice expression - return &ast.SliceExpr{ - Expr: x, - LBrack: lbrack, - RBrack: rbrack, - Low: index[0], - High: index[1], - } - } - - return &ast.IndexExpr{ - Expr: x, - LBrack: lbrack, - RBrack: rbrack, - Index: index[0], - } -} - -func (p *Parser) parseSelector(x ast.Expr) ast.Expr { - if p.trace { - defer un(trace(p, "Selector")) - } - - sel := p.parseIdent() - - return &ast.SelectorExpr{Expr: x, Sel: &ast.StringLit{ - Value: sel.Name, - ValuePos: sel.NamePos, - Literal: sel.Name, - }} -} - -func (p *Parser) parseOperand() ast.Expr { - if p.trace { - defer un(trace(p, "Operand")) - } - - switch p.token { - case token.Ident: - return p.parseIdent() - - case token.Int: - v, _ := strconv.ParseInt(p.tokenLit, 10, 64) - x := &ast.IntLit{ - Value: v, - ValuePos: p.pos, - Literal: p.tokenLit, - } - p.next() - return x - - case token.Float: - v, _ := strconv.ParseFloat(p.tokenLit, 64) - x := &ast.FloatLit{ - Value: v, - ValuePos: p.pos, - Literal: p.tokenLit, - } - p.next() - return x - - case token.Char: - return p.parseCharLit() - - case token.String: - v, _ := strconv.Unquote(p.tokenLit) - x := &ast.StringLit{ - Value: v, - ValuePos: p.pos, - Literal: p.tokenLit, - } - p.next() - return x - - case token.True: - x := &ast.BoolLit{ - Value: true, - ValuePos: p.pos, - Literal: p.tokenLit, - } - p.next() - return x - - case token.False: - x := &ast.BoolLit{ - Value: false, - ValuePos: p.pos, - Literal: p.tokenLit, - } - p.next() - return x - - case token.Undefined: - x := &ast.UndefinedLit{TokenPos: p.pos} - p.next() - return x - - case token.Import: - return p.parseImportExpr() - - case token.LParen: - lparen := p.pos - p.next() - p.exprLevel++ - x := p.parseExpr() - p.exprLevel-- - rparen := p.expect(token.RParen) - return &ast.ParenExpr{ - LParen: lparen, - Expr: x, - RParen: rparen, - } - - case token.LBrack: // array literal - return p.parseArrayLit() - - case token.LBrace: // map literal - return p.parseMapLit() - - case token.Func: // function literal - return p.parseFuncLit() - - case token.Error: // error expression - return p.parseErrorExpr() - - case token.Immutable: // immutable expression - return p.parseImmutableExpr() - } - - pos := p.pos - p.errorExpected(pos, "operand") - p.advance(stmtStart) - return &ast.BadExpr{From: pos, To: p.pos} -} - -func (p *Parser) parseImportExpr() ast.Expr { - pos := p.pos - - p.next() - - p.expect(token.LParen) - - if p.token != token.String { - p.errorExpected(p.pos, "module name") - p.advance(stmtStart) - return &ast.BadExpr{From: pos, To: p.pos} - } - - // module name - moduleName, _ := strconv.Unquote(p.tokenLit) - - expr := &ast.ImportExpr{ - ModuleName: moduleName, - Token: token.Import, - TokenPos: pos, - } - - p.next() - - p.expect(token.RParen) - - return expr -} - -func (p *Parser) parseCharLit() ast.Expr { - if n := len(p.tokenLit); n >= 3 { - if code, _, _, err := strconv.UnquoteChar(p.tokenLit[1:n-1], '\''); err == nil { - x := &ast.CharLit{ - Value: rune(code), - ValuePos: p.pos, - Literal: p.tokenLit, - } - p.next() - return x - } - } - - pos := p.pos - p.error(pos, "illegal char literal") - p.next() - return &ast.BadExpr{ - From: pos, - To: p.pos, - } -} - -func (p *Parser) parseFuncLit() ast.Expr { - if p.trace { - defer un(trace(p, "FuncLit")) - } - - typ := p.parseFuncType() - - p.exprLevel++ - body := p.parseBody() - p.exprLevel-- - - return &ast.FuncLit{ - Type: typ, - Body: body, - } -} - -func (p *Parser) parseArrayLit() ast.Expr { - if p.trace { - defer un(trace(p, "ArrayLit")) - } - - lbrack := p.expect(token.LBrack) - p.exprLevel++ - - var elements []ast.Expr - for p.token != token.RBrack && p.token != token.EOF { - elements = append(elements, p.parseExpr()) - - if !p.expectComma(token.RBrack, "array element") { - break - } - } - - p.exprLevel-- - rbrack := p.expect(token.RBrack) - - return &ast.ArrayLit{ - Elements: elements, - LBrack: lbrack, - RBrack: rbrack, - } -} - -func (p *Parser) parseErrorExpr() ast.Expr { - pos := p.pos - - p.next() - - lparen := p.expect(token.LParen) - value := p.parseExpr() - rparen := p.expect(token.RParen) - - expr := &ast.ErrorExpr{ - ErrorPos: pos, - Expr: value, - LParen: lparen, - RParen: rparen, - } - - return expr -} - -func (p *Parser) parseImmutableExpr() ast.Expr { - pos := p.pos - - p.next() - - lparen := p.expect(token.LParen) - value := p.parseExpr() - rparen := p.expect(token.RParen) - - expr := &ast.ImmutableExpr{ - ErrorPos: pos, - Expr: value, - LParen: lparen, - RParen: rparen, - } - - return expr -} - -func (p *Parser) parseFuncType() *ast.FuncType { - if p.trace { - defer un(trace(p, "FuncType")) - } - - pos := p.expect(token.Func) - params := p.parseIdentList() - - return &ast.FuncType{ - FuncPos: pos, - Params: params, - } -} - -func (p *Parser) parseBody() *ast.BlockStmt { - if p.trace { - defer un(trace(p, "Body")) - } - - lbrace := p.expect(token.LBrace) - list := p.parseStmtList() - rbrace := p.expect(token.RBrace) - - return &ast.BlockStmt{ - LBrace: lbrace, - RBrace: rbrace, - Stmts: list, - } -} - -func (p *Parser) parseStmtList() (list []ast.Stmt) { - if p.trace { - defer un(trace(p, "StatementList")) - } - - for p.token != token.RBrace && p.token != token.EOF { - list = append(list, p.parseStmt()) - } - - return -} - -func (p *Parser) parseIdent() *ast.Ident { - pos := p.pos - name := "_" - - if p.token == token.Ident { - name = p.tokenLit - p.next() - } else { - p.expect(token.Ident) - } - - return &ast.Ident{ - NamePos: pos, - Name: name, - } -} - -func (p *Parser) parseIdentList() *ast.IdentList { - if p.trace { - defer un(trace(p, "IdentList")) - } - - var params []*ast.Ident - lparen := p.expect(token.LParen) - isVarArgs := false - if p.token != token.RParen { - if p.token == token.Ellipsis { - isVarArgs = true - p.next() - } - - params = append(params, p.parseIdent()) - for !isVarArgs && p.token == token.Comma { - p.next() - if p.token == token.Ellipsis { - isVarArgs = true - p.next() - } - params = append(params, p.parseIdent()) - } - } - - rparen := p.expect(token.RParen) - - return &ast.IdentList{ - LParen: lparen, - RParen: rparen, - VarArgs: isVarArgs, - List: params, - } -} - -func (p *Parser) parseStmt() (stmt ast.Stmt) { - if p.trace { - defer un(trace(p, "Statement")) - } - - switch p.token { - case // simple statements - token.Func, token.Error, token.Immutable, token.Ident, token.Int, token.Float, token.Char, token.String, token.True, token.False, - token.Undefined, token.Import, token.LParen, token.LBrace, token.LBrack, - token.Add, token.Sub, token.Mul, token.And, token.Xor, token.Not: - s := p.parseSimpleStmt(false) - p.expectSemi() - return s - case token.Return: - return p.parseReturnStmt() - case token.Export: - return p.parseExportStmt() - case token.If: - return p.parseIfStmt() - case token.For: - return p.parseForStmt() - case token.Break, token.Continue: - return p.parseBranchStmt(p.token) - case token.Semicolon: - s := &ast.EmptyStmt{Semicolon: p.pos, Implicit: p.tokenLit == "\n"} - p.next() - return s - case token.RBrace: - // semicolon may be omitted before a closing "}" - return &ast.EmptyStmt{Semicolon: p.pos, Implicit: true} - default: - pos := p.pos - p.errorExpected(pos, "statement") - p.advance(stmtStart) - return &ast.BadStmt{From: pos, To: p.pos} - } -} - -func (p *Parser) parseForStmt() ast.Stmt { - if p.trace { - defer un(trace(p, "ForStmt")) - } - - pos := p.expect(token.For) - - // for {} - if p.token == token.LBrace { - body := p.parseBlockStmt() - p.expectSemi() - - return &ast.ForStmt{ - ForPos: pos, - Body: body, - } - } - - prevLevel := p.exprLevel - p.exprLevel = -1 - - var s1 ast.Stmt - if p.token != token.Semicolon { // skipping init - s1 = p.parseSimpleStmt(true) - } - - // for _ in seq {} or - // for value in seq {} or - // for key, value in seq {} - if forInStmt, isForIn := s1.(*ast.ForInStmt); isForIn { - forInStmt.ForPos = pos - p.exprLevel = prevLevel - forInStmt.Body = p.parseBlockStmt() - p.expectSemi() - return forInStmt - } - - // for init; cond; post {} - var s2, s3 ast.Stmt - if p.token == token.Semicolon { - p.next() - if p.token != token.Semicolon { - s2 = p.parseSimpleStmt(false) // cond - } - p.expect(token.Semicolon) - if p.token != token.LBrace { - s3 = p.parseSimpleStmt(false) // post - } - } else { - // for cond {} - s2 = s1 - s1 = nil - } - - // body - p.exprLevel = prevLevel - body := p.parseBlockStmt() - p.expectSemi() - - cond := p.makeExpr(s2, "condition expression") - - return &ast.ForStmt{ - ForPos: pos, - Init: s1, - Cond: cond, - Post: s3, - Body: body, - } - -} - -func (p *Parser) parseBranchStmt(tok token.Token) ast.Stmt { - if p.trace { - defer un(trace(p, "BranchStmt")) - } - - pos := p.expect(tok) - - var label *ast.Ident - if p.token == token.Ident { - label = p.parseIdent() - } - p.expectSemi() - - return &ast.BranchStmt{ - Token: tok, - TokenPos: pos, - Label: label, - } -} - -func (p *Parser) parseIfStmt() ast.Stmt { - if p.trace { - defer un(trace(p, "IfStmt")) - } - - pos := p.expect(token.If) - - init, cond := p.parseIfHeader() - body := p.parseBlockStmt() - - var elseStmt ast.Stmt - if p.token == token.Else { - p.next() - - switch p.token { - case token.If: - elseStmt = p.parseIfStmt() - case token.LBrace: - elseStmt = p.parseBlockStmt() - p.expectSemi() - default: - p.errorExpected(p.pos, "if or {") - elseStmt = &ast.BadStmt{From: p.pos, To: p.pos} - } - } else { - p.expectSemi() - } - - return &ast.IfStmt{ - IfPos: pos, - Init: init, - Cond: cond, - Body: body, - Else: elseStmt, - } -} - -func (p *Parser) parseBlockStmt() *ast.BlockStmt { - if p.trace { - defer un(trace(p, "BlockStmt")) - } - - lbrace := p.expect(token.LBrace) - list := p.parseStmtList() - rbrace := p.expect(token.RBrace) - - return &ast.BlockStmt{ - LBrace: lbrace, - RBrace: rbrace, - Stmts: list, - } -} - -func (p *Parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) { - if p.token == token.LBrace { - p.error(p.pos, "missing condition in if statement") - cond = &ast.BadExpr{From: p.pos, To: p.pos} - return - } - - outer := p.exprLevel - p.exprLevel = -1 - - if p.token == token.Semicolon { - p.error(p.pos, "missing init in if statement") - return - } - - init = p.parseSimpleStmt(false) - - var condStmt ast.Stmt - if p.token == token.LBrace { - condStmt = init - init = nil - } else if p.token == token.Semicolon { - p.next() - - condStmt = p.parseSimpleStmt(false) - } else { - p.error(p.pos, "missing condition in if statement") - } - - if condStmt != nil { - cond = p.makeExpr(condStmt, "boolean expression") - } - - if cond == nil { - cond = &ast.BadExpr{From: p.pos, To: p.pos} - } - - p.exprLevel = outer - - return -} - -func (p *Parser) makeExpr(s ast.Stmt, want string) ast.Expr { - if s == nil { - return nil - } - - if es, isExpr := s.(*ast.ExprStmt); isExpr { - return es.Expr - } - - found := "simple statement" - if _, isAss := s.(*ast.AssignStmt); isAss { - found = "assignment" - } - - p.error(s.Pos(), fmt.Sprintf("expected %s, found %s", want, found)) - - return &ast.BadExpr{From: s.Pos(), To: p.safePos(s.End())} -} - -func (p *Parser) parseReturnStmt() ast.Stmt { - if p.trace { - defer un(trace(p, "ReturnStmt")) - } - - pos := p.pos - p.expect(token.Return) - - var x ast.Expr - if p.token != token.Semicolon && p.token != token.RBrace { - x = p.parseExpr() - } - p.expectSemi() - - return &ast.ReturnStmt{ - ReturnPos: pos, - Result: x, - } -} - -func (p *Parser) parseExportStmt() ast.Stmt { - if p.trace { - defer un(trace(p, "ExportStmt")) - } - - pos := p.pos - p.expect(token.Export) - - x := p.parseExpr() - p.expectSemi() - - return &ast.ExportStmt{ - ExportPos: pos, - Result: x, - } -} - -func (p *Parser) parseSimpleStmt(forIn bool) ast.Stmt { - if p.trace { - defer un(trace(p, "SimpleStmt")) - } - - x := p.parseExprList() - - switch p.token { - case token.Assign, token.Define: // assignment statement - pos, tok := p.pos, p.token - p.next() - - y := p.parseExprList() - - return &ast.AssignStmt{ - LHS: x, - RHS: y, - Token: tok, - TokenPos: pos, - } - case token.In: - if forIn { - p.next() - - y := p.parseExpr() - - var key, value *ast.Ident - var ok bool - - switch len(x) { - case 1: - key = &ast.Ident{Name: "_", NamePos: x[0].Pos()} - - value, ok = x[0].(*ast.Ident) - if !ok { - p.errorExpected(x[0].Pos(), "identifier") - value = &ast.Ident{Name: "_", NamePos: x[0].Pos()} - } - case 2: - key, ok = x[0].(*ast.Ident) - if !ok { - p.errorExpected(x[0].Pos(), "identifier") - key = &ast.Ident{Name: "_", NamePos: x[0].Pos()} - } - value, ok = x[1].(*ast.Ident) - if !ok { - p.errorExpected(x[1].Pos(), "identifier") - value = &ast.Ident{Name: "_", NamePos: x[1].Pos()} - } - } - - return &ast.ForInStmt{ - Key: key, - Value: value, - Iterable: y, - } - } - } - - if len(x) > 1 { - p.errorExpected(x[0].Pos(), "1 expression") - // continue with first expression - } - - switch p.token { - case token.Define, - token.AddAssign, token.SubAssign, token.MulAssign, token.QuoAssign, token.RemAssign, - token.AndAssign, token.OrAssign, token.XorAssign, token.ShlAssign, token.ShrAssign, token.AndNotAssign: - pos, tok := p.pos, p.token - p.next() - - y := p.parseExpr() - - return &ast.AssignStmt{ - LHS: []ast.Expr{x[0]}, - RHS: []ast.Expr{y}, - Token: tok, - TokenPos: pos, - } - case token.Inc, token.Dec: - // increment or decrement statement - s := &ast.IncDecStmt{Expr: x[0], Token: p.token, TokenPos: p.pos} - p.next() - return s - } - - // expression statement - return &ast.ExprStmt{Expr: x[0]} -} - -func (p *Parser) parseExprList() (list []ast.Expr) { - if p.trace { - defer un(trace(p, "ExpressionList")) - } - - list = append(list, p.parseExpr()) - for p.token == token.Comma { - p.next() - list = append(list, p.parseExpr()) - } - - return -} - -func (p *Parser) parseMapElementLit() *ast.MapElementLit { - if p.trace { - defer un(trace(p, "MapElementLit")) - } - - pos := p.pos - name := "_" - - if p.token == token.Ident { - name = p.tokenLit - } else if p.token == token.String { - v, _ := strconv.Unquote(p.tokenLit) - name = v - } else { - p.errorExpected(pos, "map key") - } - - p.next() - - colonPos := p.expect(token.Colon) - valueExpr := p.parseExpr() - - return &ast.MapElementLit{ - Key: name, - KeyPos: pos, - ColonPos: colonPos, - Value: valueExpr, - } -} - -func (p *Parser) parseMapLit() *ast.MapLit { - if p.trace { - defer un(trace(p, "MapLit")) - } - - lbrace := p.expect(token.LBrace) - p.exprLevel++ - - var elements []*ast.MapElementLit - for p.token != token.RBrace && p.token != token.EOF { - elements = append(elements, p.parseMapElementLit()) - - if !p.expectComma(token.RBrace, "map element") { - break - } - } - - p.exprLevel-- - rbrace := p.expect(token.RBrace) - - return &ast.MapLit{ - LBrace: lbrace, - RBrace: rbrace, - Elements: elements, - } -} - -func (p *Parser) expect(token token.Token) source.Pos { - pos := p.pos - - if p.token != token { - p.errorExpected(pos, "'"+token.String()+"'") - } - p.next() - - return pos -} - -func (p *Parser) expectSemi() { - switch p.token { - case token.RParen, token.RBrace: - // semicolon is optional before a closing ')' or '}' - case token.Comma: - // permit a ',' instead of a ';' but complain - p.errorExpected(p.pos, "';'") - fallthrough - case token.Semicolon: - p.next() - default: - p.errorExpected(p.pos, "';'") - p.advance(stmtStart) - } - -} - -func (p *Parser) advance(to map[token.Token]bool) { - for ; p.token != token.EOF; p.next() { - if to[p.token] { - if p.pos == p.syncPos && p.syncCount < 10 { - p.syncCount++ - return - } - - if p.pos > p.syncPos { - p.syncPos = p.pos - p.syncCount = 0 - return - } - } - } -} - -func (p *Parser) error(pos source.Pos, msg string) { - filePos := p.file.Position(pos) - - n := len(p.errors) - if n > 0 && p.errors[n-1].Pos.Line == filePos.Line { - // discard errors reported on the same line - return - } - - if n > 10 { - // too many errors; terminate early - panic(bailout{}) - } - - p.errors.Add(filePos, msg) -} - -func (p *Parser) errorExpected(pos source.Pos, msg string) { - msg = "expected " + msg - if pos == p.pos { - // error happened at the current position: provide more specific - switch { - case p.token == token.Semicolon && p.tokenLit == "\n": - msg += ", found newline" - case p.token.IsLiteral(): - msg += ", found " + p.tokenLit - default: - msg += ", found '" + p.token.String() + "'" - } - } - - p.error(pos, msg) -} - -func (p *Parser) next() { - if p.trace && p.pos.IsValid() { - s := p.token.String() - switch { - case p.token.IsLiteral(): - p.printTrace(s, p.tokenLit) - case p.token.IsOperator(), p.token.IsKeyword(): - p.printTrace(`"` + s + `"`) - default: - p.printTrace(s) - } - } - - p.token, p.tokenLit, p.pos = p.scanner.Scan() -} - -func (p *Parser) printTrace(a ...interface{}) { - const ( - dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " - n = len(dots) - ) - - filePos := p.file.Position(p.pos) - _, _ = fmt.Fprintf(p.traceOut, "%5d: %5d:%3d: ", p.pos, filePos.Line, filePos.Column) - - i := 2 * p.indent - for i > n { - _, _ = fmt.Fprint(p.traceOut, dots) - i -= n - } - _, _ = fmt.Fprint(p.traceOut, dots[0:i]) - _, _ = fmt.Fprintln(p.traceOut, a...) -} - -func (p *Parser) safePos(pos source.Pos) source.Pos { - fileBase := p.file.Base - fileSize := p.file.Size - - if int(pos) < fileBase || int(pos) > fileBase+fileSize { - return source.Pos(fileBase + fileSize) - } - - return pos -} - -func trace(p *Parser, msg string) *Parser { - p.printTrace(msg, "(") - p.indent++ - - return p -} - -func un(p *Parser) { - p.indent-- - p.printTrace(")") -} diff --git a/vendor/github.com/d5/tengo/compiler/parser/sync.go b/vendor/github.com/d5/tengo/compiler/parser/sync.go deleted file mode 100644 index e68d623a..00000000 --- a/vendor/github.com/d5/tengo/compiler/parser/sync.go +++ /dev/null @@ -1,12 +0,0 @@ -package parser - -import "github.com/d5/tengo/compiler/token" - -var stmtStart = map[token.Token]bool{ - token.Break: true, - token.Continue: true, - token.For: true, - token.If: true, - token.Return: true, - token.Export: true, -} diff --git a/vendor/github.com/d5/tengo/compiler/scanner/error_handler.go b/vendor/github.com/d5/tengo/compiler/scanner/error_handler.go deleted file mode 100644 index 379f0196..00000000 --- a/vendor/github.com/d5/tengo/compiler/scanner/error_handler.go +++ /dev/null @@ -1,6 +0,0 @@ -package scanner - -import "github.com/d5/tengo/compiler/source" - -// ErrorHandler is an error handler for the scanner. -type ErrorHandler func(pos source.FilePos, msg string) diff --git a/vendor/github.com/d5/tengo/compiler/scanner/mode.go b/vendor/github.com/d5/tengo/compiler/scanner/mode.go deleted file mode 100644 index f67ceaf8..00000000 --- a/vendor/github.com/d5/tengo/compiler/scanner/mode.go +++ /dev/null @@ -1,10 +0,0 @@ -package scanner - -// Mode represents a scanner mode. -type Mode int - -// List of scanner modes. -const ( - ScanComments Mode = 1 << iota - DontInsertSemis -) diff --git a/vendor/github.com/d5/tengo/compiler/scanner/scanner.go b/vendor/github.com/d5/tengo/compiler/scanner/scanner.go deleted file mode 100644 index 387cd8ee..00000000 --- a/vendor/github.com/d5/tengo/compiler/scanner/scanner.go +++ /dev/null @@ -1,680 +0,0 @@ -/* - Scanner reads the Tengo source text and tokenize them. - - Scanner is a modified version of Go's scanner implementation. - - Copyright 2009 The Go Authors. All rights reserved. - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. -*/ - -package scanner - -import ( - "fmt" - "unicode" - "unicode/utf8" - - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// byte order mark -const bom = 0xFEFF - -// Scanner reads the Tengo source text. -type Scanner struct { - file *source.File // source file handle - src []byte // source - ch rune // current character - offset int // character offset - readOffset int // reading offset (position after current character) - lineOffset int // current line offset - insertSemi bool // insert a semicolon before next newline - errorHandler ErrorHandler // error reporting; or nil - errorCount int // number of errors encountered - mode Mode -} - -// NewScanner creates a Scanner. -func NewScanner(file *source.File, src []byte, errorHandler ErrorHandler, mode Mode) *Scanner { - if file.Size != len(src) { - panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size, len(src))) - } - - s := &Scanner{ - file: file, - src: src, - errorHandler: errorHandler, - ch: ' ', - mode: mode, - } - - s.next() - if s.ch == bom { - s.next() // ignore BOM at file beginning - } - - return s -} - -// ErrorCount returns the number of errors. -func (s *Scanner) ErrorCount() int { - return s.errorCount -} - -// Scan returns a token, token literal and its position. -func (s *Scanner) Scan() (tok token.Token, literal string, pos source.Pos) { - s.skipWhitespace() - - pos = s.file.FileSetPos(s.offset) - - insertSemi := false - - // determine token value - switch ch := s.ch; { - case isLetter(ch): - literal = s.scanIdentifier() - tok = token.Lookup(literal) - switch tok { - case token.Ident, token.Break, token.Continue, token.Return, token.Export, token.True, token.False, token.Undefined: - insertSemi = true - } - case '0' <= ch && ch <= '9': - insertSemi = true - tok, literal = s.scanNumber(false) - default: - s.next() // always make progress - - switch ch { - case -1: // EOF - if s.insertSemi { - s.insertSemi = false // EOF consumed - return token.Semicolon, "\n", pos - } - tok = token.EOF - case '\n': - // we only reach here if s.insertSemi was set in the first place - s.insertSemi = false // newline consumed - return token.Semicolon, "\n", pos - case '"': - insertSemi = true - tok = token.String - literal = s.scanString() - case '\'': - insertSemi = true - tok = token.Char - literal = s.scanRune() - case '`': - insertSemi = true - tok = token.String - literal = s.scanRawString() - case ':': - tok = s.switch2(token.Colon, token.Define) - case '.': - if '0' <= s.ch && s.ch <= '9' { - insertSemi = true - tok, literal = s.scanNumber(true) - } else { - tok = token.Period - if s.ch == '.' && s.peek() == '.' { - s.next() - s.next() // consume last '.' - tok = token.Ellipsis - } - } - case ',': - tok = token.Comma - case '?': - tok = token.Question - case ';': - tok = token.Semicolon - literal = ";" - case '(': - tok = token.LParen - case ')': - insertSemi = true - tok = token.RParen - case '[': - tok = token.LBrack - case ']': - insertSemi = true - tok = token.RBrack - case '{': - tok = token.LBrace - case '}': - insertSemi = true - tok = token.RBrace - case '+': - tok = s.switch3(token.Add, token.AddAssign, '+', token.Inc) - if tok == token.Inc { - insertSemi = true - } - case '-': - tok = s.switch3(token.Sub, token.SubAssign, '-', token.Dec) - if tok == token.Dec { - insertSemi = true - } - case '*': - tok = s.switch2(token.Mul, token.MulAssign) - case '/': - if s.ch == '/' || s.ch == '*' { - // comment - if s.insertSemi && s.findLineEnd() { - // reset position to the beginning of the comment - s.ch = '/' - s.offset = s.file.Offset(pos) - s.readOffset = s.offset + 1 - s.insertSemi = false // newline consumed - return token.Semicolon, "\n", pos - } - comment := s.scanComment() - if s.mode&ScanComments == 0 { - // skip comment - s.insertSemi = false // newline consumed - return s.Scan() - } - tok = token.Comment - literal = comment - } else { - tok = s.switch2(token.Quo, token.QuoAssign) - } - case '%': - tok = s.switch2(token.Rem, token.RemAssign) - case '^': - tok = s.switch2(token.Xor, token.XorAssign) - case '<': - tok = s.switch4(token.Less, token.LessEq, '<', token.Shl, token.ShlAssign) - case '>': - tok = s.switch4(token.Greater, token.GreaterEq, '>', token.Shr, token.ShrAssign) - case '=': - tok = s.switch2(token.Assign, token.Equal) - case '!': - tok = s.switch2(token.Not, token.NotEqual) - case '&': - if s.ch == '^' { - s.next() - tok = s.switch2(token.AndNot, token.AndNotAssign) - } else { - tok = s.switch3(token.And, token.AndAssign, '&', token.LAnd) - } - case '|': - tok = s.switch3(token.Or, token.OrAssign, '|', token.LOr) - default: - // next reports unexpected BOMs - don't repeat - if ch != bom { - s.error(s.file.Offset(pos), fmt.Sprintf("illegal character %#U", ch)) - } - insertSemi = s.insertSemi // preserve insertSemi info - tok = token.Illegal - literal = string(ch) - } - } - - if s.mode&DontInsertSemis == 0 { - s.insertSemi = insertSemi - } - - return -} - -func (s *Scanner) next() { - if s.readOffset < len(s.src) { - s.offset = s.readOffset - if s.ch == '\n' { - s.lineOffset = s.offset - s.file.AddLine(s.offset) - } - r, w := rune(s.src[s.readOffset]), 1 - switch { - case r == 0: - s.error(s.offset, "illegal character NUL") - case r >= utf8.RuneSelf: - // not ASCII - r, w = utf8.DecodeRune(s.src[s.readOffset:]) - if r == utf8.RuneError && w == 1 { - s.error(s.offset, "illegal UTF-8 encoding") - } else if r == bom && s.offset > 0 { - s.error(s.offset, "illegal byte order mark") - } - } - s.readOffset += w - s.ch = r - } else { - s.offset = len(s.src) - if s.ch == '\n' { - s.lineOffset = s.offset - s.file.AddLine(s.offset) - } - s.ch = -1 // eof - } -} - -func (s *Scanner) peek() byte { - if s.readOffset < len(s.src) { - return s.src[s.readOffset] - } - - return 0 -} - -func (s *Scanner) error(offset int, msg string) { - if s.errorHandler != nil { - s.errorHandler(s.file.Position(s.file.FileSetPos(offset)), msg) - } - - s.errorCount++ -} - -func (s *Scanner) scanComment() string { - // initial '/' already consumed; s.ch == '/' || s.ch == '*' - offs := s.offset - 1 // position of initial '/' - var numCR int - - if s.ch == '/' { - //-style comment - // (the final '\n' is not considered part of the comment) - s.next() - for s.ch != '\n' && s.ch >= 0 { - if s.ch == '\r' { - numCR++ - } - s.next() - } - goto exit - } - - /*-style comment */ - s.next() - for s.ch >= 0 { - ch := s.ch - if ch == '\r' { - numCR++ - } - s.next() - if ch == '*' && s.ch == '/' { - s.next() - goto exit - } - } - - s.error(offs, "comment not terminated") - -exit: - lit := s.src[offs:s.offset] - - // On Windows, a (//-comment) line may end in "\r\n". - // Remove the final '\r' before analyzing the text for line directives (matching the compiler). - // Remove any other '\r' afterwards (matching the pre-existing behavior of the scanner). - if numCR > 0 && len(lit) >= 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' { - lit = lit[:len(lit)-1] - numCR-- - } - - if numCR > 0 { - lit = StripCR(lit, lit[1] == '*') - } - - return string(lit) -} - -func (s *Scanner) findLineEnd() bool { - // initial '/' already consumed - - defer func(offs int) { - // reset scanner state to where it was upon calling findLineEnd - s.ch = '/' - s.offset = offs - s.readOffset = offs + 1 - s.next() // consume initial '/' again - }(s.offset - 1) - - // read ahead until a newline, EOF, or non-comment tok is found - for s.ch == '/' || s.ch == '*' { - if s.ch == '/' { - //-style comment always contains a newline - return true - } - /*-style comment: look for newline */ - s.next() - for s.ch >= 0 { - ch := s.ch - if ch == '\n' { - return true - } - s.next() - if ch == '*' && s.ch == '/' { - s.next() - break - } - } - s.skipWhitespace() // s.insertSemi is set - if s.ch < 0 || s.ch == '\n' { - return true - } - if s.ch != '/' { - // non-comment tok - return false - } - s.next() // consume '/' - } - - return false -} - -func (s *Scanner) scanIdentifier() string { - offs := s.offset - for isLetter(s.ch) || isDigit(s.ch) { - s.next() - } - - return string(s.src[offs:s.offset]) -} - -func (s *Scanner) scanMantissa(base int) { - for digitVal(s.ch) < base { - s.next() - } -} - -func (s *Scanner) scanNumber(seenDecimalPoint bool) (tok token.Token, lit string) { - // digitVal(s.ch) < 10 - offs := s.offset - tok = token.Int - - defer func() { - lit = string(s.src[offs:s.offset]) - }() - - if seenDecimalPoint { - offs-- - tok = token.Float - s.scanMantissa(10) - goto exponent - } - - if s.ch == '0' { - // int or float - offs := s.offset - s.next() - if s.ch == 'x' || s.ch == 'X' { - // hexadecimal int - s.next() - s.scanMantissa(16) - if s.offset-offs <= 2 { - // only scanned "0x" or "0X" - s.error(offs, "illegal hexadecimal number") - } - } else { - // octal int or float - seenDecimalDigit := false - s.scanMantissa(8) - if s.ch == '8' || s.ch == '9' { - // illegal octal int or float - seenDecimalDigit = true - s.scanMantissa(10) - } - if s.ch == '.' || s.ch == 'e' || s.ch == 'E' || s.ch == 'i' { - goto fraction - } - // octal int - if seenDecimalDigit { - s.error(offs, "illegal octal number") - } - } - - return - } - - // decimal int or float - s.scanMantissa(10) - -fraction: - if s.ch == '.' { - tok = token.Float - s.next() - s.scanMantissa(10) - } - -exponent: - if s.ch == 'e' || s.ch == 'E' { - tok = token.Float - s.next() - if s.ch == '-' || s.ch == '+' { - s.next() - } - if digitVal(s.ch) < 10 { - s.scanMantissa(10) - } else { - s.error(offs, "illegal floating-point exponent") - } - } - - return -} - -func (s *Scanner) scanEscape(quote rune) bool { - offs := s.offset - - var n int - var base, max uint32 - switch s.ch { - case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote: - s.next() - return true - case '0', '1', '2', '3', '4', '5', '6', '7': - n, base, max = 3, 8, 255 - case 'x': - s.next() - n, base, max = 2, 16, 255 - case 'u': - s.next() - n, base, max = 4, 16, unicode.MaxRune - case 'U': - s.next() - n, base, max = 8, 16, unicode.MaxRune - default: - msg := "unknown escape sequence" - if s.ch < 0 { - msg = "escape sequence not terminated" - } - s.error(offs, msg) - return false - } - - var x uint32 - for n > 0 { - d := uint32(digitVal(s.ch)) - if d >= base { - msg := fmt.Sprintf("illegal character %#U in escape sequence", s.ch) - if s.ch < 0 { - msg = "escape sequence not terminated" - } - s.error(s.offset, msg) - return false - } - x = x*base + d - s.next() - n-- - } - - if x > max || 0xD800 <= x && x < 0xE000 { - s.error(offs, "escape sequence is invalid Unicode code point") - return false - } - - return true -} - -func (s *Scanner) scanRune() string { - offs := s.offset - 1 // '\'' opening already consumed - - valid := true - n := 0 - for { - ch := s.ch - if ch == '\n' || ch < 0 { - // only report error if we don't have one already - if valid { - s.error(offs, "rune literal not terminated") - valid = false - } - break - } - s.next() - if ch == '\'' { - break - } - n++ - if ch == '\\' { - if !s.scanEscape('\'') { - valid = false - } - // continue to read to closing quote - } - } - - if valid && n != 1 { - s.error(offs, "illegal rune literal") - } - - return string(s.src[offs:s.offset]) -} - -func (s *Scanner) scanString() string { - offs := s.offset - 1 // '"' opening already consumed - - for { - ch := s.ch - if ch == '\n' || ch < 0 { - s.error(offs, "string literal not terminated") - break - } - s.next() - if ch == '"' { - break - } - if ch == '\\' { - s.scanEscape('"') - } - } - - return string(s.src[offs:s.offset]) -} - -func (s *Scanner) scanRawString() string { - offs := s.offset - 1 // '`' opening already consumed - - hasCR := false - for { - ch := s.ch - if ch < 0 { - s.error(offs, "raw string literal not terminated") - break - } - - s.next() - - if ch == '`' { - break - } - - if ch == '\r' { - hasCR = true - } - } - - lit := s.src[offs:s.offset] - if hasCR { - lit = StripCR(lit, false) - } - - return string(lit) -} - -// StripCR removes carriage return characters. -func StripCR(b []byte, comment bool) []byte { - c := make([]byte, len(b)) - - i := 0 - for j, ch := range b { - // In a /*-style comment, don't strip \r from *\r/ (incl. sequences of \r from *\r\r...\r/) - // since the resulting */ would terminate the comment too early unless the \r is immediately - // following the opening /* in which case it's ok because /*/ is not closed yet. - if ch != '\r' || comment && i > len("/*") && c[i-1] == '*' && j+1 < len(b) && b[j+1] == '/' { - c[i] = ch - i++ - } - } - - return c[:i] -} - -func (s *Scanner) skipWhitespace() { - for s.ch == ' ' || s.ch == '\t' || s.ch == '\n' && !s.insertSemi || s.ch == '\r' { - s.next() - } -} - -func (s *Scanner) switch2(tok0, tok1 token.Token) token.Token { - if s.ch == '=' { - s.next() - return tok1 - } - - return tok0 -} - -func (s *Scanner) switch3(tok0, tok1 token.Token, ch2 rune, tok2 token.Token) token.Token { - if s.ch == '=' { - s.next() - return tok1 - } - - if s.ch == ch2 { - s.next() - return tok2 - } - - return tok0 -} - -func (s *Scanner) switch4(tok0, tok1 token.Token, ch2 rune, tok2, tok3 token.Token) token.Token { - if s.ch == '=' { - s.next() - return tok1 - } - - if s.ch == ch2 { - s.next() - if s.ch == '=' { - s.next() - return tok3 - } - - return tok2 - } - - return tok0 -} - -func isLetter(ch rune) bool { - return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch) -} - -func isDigit(ch rune) bool { - return '0' <= ch && ch <= '9' || ch >= utf8.RuneSelf && unicode.IsDigit(ch) -} - -func digitVal(ch rune) int { - switch { - case '0' <= ch && ch <= '9': - return int(ch - '0') - case 'a' <= ch && ch <= 'f': - return int(ch - 'a' + 10) - case 'A' <= ch && ch <= 'F': - return int(ch - 'A' + 10) - } - - return 16 // larger than any legal digit val -} diff --git a/vendor/github.com/d5/tengo/compiler/source/file.go b/vendor/github.com/d5/tengo/compiler/source/file.go deleted file mode 100644 index 9e51c9a4..00000000 --- a/vendor/github.com/d5/tengo/compiler/source/file.go +++ /dev/null @@ -1,110 +0,0 @@ -package source - -// File represents a source file. -type File struct { - // File set for the file - set *FileSet - // File name as provided to AddFile - Name string - // Pos value range for this file is [base...base+size] - Base int - // File size as provided to AddFile - Size int - // Lines contains the offset of the first character for each line (the first entry is always 0) - Lines []int -} - -// Set returns FileSet. -func (f *File) Set() *FileSet { - return f.set -} - -// LineCount returns the current number of lines. -func (f *File) LineCount() int { - return len(f.Lines) -} - -// AddLine adds a new line. -func (f *File) AddLine(offset int) { - if i := len(f.Lines); (i == 0 || f.Lines[i-1] < offset) && offset < f.Size { - f.Lines = append(f.Lines, offset) - } -} - -// LineStart returns the position of the first character in the line. -func (f *File) LineStart(line int) Pos { - if line < 1 { - panic("illegal line number (line numbering starts at 1)") - } - - if line > len(f.Lines) { - panic("illegal line number") - } - - return Pos(f.Base + f.Lines[line-1]) -} - -// FileSetPos returns the position in the file set. -func (f *File) FileSetPos(offset int) Pos { - if offset > f.Size { - panic("illegal file offset") - } - - return Pos(f.Base + offset) -} - -// Offset translates the file set position into the file offset. -func (f *File) Offset(p Pos) int { - if int(p) < f.Base || int(p) > f.Base+f.Size { - panic("illegal Pos value") - } - - return int(p) - f.Base -} - -// Position translates the file set position into the file position. -func (f *File) Position(p Pos) (pos FilePos) { - if p != NoPos { - if int(p) < f.Base || int(p) > f.Base+f.Size { - panic("illegal Pos value") - } - - pos = f.position(p) - } - - return -} - -func (f *File) position(p Pos) (pos FilePos) { - offset := int(p) - f.Base - pos.Offset = offset - pos.Filename, pos.Line, pos.Column = f.unpack(offset) - - return -} - -func (f *File) unpack(offset int) (filename string, line, column int) { - filename = f.Name - if i := searchInts(f.Lines, offset); i >= 0 { - line, column = i+1, offset-f.Lines[i]+1 - } - - return -} - -func searchInts(a []int, x int) int { - // This function body is a manually inlined version of: - // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1 - i, j := 0, len(a) - for i < j { - h := i + (j-i)/2 // avoid overflow when computing h - // i ≤ h < j - if a[h] <= x { - i = h + 1 - } else { - j = h - } - } - - return i - 1 -} diff --git a/vendor/github.com/d5/tengo/compiler/source/file_pos.go b/vendor/github.com/d5/tengo/compiler/source/file_pos.go deleted file mode 100644 index 4055fe6d..00000000 --- a/vendor/github.com/d5/tengo/compiler/source/file_pos.go +++ /dev/null @@ -1,47 +0,0 @@ -package source - -import "fmt" - -// FilePos represents a position information in the file. -type FilePos struct { - Filename string // filename, if any - Offset int // offset, starting at 0 - Line int // line number, starting at 1 - Column int // column number, starting at 1 (byte count) -} - -// IsValid returns true if the position is valid. -func (p FilePos) IsValid() bool { - return p.Line > 0 -} - -// String returns a string in one of several forms: -// -// file:line:column valid position with file name -// file:line valid position with file name but no column (column == 0) -// line:column valid position without file name -// line valid position without file name and no column (column == 0) -// file invalid position with file name -// - invalid position without file name -// -func (p FilePos) String() string { - s := p.Filename - - if p.IsValid() { - if s != "" { - s += ":" - } - - s += fmt.Sprintf("%d", p.Line) - - if p.Column != 0 { - s += fmt.Sprintf(":%d", p.Column) - } - } - - if s == "" { - s = "-" - } - - return s -} diff --git a/vendor/github.com/d5/tengo/compiler/source/file_set.go b/vendor/github.com/d5/tengo/compiler/source/file_set.go deleted file mode 100644 index da342364..00000000 --- a/vendor/github.com/d5/tengo/compiler/source/file_set.go +++ /dev/null @@ -1,96 +0,0 @@ -package source - -import ( - "sort" -) - -// FileSet represents a set of source files. -type FileSet struct { - Base int // base offset for the next file - Files []*File // list of files in the order added to the set - LastFile *File // cache of last file looked up -} - -// NewFileSet creates a new file set. -func NewFileSet() *FileSet { - return &FileSet{ - Base: 1, // 0 == NoPos - } -} - -// AddFile adds a new file in the file set. -func (s *FileSet) AddFile(filename string, base, size int) *File { - if base < 0 { - base = s.Base - } - if base < s.Base || size < 0 { - panic("illegal base or size") - } - - f := &File{ - set: s, - Name: filename, - Base: base, - Size: size, - Lines: []int{0}, - } - - base += size + 1 // +1 because EOF also has a position - if base < 0 { - panic("offset overflow (> 2G of source code in file set)") - } - - // add the file to the file set - s.Base = base - s.Files = append(s.Files, f) - s.LastFile = f - - return f -} - -// File returns the file that contains the position p. -// If no such file is found (for instance for p == NoPos), -// the result is nil. -// -func (s *FileSet) File(p Pos) (f *File) { - if p != NoPos { - f = s.file(p) - } - - return -} - -// Position converts a Pos p in the fileset into a FilePos value. -func (s *FileSet) Position(p Pos) (pos FilePos) { - if p != NoPos { - if f := s.file(p); f != nil { - return f.position(p) - } - } - - return -} - -func (s *FileSet) file(p Pos) *File { - // common case: p is in last file - if f := s.LastFile; f != nil && f.Base <= int(p) && int(p) <= f.Base+f.Size { - return f - } - - // p is not in last file - search all files - if i := searchFiles(s.Files, int(p)); i >= 0 { - f := s.Files[i] - - // f.base <= int(p) by definition of searchFiles - if int(p) <= f.Base+f.Size { - s.LastFile = f // race is ok - s.last is only a cache - return f - } - } - - return nil -} - -func searchFiles(a []*File, x int) int { - return sort.Search(len(a), func(i int) bool { return a[i].Base > x }) - 1 -} diff --git a/vendor/github.com/d5/tengo/compiler/source/pos.go b/vendor/github.com/d5/tengo/compiler/source/pos.go deleted file mode 100644 index 72128b13..00000000 --- a/vendor/github.com/d5/tengo/compiler/source/pos.go +++ /dev/null @@ -1,12 +0,0 @@ -package source - -// Pos represents a position in the file set. -type Pos int - -// NoPos represents an invalid position. -const NoPos Pos = 0 - -// IsValid returns true if the position is valid. -func (p Pos) IsValid() bool { - return p != NoPos -} diff --git a/vendor/github.com/d5/tengo/compiler/symbol.go b/vendor/github.com/d5/tengo/compiler/symbol.go deleted file mode 100644 index bcd53234..00000000 --- a/vendor/github.com/d5/tengo/compiler/symbol.go +++ /dev/null @@ -1,9 +0,0 @@ -package compiler - -// Symbol represents a symbol in the symbol table. -type Symbol struct { - Name string - Scope SymbolScope - Index int - LocalAssigned bool // if the local symbol is assigned at least once -} diff --git a/vendor/github.com/d5/tengo/compiler/symbol_scopes.go b/vendor/github.com/d5/tengo/compiler/symbol_scopes.go deleted file mode 100644 index e0c0d94b..00000000 --- a/vendor/github.com/d5/tengo/compiler/symbol_scopes.go +++ /dev/null @@ -1,12 +0,0 @@ -package compiler - -// SymbolScope represents a symbol scope. -type SymbolScope string - -// List of symbol scopes -const ( - ScopeGlobal SymbolScope = "GLOBAL" - ScopeLocal SymbolScope = "LOCAL" - ScopeBuiltin SymbolScope = "BUILTIN" - ScopeFree SymbolScope = "FREE" -) diff --git a/vendor/github.com/d5/tengo/compiler/symbol_table.go b/vendor/github.com/d5/tengo/compiler/symbol_table.go deleted file mode 100644 index 94c868de..00000000 --- a/vendor/github.com/d5/tengo/compiler/symbol_table.go +++ /dev/null @@ -1,159 +0,0 @@ -package compiler - -// SymbolTable represents a symbol table. -type SymbolTable struct { - parent *SymbolTable - block bool - store map[string]*Symbol - numDefinition int - maxDefinition int - freeSymbols []*Symbol - builtinSymbols []*Symbol -} - -// NewSymbolTable creates a SymbolTable. -func NewSymbolTable() *SymbolTable { - return &SymbolTable{ - store: make(map[string]*Symbol), - } -} - -// Define adds a new symbol in the current scope. -func (t *SymbolTable) Define(name string) *Symbol { - symbol := &Symbol{Name: name, Index: t.nextIndex()} - t.numDefinition++ - - if t.Parent(true) == nil { - symbol.Scope = ScopeGlobal - } else { - symbol.Scope = ScopeLocal - } - - t.store[name] = symbol - - t.updateMaxDefs(symbol.Index + 1) - - return symbol -} - -// DefineBuiltin adds a symbol for builtin function. -func (t *SymbolTable) DefineBuiltin(index int, name string) *Symbol { - if t.parent != nil { - return t.parent.DefineBuiltin(index, name) - } - - symbol := &Symbol{ - Name: name, - Index: index, - Scope: ScopeBuiltin, - } - - t.store[name] = symbol - - t.builtinSymbols = append(t.builtinSymbols, symbol) - - return symbol -} - -// Resolve resolves a symbol with a given name. -func (t *SymbolTable) Resolve(name string) (symbol *Symbol, depth int, ok bool) { - symbol, ok = t.store[name] - if !ok && t.parent != nil { - symbol, depth, ok = t.parent.Resolve(name) - if !ok { - return - } - - depth++ - - // if symbol is defined in parent table and if it's not global/builtin - // then it's free variable. - if !t.block && depth > 0 && symbol.Scope != ScopeGlobal && symbol.Scope != ScopeBuiltin { - return t.defineFree(symbol), depth, true - } - - return - } - - return -} - -// Fork creates a new symbol table for a new scope. -func (t *SymbolTable) Fork(block bool) *SymbolTable { - return &SymbolTable{ - store: make(map[string]*Symbol), - parent: t, - block: block, - } -} - -// Parent returns the outer scope of the current symbol table. -func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable { - if skipBlock && t.block { - return t.parent.Parent(skipBlock) - } - - return t.parent -} - -// MaxSymbols returns the total number of symbols defined in the scope. -func (t *SymbolTable) MaxSymbols() int { - return t.maxDefinition -} - -// FreeSymbols returns free symbols for the scope. -func (t *SymbolTable) FreeSymbols() []*Symbol { - return t.freeSymbols -} - -// BuiltinSymbols returns builtin symbols for the scope. -func (t *SymbolTable) BuiltinSymbols() []*Symbol { - if t.parent != nil { - return t.parent.BuiltinSymbols() - } - - return t.builtinSymbols -} - -// Names returns the name of all the symbols. -func (t *SymbolTable) Names() []string { - var names []string - for name := range t.store { - names = append(names, name) - } - return names -} - -func (t *SymbolTable) nextIndex() int { - if t.block { - return t.parent.nextIndex() + t.numDefinition - } - - return t.numDefinition -} - -func (t *SymbolTable) updateMaxDefs(numDefs int) { - if numDefs > t.maxDefinition { - t.maxDefinition = numDefs - } - - if t.block { - t.parent.updateMaxDefs(numDefs) - } -} - -func (t *SymbolTable) defineFree(original *Symbol) *Symbol { - // TODO: should we check duplicates? - - t.freeSymbols = append(t.freeSymbols, original) - - symbol := &Symbol{ - Name: original.Name, - Index: len(t.freeSymbols) - 1, - Scope: ScopeFree, - } - - t.store[original.Name] = symbol - - return symbol -} diff --git a/vendor/github.com/d5/tengo/compiler/token/keywords.go b/vendor/github.com/d5/tengo/compiler/token/keywords.go deleted file mode 100644 index fd6e9d0b..00000000 --- a/vendor/github.com/d5/tengo/compiler/token/keywords.go +++ /dev/null @@ -1,19 +0,0 @@ -package token - -var keywords map[string]Token - -func init() { - keywords = make(map[string]Token) - for i := _keywordBeg + 1; i < _keywordEnd; i++ { - keywords[tokens[i]] = i - } -} - -// Lookup returns corresponding keyword if ident is a keyword. -func Lookup(ident string) Token { - if tok, isKeyword := keywords[ident]; isKeyword { - return tok - } - - return Ident -} diff --git a/vendor/github.com/d5/tengo/compiler/token/tokens.go b/vendor/github.com/d5/tengo/compiler/token/tokens.go deleted file mode 100644 index b32d36ee..00000000 --- a/vendor/github.com/d5/tengo/compiler/token/tokens.go +++ /dev/null @@ -1,208 +0,0 @@ -package token - -import "strconv" - -// Token represents a token. -type Token int - -// List of tokens -const ( - Illegal Token = iota - EOF - Comment - _literalBeg - Ident - Int - Float - Char - String - _literalEnd - _operatorBeg - Add // + - Sub // - - Mul // * - Quo // / - Rem // % - And // & - Or // | - Xor // ^ - Shl // << - Shr // >> - AndNot // &^ - AddAssign // += - SubAssign // -= - MulAssign // *= - QuoAssign // /= - RemAssign // %= - AndAssign // &= - OrAssign // |= - XorAssign // ^= - ShlAssign // <<= - ShrAssign // >>= - AndNotAssign // &^= - LAnd // && - LOr // || - Inc // ++ - Dec // -- - Equal // == - Less // < - Greater // > - Assign // = - Not // ! - NotEqual // != - LessEq // <= - GreaterEq // >= - Define // := - Ellipsis // ... - LParen // ( - LBrack // [ - LBrace // { - Comma // , - Period // . - RParen // ) - RBrack // ] - RBrace // } - Semicolon // ; - Colon // : - Question // ? - _operatorEnd - _keywordBeg - Break - Continue - Else - For - Func - Error - Immutable - If - Return - Export - True - False - In - Undefined - Import - _keywordEnd -) - -var tokens = [...]string{ - Illegal: "ILLEGAL", - EOF: "EOF", - Comment: "COMMENT", - Ident: "IDENT", - Int: "INT", - Float: "FLOAT", - Char: "CHAR", - String: "STRING", - Add: "+", - Sub: "-", - Mul: "*", - Quo: "/", - Rem: "%", - And: "&", - Or: "|", - Xor: "^", - Shl: "<<", - Shr: ">>", - AndNot: "&^", - AddAssign: "+=", - SubAssign: "-=", - MulAssign: "*=", - QuoAssign: "/=", - RemAssign: "%=", - AndAssign: "&=", - OrAssign: "|=", - XorAssign: "^=", - ShlAssign: "<<=", - ShrAssign: ">>=", - AndNotAssign: "&^=", - LAnd: "&&", - LOr: "||", - Inc: "++", - Dec: "--", - Equal: "==", - Less: "<", - Greater: ">", - Assign: "=", - Not: "!", - NotEqual: "!=", - LessEq: "<=", - GreaterEq: ">=", - Define: ":=", - Ellipsis: "...", - LParen: "(", - LBrack: "[", - LBrace: "{", - Comma: ",", - Period: ".", - RParen: ")", - RBrack: "]", - RBrace: "}", - Semicolon: ";", - Colon: ":", - Question: "?", - Break: "break", - Continue: "continue", - Else: "else", - For: "for", - Func: "func", - Error: "error", - Immutable: "immutable", - If: "if", - Return: "return", - Export: "export", - True: "true", - False: "false", - In: "in", - Undefined: "undefined", - Import: "import", -} - -func (tok Token) String() string { - s := "" - - if 0 <= tok && tok < Token(len(tokens)) { - s = tokens[tok] - } - - if s == "" { - s = "token(" + strconv.Itoa(int(tok)) + ")" - } - - return s -} - -// LowestPrec represents lowest operator precedence. -const LowestPrec = 0 - -// Precedence returns the precedence for the operator token. -func (tok Token) Precedence() int { - switch tok { - case LOr: - return 1 - case LAnd: - return 2 - case Equal, NotEqual, Less, LessEq, Greater, GreaterEq: - return 3 - case Add, Sub, Or, Xor: - return 4 - case Mul, Quo, Rem, Shl, Shr, And, AndNot: - return 5 - } - return LowestPrec -} - -// IsLiteral returns true if the token is a literal. -func (tok Token) IsLiteral() bool { - return _literalBeg < tok && tok < _literalEnd -} - -// IsOperator returns true if the token is an operator. -func (tok Token) IsOperator() bool { - return _operatorBeg < tok && tok < _operatorEnd -} - -// IsKeyword returns true if the token is a keyword. -func (tok Token) IsKeyword() bool { - return _keywordBeg < tok && tok < _keywordEnd -} diff --git a/vendor/github.com/d5/tengo/go.mod b/vendor/github.com/d5/tengo/go.mod deleted file mode 100644 index 8421e273..00000000 --- a/vendor/github.com/d5/tengo/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/d5/tengo - -go 1.12 diff --git a/vendor/github.com/d5/tengo/go.sum b/vendor/github.com/d5/tengo/go.sum deleted file mode 100644 index e69de29b..00000000 diff --git a/vendor/github.com/d5/tengo/objects/array.go b/vendor/github.com/d5/tengo/objects/array.go deleted file mode 100644 index 1e917c59..00000000 --- a/vendor/github.com/d5/tengo/objects/array.go +++ /dev/null @@ -1,130 +0,0 @@ -package objects - -import ( - "fmt" - "strings" - - "github.com/d5/tengo/compiler/token" -) - -// Array represents an array of objects. -type Array struct { - Value []Object -} - -// TypeName returns the name of the type. -func (o *Array) TypeName() string { - return "array" -} - -func (o *Array) String() string { - var elements []string - for _, e := range o.Value { - elements = append(elements, e.String()) - } - - return fmt.Sprintf("[%s]", strings.Join(elements, ", ")) -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) { - if rhs, ok := rhs.(*Array); ok { - switch op { - case token.Add: - if len(rhs.Value) == 0 { - return o, nil - } - return &Array{Value: append(o.Value, rhs.Value...)}, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Array) Copy() Object { - var c []Object - for _, elem := range o.Value { - c = append(c, elem.Copy()) - } - - return &Array{Value: c} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Array) IsFalsy() bool { - return len(o.Value) == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Array) Equals(x Object) bool { - var xVal []Object - switch x := x.(type) { - case *Array: - xVal = x.Value - case *ImmutableArray: - xVal = x.Value - default: - return false - } - - if len(o.Value) != len(xVal) { - return false - } - - for i, e := range o.Value { - if !e.Equals(xVal[i]) { - return false - } - } - - return true -} - -// IndexGet returns an element at a given index. -func (o *Array) IndexGet(index Object) (res Object, err error) { - intIdx, ok := index.(*Int) - if !ok { - err = ErrInvalidIndexType - return - } - - idxVal := int(intIdx.Value) - - if idxVal < 0 || idxVal >= len(o.Value) { - res = UndefinedValue - return - } - - res = o.Value[idxVal] - - return -} - -// IndexSet sets an element at a given index. -func (o *Array) IndexSet(index, value Object) (err error) { - intIdx, ok := ToInt(index) - if !ok { - err = ErrInvalidIndexType - return - } - - if intIdx < 0 || intIdx >= len(o.Value) { - err = ErrIndexOutOfBounds - return - } - - o.Value[intIdx] = value - - return nil -} - -// Iterate creates an array iterator. -func (o *Array) Iterate() Iterator { - return &ArrayIterator{ - v: o.Value, - l: len(o.Value), - } -} diff --git a/vendor/github.com/d5/tengo/objects/array_iterator.go b/vendor/github.com/d5/tengo/objects/array_iterator.go deleted file mode 100644 index 204faa41..00000000 --- a/vendor/github.com/d5/tengo/objects/array_iterator.go +++ /dev/null @@ -1,57 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// ArrayIterator is an iterator for an array. -type ArrayIterator struct { - v []Object - i int - l int -} - -// TypeName returns the name of the type. -func (i *ArrayIterator) TypeName() string { - return "array-iterator" -} - -func (i *ArrayIterator) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (i *ArrayIterator) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (i *ArrayIterator) IsFalsy() bool { - return true -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (i *ArrayIterator) Equals(Object) bool { - return false -} - -// Copy returns a copy of the type. -func (i *ArrayIterator) Copy() Object { - return &ArrayIterator{v: i.v, i: i.i, l: i.l} -} - -// Next returns true if there are more elements to iterate. -func (i *ArrayIterator) Next() bool { - i.i++ - return i.i <= i.l -} - -// Key returns the key or index value of the current element. -func (i *ArrayIterator) Key() Object { - return &Int{Value: int64(i.i - 1)} -} - -// Value returns the value of the current element. -func (i *ArrayIterator) Value() Object { - return i.v[i.i-1] -} diff --git a/vendor/github.com/d5/tengo/objects/bool.go b/vendor/github.com/d5/tengo/objects/bool.go deleted file mode 100644 index ac9949e4..00000000 --- a/vendor/github.com/d5/tengo/objects/bool.go +++ /dev/null @@ -1,64 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// Bool represents a boolean value. -type Bool struct { - // this is intentionally non-public to force using objects.TrueValue and FalseValue always - value bool -} - -func (o *Bool) String() string { - if o.value { - return "true" - } - - return "false" -} - -// TypeName returns the name of the type. -func (o *Bool) TypeName() string { - return "bool" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Bool) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Bool) Copy() Object { - return o -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Bool) IsFalsy() bool { - return !o.value -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Bool) Equals(x Object) bool { - return o == x -} - -// GobDecode decodes bool value from input bytes. -func (o *Bool) GobDecode(b []byte) (err error) { - o.value = b[0] == 1 - - return -} - -// GobEncode encodes bool values into bytes. -func (o *Bool) GobEncode() (b []byte, err error) { - if o.value { - b = []byte{1} - } else { - b = []byte{0} - } - - return -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_append.go b/vendor/github.com/d5/tengo/objects/builtin_append.go deleted file mode 100644 index 9fb14b82..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_append.go +++ /dev/null @@ -1,21 +0,0 @@ -package objects - -// 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(), - } - } -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_convert.go b/vendor/github.com/d5/tengo/objects/builtin_convert.go deleted file mode 100644 index b5f2d05d..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_convert.go +++ /dev/null @@ -1,169 +0,0 @@ -package objects - -import "github.com/d5/tengo" - -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) > tengo.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(tengo.MaxBytesLen) { - return nil, ErrBytesLimit - } - - return &Bytes{Value: make([]byte, int(n.Value))}, nil - } - - v, ok := ToByteSlice(args[0]) - if ok { - if len(v) > tengo.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 -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_copy.go b/vendor/github.com/d5/tengo/objects/builtin_copy.go deleted file mode 100644 index 4b254b2b..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_copy.go +++ /dev/null @@ -1,9 +0,0 @@ -package objects - -func builtinCopy(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - return args[0].Copy(), nil -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_format.go b/vendor/github.com/d5/tengo/objects/builtin_format.go deleted file mode 100644 index 1f0e75ed..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_format.go +++ /dev/null @@ -1,27 +0,0 @@ -package objects - -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 { - return format, nil // okay to return 'format' directly as String is immutable - } - - s, err := Format(format.Value, args[1:]...) - if err != nil { - return nil, err - } - - return &String{Value: s}, nil -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_function.go b/vendor/github.com/d5/tengo/objects/builtin_function.go deleted file mode 100644 index 1d021617..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_function.go +++ /dev/null @@ -1,47 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// BuiltinFunction represents a builtin function. -type BuiltinFunction struct { - Name string - Value CallableFunc -} - -// TypeName returns the name of the type. -func (o *BuiltinFunction) TypeName() string { - return "builtin-function:" + o.Name -} - -func (o *BuiltinFunction) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *BuiltinFunction) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *BuiltinFunction) Copy() Object { - return &BuiltinFunction{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *BuiltinFunction) IsFalsy() bool { - return false -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *BuiltinFunction) Equals(x Object) bool { - return false -} - -// Call executes a builtin function. -func (o *BuiltinFunction) Call(args ...Object) (Object, error) { - return o.Value(args...) -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_len.go b/vendor/github.com/d5/tengo/objects/builtin_len.go deleted file mode 100644 index 39fbedd8..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_len.go +++ /dev/null @@ -1,29 +0,0 @@ -package objects - -// 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(), - } - } -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_module.go b/vendor/github.com/d5/tengo/objects/builtin_module.go deleted file mode 100644 index 0ad1d99d..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_module.go +++ /dev/null @@ -1,23 +0,0 @@ -package objects - -// BuiltinModule is an importable module that's written in Go. -type BuiltinModule struct { - Attrs map[string]Object -} - -// Import returns an immutable map for the module. -func (m *BuiltinModule) Import(moduleName string) (interface{}, error) { - return m.AsImmutableMap(moduleName), nil -} - -// AsImmutableMap converts builtin module into an immutable map. -func (m *BuiltinModule) AsImmutableMap(moduleName string) *ImmutableMap { - attrs := make(map[string]Object, len(m.Attrs)) - for k, v := range m.Attrs { - attrs[k] = v.Copy() - } - - attrs["__module_name__"] = &String{Value: moduleName} - - return &ImmutableMap{Value: attrs} -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_type.go b/vendor/github.com/d5/tengo/objects/builtin_type.go deleted file mode 100644 index 376c26bb..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_type.go +++ /dev/null @@ -1,9 +0,0 @@ -package objects - -func builtinTypeName(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - return &String{Value: args[0].TypeName()}, nil -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_type_checks.go b/vendor/github.com/d5/tengo/objects/builtin_type_checks.go deleted file mode 100644 index d1e8471d..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_type_checks.go +++ /dev/null @@ -1,195 +0,0 @@ -package objects - -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, *Closure: - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsCallable(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - switch args[0].(type) { - case *CompiledFunction, *Closure, Callable: // BuiltinFunction is Callable - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsIterable(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(Iterable); ok { - return TrueValue, nil - } - - return FalseValue, nil -} diff --git a/vendor/github.com/d5/tengo/objects/builtins.go b/vendor/github.com/d5/tengo/objects/builtins.go deleted file mode 100644 index 773636ec..00000000 --- a/vendor/github.com/d5/tengo/objects/builtins.go +++ /dev/null @@ -1,118 +0,0 @@ -package objects - -// Builtins contains all default builtin functions. -// Use GetBuiltinFunctions instead of accessing Builtins directly. -var Builtins = []*BuiltinFunction{ - { - Name: "len", - Value: builtinLen, - }, - { - Name: "copy", - Value: builtinCopy, - }, - { - Name: "append", - Value: builtinAppend, - }, - { - 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, - }, -} diff --git a/vendor/github.com/d5/tengo/objects/bytes.go b/vendor/github.com/d5/tengo/objects/bytes.go deleted file mode 100644 index 5159c22f..00000000 --- a/vendor/github.com/d5/tengo/objects/bytes.go +++ /dev/null @@ -1,89 +0,0 @@ -package objects - -import ( - "bytes" - - "github.com/d5/tengo" - "github.com/d5/tengo/compiler/token" -) - -// Bytes represents a byte array. -type Bytes struct { - Value []byte -} - -func (o *Bytes) String() string { - return string(o.Value) -} - -// TypeName returns the name of the type. -func (o *Bytes) TypeName() string { - return "bytes" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Bytes) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch op { - case token.Add: - switch rhs := rhs.(type) { - case *Bytes: - if len(o.Value)+len(rhs.Value) > tengo.MaxBytesLen { - return nil, ErrBytesLimit - } - - return &Bytes{Value: append(o.Value, rhs.Value...)}, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Bytes) Copy() Object { - return &Bytes{Value: append([]byte{}, o.Value...)} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Bytes) IsFalsy() bool { - return len(o.Value) == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Bytes) Equals(x Object) bool { - t, ok := x.(*Bytes) - if !ok { - return false - } - - return bytes.Equal(o.Value, t.Value) -} - -// IndexGet returns an element (as Int) at a given index. -func (o *Bytes) IndexGet(index Object) (res Object, err error) { - intIdx, ok := index.(*Int) - if !ok { - err = ErrInvalidIndexType - return - } - - idxVal := int(intIdx.Value) - - if idxVal < 0 || idxVal >= len(o.Value) { - res = UndefinedValue - return - } - - res = &Int{Value: int64(o.Value[idxVal])} - - return -} - -// Iterate creates a bytes iterator. -func (o *Bytes) Iterate() Iterator { - return &BytesIterator{ - v: o.Value, - l: len(o.Value), - } -} diff --git a/vendor/github.com/d5/tengo/objects/bytes_iterator.go b/vendor/github.com/d5/tengo/objects/bytes_iterator.go deleted file mode 100644 index 18a36e17..00000000 --- a/vendor/github.com/d5/tengo/objects/bytes_iterator.go +++ /dev/null @@ -1,57 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// BytesIterator represents an iterator for a string. -type BytesIterator struct { - v []byte - i int - l int -} - -// TypeName returns the name of the type. -func (i *BytesIterator) TypeName() string { - return "bytes-iterator" -} - -func (i *BytesIterator) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (i *BytesIterator) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (i *BytesIterator) IsFalsy() bool { - return true -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (i *BytesIterator) Equals(Object) bool { - return false -} - -// Copy returns a copy of the type. -func (i *BytesIterator) Copy() Object { - return &BytesIterator{v: i.v, i: i.i, l: i.l} -} - -// Next returns true if there are more elements to iterate. -func (i *BytesIterator) Next() bool { - i.i++ - return i.i <= i.l -} - -// Key returns the key or index value of the current element. -func (i *BytesIterator) Key() Object { - return &Int{Value: int64(i.i - 1)} -} - -// Value returns the value of the current element. -func (i *BytesIterator) Value() Object { - return &Int{Value: int64(i.v[i.i-1])} -} diff --git a/vendor/github.com/d5/tengo/objects/callable.go b/vendor/github.com/d5/tengo/objects/callable.go deleted file mode 100644 index a066e1b9..00000000 --- a/vendor/github.com/d5/tengo/objects/callable.go +++ /dev/null @@ -1,9 +0,0 @@ -package objects - -// Callable represents an object that can be called like a function. -type Callable interface { - // Call should take an arbitrary number of arguments - // and returns a return value and/or an error, - // which the VM will consider as a run-time error. - Call(args ...Object) (ret Object, err error) -} diff --git a/vendor/github.com/d5/tengo/objects/callable_func.go b/vendor/github.com/d5/tengo/objects/callable_func.go deleted file mode 100644 index ad25e65d..00000000 --- a/vendor/github.com/d5/tengo/objects/callable_func.go +++ /dev/null @@ -1,4 +0,0 @@ -package objects - -// CallableFunc is a function signature for the callable functions. -type CallableFunc = func(args ...Object) (ret Object, err error) diff --git a/vendor/github.com/d5/tengo/objects/char.go b/vendor/github.com/d5/tengo/objects/char.go deleted file mode 100644 index 4458bd12..00000000 --- a/vendor/github.com/d5/tengo/objects/char.go +++ /dev/null @@ -1,119 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// Char represents a character value. -type Char struct { - Value rune -} - -func (o *Char) String() string { - return string(o.Value) -} - -// TypeName returns the name of the type. -func (o *Char) TypeName() string { - return "char" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Char) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch rhs := rhs.(type) { - case *Char: - switch op { - case token.Add: - r := o.Value + rhs.Value - if r == o.Value { - return o, nil - } - return &Char{Value: r}, nil - case token.Sub: - r := o.Value - rhs.Value - if r == o.Value { - return o, nil - } - return &Char{Value: r}, nil - case token.Less: - if o.Value < rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value > rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value <= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value >= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - } - case *Int: - switch op { - case token.Add: - r := o.Value + rune(rhs.Value) - if r == o.Value { - return o, nil - } - return &Char{Value: r}, nil - case token.Sub: - r := o.Value - rune(rhs.Value) - if r == o.Value { - return o, nil - } - return &Char{Value: r}, nil - case token.Less: - if int64(o.Value) < rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if int64(o.Value) > rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if int64(o.Value) <= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if int64(o.Value) >= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Char) Copy() Object { - return &Char{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Char) IsFalsy() bool { - return o.Value == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Char) Equals(x Object) bool { - t, ok := x.(*Char) - if !ok { - return false - } - - return o.Value == t.Value -} diff --git a/vendor/github.com/d5/tengo/objects/closure.go b/vendor/github.com/d5/tengo/objects/closure.go deleted file mode 100644 index 06058b23..00000000 --- a/vendor/github.com/d5/tengo/objects/closure.go +++ /dev/null @@ -1,45 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// Closure represents a function closure. -type Closure struct { - Fn *CompiledFunction - Free []*ObjectPtr -} - -// TypeName returns the name of the type. -func (o *Closure) TypeName() string { - return "closure" -} - -func (o *Closure) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Closure) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Closure) Copy() Object { - return &Closure{ - Fn: o.Fn.Copy().(*CompiledFunction), - Free: append([]*ObjectPtr{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers - } -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Closure) IsFalsy() bool { - return false -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Closure) Equals(x Object) bool { - return false -} diff --git a/vendor/github.com/d5/tengo/objects/compiled_function.go b/vendor/github.com/d5/tengo/objects/compiled_function.go deleted file mode 100644 index d42e69ec..00000000 --- a/vendor/github.com/d5/tengo/objects/compiled_function.go +++ /dev/null @@ -1,62 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// CompiledFunction represents a compiled function. -type CompiledFunction struct { - Instructions []byte - NumLocals int // number of local variables (including function parameters) - NumParameters int - VarArgs bool - SourceMap map[int]source.Pos -} - -// TypeName returns the name of the type. -func (o *CompiledFunction) TypeName() string { - return "compiled-function" -} - -func (o *CompiledFunction) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *CompiledFunction) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *CompiledFunction) Copy() Object { - return &CompiledFunction{ - Instructions: append([]byte{}, o.Instructions...), - NumLocals: o.NumLocals, - NumParameters: o.NumParameters, - VarArgs: o.VarArgs, - } -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *CompiledFunction) IsFalsy() bool { - return false -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *CompiledFunction) Equals(x Object) bool { - return false -} - -// SourcePos returns the source position of the instruction at ip. -func (o *CompiledFunction) SourcePos(ip int) source.Pos { - for ip >= 0 { - if p, ok := o.SourceMap[ip]; ok { - return p - } - ip-- - } - return source.NoPos -} diff --git a/vendor/github.com/d5/tengo/objects/conversion.go b/vendor/github.com/d5/tengo/objects/conversion.go deleted file mode 100644 index 27514132..00000000 --- a/vendor/github.com/d5/tengo/objects/conversion.go +++ /dev/null @@ -1,276 +0,0 @@ -package objects - -import ( - "errors" - "fmt" - "strconv" - "time" - - "github.com/d5/tengo" -) - -// ToString will try to convert object o to string value. -func ToString(o Object) (v string, ok bool) { - if o == UndefinedValue { - //ok = false - return - } - - ok = true - - if str, isStr := o.(*String); isStr { - v = str.Value - } else { - v = o.String() - } - - return -} - -// ToInt will try to convert object o to int value. -func ToInt(o Object) (v int, ok bool) { - switch o := o.(type) { - case *Int: - v = int(o.Value) - ok = true - case *Float: - v = int(o.Value) - ok = true - case *Char: - v = int(o.Value) - ok = true - case *Bool: - if o == TrueValue { - v = 1 - } - ok = true - case *String: - c, err := strconv.ParseInt(o.Value, 10, 64) - if err == nil { - v = int(c) - ok = true - } - } - - //ok = false - return -} - -// ToInt64 will try to convert object o to int64 value. -func ToInt64(o Object) (v int64, ok bool) { - switch o := o.(type) { - case *Int: - v = o.Value - ok = true - case *Float: - v = int64(o.Value) - ok = true - case *Char: - v = int64(o.Value) - ok = true - case *Bool: - if o == TrueValue { - v = 1 - } - ok = true - case *String: - c, err := strconv.ParseInt(o.Value, 10, 64) - if err == nil { - v = c - ok = true - } - } - - //ok = false - return -} - -// ToFloat64 will try to convert object o to float64 value. -func ToFloat64(o Object) (v float64, ok bool) { - switch o := o.(type) { - case *Int: - v = float64(o.Value) - ok = true - case *Float: - v = o.Value - ok = true - case *String: - c, err := strconv.ParseFloat(o.Value, 64) - if err == nil { - v = c - ok = true - } - } - - //ok = false - return -} - -// ToBool will try to convert object o to bool value. -func ToBool(o Object) (v bool, ok bool) { - ok = true - v = !o.IsFalsy() - - return -} - -// ToRune will try to convert object o to rune value. -func ToRune(o Object) (v rune, ok bool) { - switch o := o.(type) { - case *Int: - v = rune(o.Value) - ok = true - case *Char: - v = rune(o.Value) - ok = true - } - - //ok = false - return -} - -// ToByteSlice will try to convert object o to []byte value. -func ToByteSlice(o Object) (v []byte, ok bool) { - switch o := o.(type) { - case *Bytes: - v = o.Value - ok = true - case *String: - v = []byte(o.Value) - ok = true - } - - //ok = false - return -} - -// ToTime will try to convert object o to time.Time value. -func ToTime(o Object) (v time.Time, ok bool) { - switch o := o.(type) { - case *Time: - v = o.Value - ok = true - case *Int: - v = time.Unix(o.Value, 0) - ok = true - } - - //ok = false - return -} - -// ToInterface attempts to convert an object o to an interface{} value -func ToInterface(o Object) (res interface{}) { - switch o := o.(type) { - case *Int: - res = o.Value - case *String: - res = o.Value - case *Float: - res = o.Value - case *Bool: - res = o == TrueValue - case *Char: - res = o.Value - case *Bytes: - res = o.Value - case *Array: - res = make([]interface{}, len(o.Value)) - for i, val := range o.Value { - res.([]interface{})[i] = ToInterface(val) - } - case *ImmutableArray: - res = make([]interface{}, len(o.Value)) - for i, val := range o.Value { - res.([]interface{})[i] = ToInterface(val) - } - case *Map: - res = make(map[string]interface{}) - for key, v := range o.Value { - res.(map[string]interface{})[key] = ToInterface(v) - } - case *ImmutableMap: - res = make(map[string]interface{}) - for key, v := range o.Value { - res.(map[string]interface{})[key] = ToInterface(v) - } - case *Time: - res = o.Value - case *Error: - res = errors.New(o.String()) - case *Undefined: - res = nil - case Object: - return o - } - - return -} - -// FromInterface will attempt to convert an interface{} v to a Tengo Object -func FromInterface(v interface{}) (Object, error) { - switch v := v.(type) { - case nil: - return UndefinedValue, nil - case string: - if len(v) > tengo.MaxStringLen { - return nil, ErrStringLimit - } - return &String{Value: v}, nil - case int64: - return &Int{Value: v}, nil - case int: - return &Int{Value: int64(v)}, nil - case bool: - if v { - return TrueValue, nil - } - return FalseValue, nil - case rune: - return &Char{Value: v}, nil - case byte: - return &Char{Value: rune(v)}, nil - case float64: - return &Float{Value: v}, nil - case []byte: - if len(v) > tengo.MaxBytesLen { - return nil, ErrBytesLimit - } - return &Bytes{Value: v}, nil - case error: - return &Error{Value: &String{Value: v.Error()}}, nil - case map[string]Object: - return &Map{Value: v}, nil - case map[string]interface{}: - kv := make(map[string]Object) - for vk, vv := range v { - vo, err := FromInterface(vv) - if err != nil { - return nil, err - } - kv[vk] = vo - } - return &Map{Value: kv}, nil - case []Object: - return &Array{Value: v}, nil - case []interface{}: - arr := make([]Object, len(v)) - for i, e := range v { - vo, err := FromInterface(e) - if err != nil { - return nil, err - } - - arr[i] = vo - } - return &Array{Value: arr}, nil - case time.Time: - return &Time{Value: v}, nil - case Object: - return v, nil - case CallableFunc: - return &UserFunction{Value: v}, nil - } - - return nil, fmt.Errorf("cannot convert to object: %T", v) -} diff --git a/vendor/github.com/d5/tengo/objects/count_objects.go b/vendor/github.com/d5/tengo/objects/count_objects.go deleted file mode 100644 index 8c482eb3..00000000 --- a/vendor/github.com/d5/tengo/objects/count_objects.go +++ /dev/null @@ -1,31 +0,0 @@ -package objects - -// CountObjects returns the number of objects that a given object o contains. -// For scalar value types, it will always be 1. For compound value types, -// this will include its elements and all of their elements recursively. -func CountObjects(o Object) (c int) { - c = 1 - - switch o := o.(type) { - case *Array: - for _, v := range o.Value { - c += CountObjects(v) - } - case *ImmutableArray: - for _, v := range o.Value { - c += CountObjects(v) - } - case *Map: - for _, v := range o.Value { - c += CountObjects(v) - } - case *ImmutableMap: - for _, v := range o.Value { - c += CountObjects(v) - } - case *Error: - c += CountObjects(o.Value) - } - - return -} diff --git a/vendor/github.com/d5/tengo/objects/error.go b/vendor/github.com/d5/tengo/objects/error.go deleted file mode 100644 index be21de03..00000000 --- a/vendor/github.com/d5/tengo/objects/error.go +++ /dev/null @@ -1,47 +0,0 @@ -package objects - -import ( - "fmt" - - "github.com/d5/tengo/compiler/token" -) - -// Error represents a string value. -type Error struct { - Value Object -} - -// TypeName returns the name of the type. -func (o *Error) TypeName() string { - return "error" -} - -func (o *Error) String() string { - if o.Value != nil { - return fmt.Sprintf("error: %s", o.Value.String()) - } - - return "error" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Error) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Error) IsFalsy() bool { - return true // error is always false. -} - -// Copy returns a copy of the type. -func (o *Error) Copy() Object { - return &Error{Value: o.Value.Copy()} -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Error) Equals(x Object) bool { - return o == x // pointer equality -} diff --git a/vendor/github.com/d5/tengo/objects/errors.go b/vendor/github.com/d5/tengo/objects/errors.go deleted file mode 100644 index bcd480a1..00000000 --- a/vendor/github.com/d5/tengo/objects/errors.go +++ /dev/null @@ -1,38 +0,0 @@ -package objects - -import ( - "errors" - "fmt" -) - -// ErrIndexOutOfBounds is an error where a given index is out of the bounds. -var ErrIndexOutOfBounds = errors.New("index out of bounds") - -// ErrInvalidIndexType represents an invalid index type. -var ErrInvalidIndexType = errors.New("invalid index type") - -// ErrInvalidIndexValueType represents an invalid index value type. -var ErrInvalidIndexValueType = errors.New("invalid index value type") - -// ErrInvalidOperator represents an error for invalid operator usage. -var ErrInvalidOperator = errors.New("invalid operator") - -// ErrWrongNumArguments represents a wrong number of arguments error. -var ErrWrongNumArguments = errors.New("wrong number of arguments") - -// ErrBytesLimit represents an error where the size of bytes value exceeds the limit. -var ErrBytesLimit = errors.New("exceeding bytes size limit") - -// ErrStringLimit represents an error where the size of string value exceeds the limit. -var ErrStringLimit = errors.New("exceeding string size limit") - -// ErrInvalidArgumentType represents an invalid argument value type error. -type ErrInvalidArgumentType struct { - Name string - Expected string - Found string -} - -func (e ErrInvalidArgumentType) Error() string { - return fmt.Sprintf("invalid type for argument '%s': expected %s, found %s", e.Name, e.Expected, e.Found) -} diff --git a/vendor/github.com/d5/tengo/objects/float.go b/vendor/github.com/d5/tengo/objects/float.go deleted file mode 100644 index 65997303..00000000 --- a/vendor/github.com/d5/tengo/objects/float.go +++ /dev/null @@ -1,146 +0,0 @@ -package objects - -import ( - "math" - "strconv" - - "github.com/d5/tengo/compiler/token" -) - -// Float represents a floating point number value. -type Float struct { - Value float64 -} - -func (o *Float) String() string { - return strconv.FormatFloat(o.Value, 'f', -1, 64) -} - -// TypeName returns the name of the type. -func (o *Float) TypeName() string { - return "float" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Float) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch rhs := rhs.(type) { - case *Float: - switch op { - case token.Add: - r := o.Value + rhs.Value - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Sub: - r := o.Value - rhs.Value - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Mul: - r := o.Value * rhs.Value - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Quo: - r := o.Value / rhs.Value - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Less: - if o.Value < rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value > rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value <= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value >= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - } - case *Int: - switch op { - case token.Add: - r := o.Value + float64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Sub: - r := o.Value - float64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Mul: - r := o.Value * float64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Quo: - r := o.Value / float64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Less: - if o.Value < float64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value > float64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value <= float64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value >= float64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Float) Copy() Object { - return &Float{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Float) IsFalsy() bool { - return math.IsNaN(o.Value) -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Float) Equals(x Object) bool { - t, ok := x.(*Float) - if !ok { - return false - } - - return o.Value == t.Value -} diff --git a/vendor/github.com/d5/tengo/objects/formatter.go b/vendor/github.com/d5/tengo/objects/formatter.go deleted file mode 100644 index 95d7f6b1..00000000 --- a/vendor/github.com/d5/tengo/objects/formatter.go +++ /dev/null @@ -1,1212 +0,0 @@ -package objects - -import ( - "strconv" - "sync" - "unicode/utf8" - - "github.com/d5/tengo" -) - -// Strings for use with buffer.WriteString. -// This is less overhead than using buffer.Write with byte arrays. -const ( - commaSpaceString = ", " - nilParenString = "(nil)" - percentBangString = "%!" - missingString = "(MISSING)" - badIndexString = "(BADINDEX)" - extraString = "%!(EXTRA " - badWidthString = "%!(BADWIDTH)" - badPrecString = "%!(BADPREC)" - noVerbString = "%!(NOVERB)" -) - -const ( - ldigits = "0123456789abcdefx" - udigits = "0123456789ABCDEFX" -) - -const ( - signed = true - unsigned = false -) - -// flags placed in a separate struct for easy clearing. -type fmtFlags struct { - widPresent bool - precPresent bool - minus bool - plus bool - sharp bool - space bool - zero bool - - // For the formats %+v %#v, we set the plusV/sharpV flags - // and clear the plus/sharp flags since %+v and %#v are in effect - // different, flagless formats set at the top level. - plusV bool - sharpV bool - - // error-related flags. - inDetail bool - needNewline bool - needColon bool -} - -// A formatter is the raw formatter used by Printf etc. -// It prints into a buffer that must be set up separately. -type formatter struct { - buf *buffer - - fmtFlags - - wid int // width - prec int // precision - - // intbuf is large enough to store %b of an int64 with a sign and - // avoids padding at the end of the struct on 32 bit architectures. - intbuf [68]byte -} - -func (f *formatter) clearflags() { - f.fmtFlags = fmtFlags{} -} - -func (f *formatter) init(buf *buffer) { - f.buf = buf - f.clearflags() -} - -// writePadding generates n bytes of padding. -func (f *formatter) writePadding(n int) { - if n <= 0 { // No padding bytes needed. - return - } - buf := *f.buf - oldLen := len(buf) - newLen := oldLen + n - - if newLen > tengo.MaxStringLen { - panic(ErrStringLimit) - } - - // Make enough room for padding. - if newLen > cap(buf) { - buf = make(buffer, cap(buf)*2+n) - copy(buf, *f.buf) - } - // Decide which byte the padding should be filled with. - padByte := byte(' ') - if f.zero { - padByte = byte('0') - } - // Fill padding with padByte. - padding := buf[oldLen:newLen] - for i := range padding { - padding[i] = padByte - } - *f.buf = buf[:newLen] -} - -// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus). -func (f *formatter) pad(b []byte) { - if !f.widPresent || f.wid == 0 { - f.buf.Write(b) - return - } - width := f.wid - utf8.RuneCount(b) - if !f.minus { - // left padding - f.writePadding(width) - f.buf.Write(b) - } else { - // right padding - f.buf.Write(b) - f.writePadding(width) - } -} - -// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus). -func (f *formatter) padString(s string) { - if !f.widPresent || f.wid == 0 { - f.buf.WriteString(s) - return - } - width := f.wid - utf8.RuneCountInString(s) - if !f.minus { - // left padding - f.writePadding(width) - f.buf.WriteString(s) - } else { - // right padding - f.buf.WriteString(s) - f.writePadding(width) - } -} - -// fmtBoolean formats a boolean. -func (f *formatter) fmtBoolean(v bool) { - if v { - f.padString("true") - } else { - f.padString("false") - } -} - -// fmtUnicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'". -func (f *formatter) fmtUnicode(u uint64) { - buf := f.intbuf[0:] - - // With default precision set the maximum needed buf length is 18 - // for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF") which fits - // into the already allocated intbuf with a capacity of 68 bytes. - prec := 4 - if f.precPresent && f.prec > 4 { - prec = f.prec - // Compute space needed for "U+" , number, " '", character, "'". - width := 2 + prec + 2 + utf8.UTFMax + 1 - if width > len(buf) { - buf = make([]byte, width) - } - } - - // Format into buf, ending at buf[i]. Formatting numbers is easier right-to-left. - i := len(buf) - - // For %#U we want to add a space and a quoted character at the end of the buffer. - if f.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) { - i-- - buf[i] = '\'' - i -= utf8.RuneLen(rune(u)) - utf8.EncodeRune(buf[i:], rune(u)) - i-- - buf[i] = '\'' - i-- - buf[i] = ' ' - } - // Format the Unicode code point u as a hexadecimal number. - for u >= 16 { - i-- - buf[i] = udigits[u&0xF] - prec-- - u >>= 4 - } - i-- - buf[i] = udigits[u] - prec-- - // Add zeros in front of the number until requested precision is reached. - for prec > 0 { - i-- - buf[i] = '0' - prec-- - } - // Add a leading "U+". - i-- - buf[i] = '+' - i-- - buf[i] = 'U' - - oldZero := f.zero - f.zero = false - f.pad(buf[i:]) - f.zero = oldZero -} - -// fmtInteger formats signed and unsigned integers. -func (f *formatter) fmtInteger(u uint64, base int, isSigned bool, verb rune, digits string) { - negative := isSigned && int64(u) < 0 - if negative { - u = -u - } - - buf := f.intbuf[0:] - // The already allocated f.intbuf with a capacity of 68 bytes - // is large enough for integer formatting when no precision or width is set. - if f.widPresent || f.precPresent { - // Account 3 extra bytes for possible addition of a sign and "0x". - width := 3 + f.wid + f.prec // wid and prec are always positive. - if width > len(buf) { - // We're going to need a bigger boat. - buf = make([]byte, width) - } - } - - // Two ways to ask for extra leading zero digits: %.3d or %03d. - // If both are specified the f.zero flag is ignored and - // padding with spaces is used instead. - prec := 0 - if f.precPresent { - prec = f.prec - // Precision of 0 and value of 0 means "print nothing" but padding. - if prec == 0 && u == 0 { - oldZero := f.zero - f.zero = false - f.writePadding(f.wid) - f.zero = oldZero - return - } - } else if f.zero && f.widPresent { - prec = f.wid - if negative || f.plus || f.space { - prec-- // leave room for sign - } - } - - // Because printing is easier right-to-left: format u into buf, ending at buf[i]. - // We could make things marginally faster by splitting the 32-bit case out - // into a separate block but it's not worth the duplication, so u has 64 bits. - i := len(buf) - // Use constants for the division and modulo for more efficient code. - // Switch cases ordered by popularity. - switch base { - case 10: - for u >= 10 { - i-- - next := u / 10 - buf[i] = byte('0' + u - next*10) - u = next - } - case 16: - for u >= 16 { - i-- - buf[i] = digits[u&0xF] - u >>= 4 - } - case 8: - for u >= 8 { - i-- - buf[i] = byte('0' + u&7) - u >>= 3 - } - case 2: - for u >= 2 { - i-- - buf[i] = byte('0' + u&1) - u >>= 1 - } - default: - panic("fmt: unknown base; can't happen") - } - i-- - buf[i] = digits[u] - for i > 0 && prec > len(buf)-i { - i-- - buf[i] = '0' - } - - // Various prefixes: 0x, -, etc. - if f.sharp { - switch base { - case 2: - // Add a leading 0b. - i-- - buf[i] = 'b' - i-- - buf[i] = '0' - case 8: - if buf[i] != '0' { - i-- - buf[i] = '0' - } - case 16: - // Add a leading 0x or 0X. - i-- - buf[i] = digits[16] - i-- - buf[i] = '0' - } - } - if verb == 'O' { - i-- - buf[i] = 'o' - i-- - buf[i] = '0' - } - - if negative { - i-- - buf[i] = '-' - } else if f.plus { - i-- - buf[i] = '+' - } else if f.space { - i-- - buf[i] = ' ' - } - - // Left padding with zeros has already been handled like precision earlier - // or the f.zero flag is ignored due to an explicitly set precision. - oldZero := f.zero - f.zero = false - f.pad(buf[i:]) - f.zero = oldZero -} - -// truncate truncates the string s to the specified precision, if present. -func (f *formatter) truncateString(s string) string { - if f.precPresent { - n := f.prec - for i := range s { - n-- - if n < 0 { - return s[:i] - } - } - } - return s -} - -// truncate truncates the byte slice b as a string of the specified precision, if present. -func (f *formatter) truncate(b []byte) []byte { - if f.precPresent { - n := f.prec - for i := 0; i < len(b); { - n-- - if n < 0 { - return b[:i] - } - wid := 1 - if b[i] >= utf8.RuneSelf { - _, wid = utf8.DecodeRune(b[i:]) - } - i += wid - } - } - return b -} - -// fmtS formats a string. -func (f *formatter) fmtS(s string) { - s = f.truncateString(s) - f.padString(s) -} - -// fmtBs formats the byte slice b as if it was formatted as string with fmtS. -func (f *formatter) fmtBs(b []byte) { - b = f.truncate(b) - f.pad(b) -} - -// fmtSbx formats a string or byte slice as a hexadecimal encoding of its bytes. -func (f *formatter) fmtSbx(s string, b []byte, digits string) { - length := len(b) - if b == nil { - // No byte slice present. Assume string s should be encoded. - length = len(s) - } - // Set length to not process more bytes than the precision demands. - if f.precPresent && f.prec < length { - length = f.prec - } - // Compute width of the encoding taking into account the f.sharp and f.space flag. - width := 2 * length - if width > 0 { - if f.space { - // Each element encoded by two hexadecimals will get a leading 0x or 0X. - if f.sharp { - width *= 2 - } - // Elements will be separated by a space. - width += length - 1 - } else if f.sharp { - // Only a leading 0x or 0X will be added for the whole string. - width += 2 - } - } else { // The byte slice or string that should be encoded is empty. - if f.widPresent { - f.writePadding(f.wid) - } - return - } - // Handle padding to the left. - if f.widPresent && f.wid > width && !f.minus { - f.writePadding(f.wid - width) - } - // Write the encoding directly into the output buffer. - buf := *f.buf - if f.sharp { - // Add leading 0x or 0X. - buf = append(buf, '0', digits[16]) - } - var c byte - for i := 0; i < length; i++ { - if f.space && i > 0 { - // Separate elements with a space. - buf = append(buf, ' ') - if f.sharp { - // Add leading 0x or 0X for each element. - buf = append(buf, '0', digits[16]) - } - } - if b != nil { - c = b[i] // Take a byte from the input byte slice. - } else { - c = s[i] // Take a byte from the input string. - } - // Encode each byte as two hexadecimal digits. - buf = append(buf, digits[c>>4], digits[c&0xF]) - } - *f.buf = buf - // Handle padding to the right. - if f.widPresent && f.wid > width && f.minus { - f.writePadding(f.wid - width) - } -} - -// fmtSx formats a string as a hexadecimal encoding of its bytes. -func (f *formatter) fmtSx(s, digits string) { - f.fmtSbx(s, nil, digits) -} - -// fmtBx formats a byte slice as a hexadecimal encoding of its bytes. -func (f *formatter) fmtBx(b []byte, digits string) { - f.fmtSbx("", b, digits) -} - -// fmtQ formats a string as a double-quoted, escaped Go string constant. -// If f.sharp is set a raw (backquoted) string may be returned instead -// if the string does not contain any control characters other than tab. -func (f *formatter) fmtQ(s string) { - s = f.truncateString(s) - if f.sharp && strconv.CanBackquote(s) { - f.padString("`" + s + "`") - return - } - buf := f.intbuf[:0] - if f.plus { - f.pad(strconv.AppendQuoteToASCII(buf, s)) - } else { - f.pad(strconv.AppendQuote(buf, s)) - } -} - -// fmtC formats an integer as a Unicode character. -// If the character is not valid Unicode, it will print '\ufffd'. -func (f *formatter) fmtC(c uint64) { - r := rune(c) - if c > utf8.MaxRune { - r = utf8.RuneError - } - buf := f.intbuf[:0] - w := utf8.EncodeRune(buf[:utf8.UTFMax], r) - f.pad(buf[:w]) -} - -// fmtQc formats an integer as a single-quoted, escaped Go character constant. -// If the character is not valid Unicode, it will print '\ufffd'. -func (f *formatter) fmtQc(c uint64) { - r := rune(c) - if c > utf8.MaxRune { - r = utf8.RuneError - } - buf := f.intbuf[:0] - if f.plus { - f.pad(strconv.AppendQuoteRuneToASCII(buf, r)) - } else { - f.pad(strconv.AppendQuoteRune(buf, r)) - } -} - -// fmtFloat formats a float64. It assumes that verb is a valid format specifier -// for strconv.AppendFloat and therefore fits into a byte. -func (f *formatter) fmtFloat(v float64, size int, verb rune, prec int) { - // Explicit precision in format specifier overrules default precision. - if f.precPresent { - prec = f.prec - } - // Format number, reserving space for leading + sign if needed. - num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size) - if num[1] == '-' || num[1] == '+' { - num = num[1:] - } else { - num[0] = '+' - } - // f.space means to add a leading space instead of a "+" sign unless - // the sign is explicitly asked for by f.plus. - if f.space && num[0] == '+' && !f.plus { - num[0] = ' ' - } - // Special handling for infinities and NaN, - // which don't look like a number so shouldn't be padded with zeros. - if num[1] == 'I' || num[1] == 'N' { - oldZero := f.zero - f.zero = false - // Remove sign before NaN if not asked for. - if num[1] == 'N' && !f.space && !f.plus { - num = num[1:] - } - f.pad(num) - f.zero = oldZero - return - } - // The sharp flag forces printing a decimal point for non-binary formats - // and retains trailing zeros, which we may need to restore. - if f.sharp && verb != 'b' { - digits := 0 - switch verb { - case 'v', 'g', 'G', 'x': - digits = prec - // If no precision is set explicitly use a precision of 6. - if digits == -1 { - digits = 6 - } - } - - // Buffer pre-allocated with enough room for - // exponent notations of the form "e+123" or "p-1023". - var tailBuf [6]byte - tail := tailBuf[:0] - - hasDecimalPoint := false - // Starting from i = 1 to skip sign at num[0]. - for i := 1; i < len(num); i++ { - switch num[i] { - case '.': - hasDecimalPoint = true - case 'p', 'P': - tail = append(tail, num[i:]...) - num = num[:i] - case 'e', 'E': - if verb != 'x' && verb != 'X' { - tail = append(tail, num[i:]...) - num = num[:i] - break - } - fallthrough - default: - digits-- - } - } - if !hasDecimalPoint { - num = append(num, '.') - } - for digits > 0 { - num = append(num, '0') - digits-- - } - num = append(num, tail...) - } - // We want a sign if asked for and if the sign is not positive. - if f.plus || num[0] != '+' { - // If we're zero padding to the left we want the sign before the leading zeros. - // Achieve this by writing the sign out and then padding the unsigned number. - if f.zero && f.widPresent && f.wid > len(num) { - f.buf.WriteSingleByte(num[0]) - f.writePadding(f.wid - len(num)) - f.buf.Write(num[1:]) - return - } - f.pad(num) - return - } - // No sign to show and the number is positive; just print the unsigned number. - f.pad(num[1:]) -} - -// Use simple []byte instead of bytes.Buffer to avoid large dependency. -type buffer []byte - -func (b *buffer) Write(p []byte) { - if len(*b)+len(p) > tengo.MaxStringLen { - panic(ErrStringLimit) - } - - *b = append(*b, p...) -} - -func (b *buffer) WriteString(s string) { - if len(*b)+len(s) > tengo.MaxStringLen { - panic(ErrStringLimit) - } - - *b = append(*b, s...) -} - -func (b *buffer) WriteSingleByte(c byte) { - if len(*b) >= tengo.MaxStringLen { - panic(ErrStringLimit) - } - - *b = append(*b, c) -} - -func (b *buffer) WriteRune(r rune) { - if len(*b)+utf8.RuneLen(r) > tengo.MaxStringLen { - panic(ErrStringLimit) - } - - if r < utf8.RuneSelf { - *b = append(*b, byte(r)) - return - } - - b2 := *b - n := len(b2) - for n+utf8.UTFMax > cap(b2) { - b2 = append(b2, 0) - } - w := utf8.EncodeRune(b2[n:n+utf8.UTFMax], r) - *b = b2[:n+w] -} - -// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations. -type pp struct { - buf buffer - - // arg holds the current item. - arg Object - - // fmt is used to format basic items such as integers or strings. - fmt formatter - - // reordered records whether the format string used argument reordering. - reordered bool - - // goodArgNum records whether the most recent reordering directive was valid. - goodArgNum bool - - // erroring is set when printing an error string to guard against calling handleMethods. - erroring bool -} - -var ppFree = sync.Pool{ - New: func() interface{} { return new(pp) }, -} - -// newPrinter allocates a new pp struct or grabs a cached one. -func newPrinter() *pp { - p := ppFree.Get().(*pp) - p.erroring = false - p.fmt.init(&p.buf) - return p -} - -// free saves used pp structs in ppFree; avoids an allocation per invocation. -func (p *pp) free() { - // Proper usage of a sync.Pool requires each entry to have approximately - // the same memory cost. To obtain this property when the stored type - // contains a variably-sized buffer, we add a hard limit on the maximum buffer - // to place back in the pool. - // - // See https://golang.org/issue/23199 - if cap(p.buf) > 64<<10 { - return - } - - p.buf = p.buf[:0] - p.arg = nil - ppFree.Put(p) -} - -func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent } - -func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent } - -func (p *pp) Flag(b int) bool { - switch b { - case '-': - return p.fmt.minus - case '+': - return p.fmt.plus || p.fmt.plusV - case '#': - return p.fmt.sharp || p.fmt.sharpV - case ' ': - return p.fmt.space - case '0': - return p.fmt.zero - } - return false -} - -// Implement Write so we can call Fprintf on a pp (through State), for -// recursive use in custom verbs. -func (p *pp) Write(b []byte) (ret int, err error) { - p.buf.Write(b) - return len(b), nil -} - -// Implement WriteString so that we can call io.WriteString -// on a pp (through state), for efficiency. -func (p *pp) WriteString(s string) (ret int, err error) { - p.buf.WriteString(s) - return len(s), nil -} - -func (p *pp) WriteRune(r rune) (ret int, err error) { - p.buf.WriteRune(r) - return utf8.RuneLen(r), nil -} - -func (p *pp) WriteSingleByte(c byte) (ret int, err error) { - p.buf.WriteSingleByte(c) - return 1, nil -} - -// tooLarge reports whether the magnitude of the integer is -// too large to be used as a formatting width or precision. -func tooLarge(x int) bool { - const max int = 1e6 - return x > max || x < -max -} - -// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present. -func parsenum(s string, start, end int) (num int, isnum bool, newi int) { - if start >= end { - return 0, false, end - } - for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ { - if tooLarge(num) { - return 0, false, end // Overflow; crazy long number most likely. - } - num = num*10 + int(s[newi]-'0') - isnum = true - } - return -} - -func (p *pp) badVerb(verb rune) { - p.erroring = true - _, _ = p.WriteString(percentBangString) - _, _ = p.WriteRune(verb) - _, _ = p.WriteSingleByte('(') - switch { - case p.arg != nil: - _, _ = p.WriteString(p.arg.String()) - _, _ = p.WriteSingleByte('=') - p.printArg(p.arg, 'v') - default: - _, _ = p.WriteString(UndefinedValue.String()) - } - _, _ = p.WriteSingleByte(')') - p.erroring = false -} - -func (p *pp) fmtBool(v bool, verb rune) { - switch verb { - case 't', 'v': - p.fmt.fmtBoolean(v) - default: - p.badVerb(verb) - } -} - -// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or -// not, as requested, by temporarily setting the sharp flag. -func (p *pp) fmt0x64(v uint64, leading0x bool) { - sharp := p.fmt.sharp - p.fmt.sharp = leading0x - p.fmt.fmtInteger(v, 16, unsigned, 'v', ldigits) - p.fmt.sharp = sharp -} - -// fmtInteger formats a signed or unsigned integer. -func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) { - switch verb { - case 'v': - if p.fmt.sharpV && !isSigned { - p.fmt0x64(v, true) - } else { - p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits) - } - case 'd': - p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits) - case 'b': - p.fmt.fmtInteger(v, 2, isSigned, verb, ldigits) - case 'o', 'O': - p.fmt.fmtInteger(v, 8, isSigned, verb, ldigits) - case 'x': - p.fmt.fmtInteger(v, 16, isSigned, verb, ldigits) - case 'X': - p.fmt.fmtInteger(v, 16, isSigned, verb, udigits) - case 'c': - p.fmt.fmtC(v) - case 'q': - if v <= utf8.MaxRune { - p.fmt.fmtQc(v) - } else { - p.badVerb(verb) - } - case 'U': - p.fmt.fmtUnicode(v) - default: - p.badVerb(verb) - } -} - -// fmtFloat formats a float. The default precision for each verb -// is specified as last argument in the call to fmt_float. -func (p *pp) fmtFloat(v float64, size int, verb rune) { - switch verb { - case 'v': - p.fmt.fmtFloat(v, size, 'g', -1) - case 'b', 'g', 'G', 'x', 'X': - p.fmt.fmtFloat(v, size, verb, -1) - case 'f', 'e', 'E': - p.fmt.fmtFloat(v, size, verb, 6) - case 'F': - p.fmt.fmtFloat(v, size, 'f', 6) - default: - p.badVerb(verb) - } -} - -func (p *pp) fmtString(v string, verb rune) { - switch verb { - case 'v': - if p.fmt.sharpV { - p.fmt.fmtQ(v) - } else { - p.fmt.fmtS(v) - } - case 's': - p.fmt.fmtS(v) - case 'x': - p.fmt.fmtSx(v, ldigits) - case 'X': - p.fmt.fmtSx(v, udigits) - case 'q': - p.fmt.fmtQ(v) - default: - p.badVerb(verb) - } -} - -func (p *pp) fmtBytes(v []byte, verb rune, typeString string) { - switch verb { - case 'v', 'd': - if p.fmt.sharpV { - _, _ = p.WriteString(typeString) - if v == nil { - _, _ = p.WriteString(nilParenString) - return - } - _, _ = p.WriteSingleByte('{') - for i, c := range v { - if i > 0 { - _, _ = p.WriteString(commaSpaceString) - } - p.fmt0x64(uint64(c), true) - } - _, _ = p.WriteSingleByte('}') - } else { - _, _ = p.WriteSingleByte('[') - for i, c := range v { - if i > 0 { - _, _ = p.WriteSingleByte(' ') - } - p.fmt.fmtInteger(uint64(c), 10, unsigned, verb, ldigits) - } - _, _ = p.WriteSingleByte(']') - } - case 's': - p.fmt.fmtBs(v) - case 'x': - p.fmt.fmtBx(v, ldigits) - case 'X': - p.fmt.fmtBx(v, udigits) - case 'q': - p.fmt.fmtQ(string(v)) - } -} - -func (p *pp) printArg(arg Object, verb rune) { - p.arg = arg - - if arg == nil { - arg = UndefinedValue - } - - // Special processing considerations. - // %T (the value's type) and %p (its address) are special; we always do them first. - switch verb { - case 'T': - p.fmt.fmtS(arg.TypeName()) - return - case 'v': - p.fmt.fmtS(arg.String()) - return - } - - // Some types can be done without reflection. - switch f := arg.(type) { - case *Bool: - p.fmtBool(!f.IsFalsy(), verb) - case *Float: - p.fmtFloat(f.Value, 64, verb) - case *Int: - p.fmtInteger(uint64(f.Value), signed, verb) - case *String: - p.fmtString(f.Value, verb) - case *Bytes: - p.fmtBytes(f.Value, verb, "[]byte") - default: - p.fmtString(f.String(), verb) - } -} - -// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has integer type. -func intFromArg(a []Object, argNum int) (num int, isInt bool, newArgNum int) { - newArgNum = argNum - if argNum < len(a) { - var num64 int64 - num64, isInt = ToInt64(a[argNum]) - num = int(num64) - newArgNum = argNum + 1 - if tooLarge(num) { - num = 0 - isInt = false - } - } - return -} - -// parseArgNumber returns the value of the bracketed number, minus 1 -// (explicit argument numbers are one-indexed but we want zero-indexed). -// The opening bracket is known to be present at format[0]. -// The returned values are the index, the number of bytes to consume -// up to the closing paren, if present, and whether the number parsed -// ok. The bytes to consume will be 1 if no closing paren is present. -func parseArgNumber(format string) (index int, wid int, ok bool) { - // There must be at least 3 bytes: [n]. - if len(format) < 3 { - return 0, 1, false - } - - // Find closing bracket. - for i := 1; i < len(format); i++ { - if format[i] == ']' { - width, ok, newi := parsenum(format, 1, i) - if !ok || newi != i { - return 0, i + 1, false - } - return width - 1, i + 1, true // arg numbers are one-indexed and skip paren. - } - } - return 0, 1, false -} - -// argNumber returns the next argument to evaluate, which is either the value of the passed-in -// argNum or the value of the bracketed integer that begins format[i:]. It also returns -// the new value of i, that is, the index of the next byte of the format to process. -func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) { - if len(format) <= i || format[i] != '[' { - return argNum, i, false - } - p.reordered = true - index, wid, ok := parseArgNumber(format[i:]) - if ok && 0 <= index && index < numArgs { - return index, i + wid, true - } - p.goodArgNum = false - return argNum, i + wid, ok -} - -func (p *pp) badArgNum(verb rune) { - _, _ = p.WriteString(percentBangString) - _, _ = p.WriteRune(verb) - _, _ = p.WriteString(badIndexString) -} - -func (p *pp) missingArg(verb rune) { - _, _ = p.WriteString(percentBangString) - _, _ = p.WriteRune(verb) - _, _ = p.WriteString(missingString) -} - -func (p *pp) doFormat(format string, a []Object) (err error) { - defer func() { - if r := recover(); r != nil { - if e, ok := r.(error); ok && e == ErrStringLimit { - err = e - return - } - panic(r) - } - }() - - end := len(format) - argNum := 0 // we process one argument per non-trivial format - afterIndex := false // previous item in format was an index like [3]. - p.reordered = false -formatLoop: - for i := 0; i < end; { - p.goodArgNum = true - lasti := i - for i < end && format[i] != '%' { - i++ - } - if i > lasti { - _, _ = p.WriteString(format[lasti:i]) - } - if i >= end { - // done processing format string - break - } - - // Process one verb - i++ - - // Do we have flags? - p.fmt.clearflags() - simpleFormat: - for ; i < end; i++ { - c := format[i] - switch c { - case '#': - p.fmt.sharp = true - case '0': - p.fmt.zero = !p.fmt.minus // Only allow zero padding to the left. - case '+': - p.fmt.plus = true - case '-': - p.fmt.minus = true - p.fmt.zero = false // Do not pad with zeros to the right. - case ' ': - p.fmt.space = true - default: - // Fast path for common case of ascii lower case simple verbs - // without precision or width or argument indices. - if 'a' <= c && c <= 'z' && argNum < len(a) { - if c == 'v' { - // Go syntax - p.fmt.sharpV = p.fmt.sharp - p.fmt.sharp = false - // Struct-field syntax - p.fmt.plusV = p.fmt.plus - p.fmt.plus = false - } - p.printArg(a[argNum], rune(c)) - argNum++ - i++ - continue formatLoop - } - // Format is more complex than simple flags and a verb or is malformed. - break simpleFormat - } - } - - // Do we have an explicit argument index? - argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) - - // Do we have width? - if i < end && format[i] == '*' { - i++ - p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum) - - if !p.fmt.widPresent { - _, _ = p.WriteString(badWidthString) - } - - // We have a negative width, so take its value and ensure - // that the minus flag is set - if p.fmt.wid < 0 { - p.fmt.wid = -p.fmt.wid - p.fmt.minus = true - p.fmt.zero = false // Do not pad with zeros to the right. - } - afterIndex = false - } else { - p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) - if afterIndex && p.fmt.widPresent { // "%[3]2d" - p.goodArgNum = false - } - } - - // Do we have precision? - if i+1 < end && format[i] == '.' { - i++ - if afterIndex { // "%[3].2d" - p.goodArgNum = false - } - argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) - if i < end && format[i] == '*' { - i++ - p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum) - // Negative precision arguments don't make sense - if p.fmt.prec < 0 { - p.fmt.prec = 0 - p.fmt.precPresent = false - } - if !p.fmt.precPresent { - _, _ = p.WriteString(badPrecString) - } - afterIndex = false - } else { - p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i, end) - if !p.fmt.precPresent { - p.fmt.prec = 0 - p.fmt.precPresent = true - } - } - } - - if !afterIndex { - argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) - } - - if i >= end { - _, _ = p.WriteString(noVerbString) - break - } - - verb, size := rune(format[i]), 1 - if verb >= utf8.RuneSelf { - verb, size = utf8.DecodeRuneInString(format[i:]) - } - i += size - - switch { - case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec. - _, _ = p.WriteSingleByte('%') - case !p.goodArgNum: - p.badArgNum(verb) - case argNum >= len(a): // No argument left over to print for the current verb. - p.missingArg(verb) - case verb == 'v': - // Go syntax - p.fmt.sharpV = p.fmt.sharp - p.fmt.sharp = false - // Struct-field syntax - p.fmt.plusV = p.fmt.plus - p.fmt.plus = false - fallthrough - default: - p.printArg(a[argNum], verb) - argNum++ - } - } - - // Check for extra arguments unless the call accessed the arguments - // out of order, in which case it's too expensive to detect if they've all - // been used and arguably OK if they're not. - if !p.reordered && argNum < len(a) { - p.fmt.clearflags() - _, _ = p.WriteString(extraString) - for i, arg := range a[argNum:] { - if i > 0 { - _, _ = p.WriteString(commaSpaceString) - } - if arg == nil { - _, _ = p.WriteString(UndefinedValue.String()) - } else { - _, _ = p.WriteString(arg.TypeName()) - _, _ = p.WriteSingleByte('=') - p.printArg(arg, 'v') - } - } - _, _ = p.WriteSingleByte(')') - } - - return nil -} - -// Format formats according to a format specifier and returns the resulting string. -func Format(format string, a ...Object) (string, error) { - p := newPrinter() - err := p.doFormat(format, a) - s := string(p.buf) - p.free() - - return s, err -} diff --git a/vendor/github.com/d5/tengo/objects/immutable_array.go b/vendor/github.com/d5/tengo/objects/immutable_array.go deleted file mode 100644 index f3621e29..00000000 --- a/vendor/github.com/d5/tengo/objects/immutable_array.go +++ /dev/null @@ -1,109 +0,0 @@ -package objects - -import ( - "fmt" - "strings" - - "github.com/d5/tengo/compiler/token" -) - -// ImmutableArray represents an immutable array of objects. -type ImmutableArray struct { - Value []Object -} - -// TypeName returns the name of the type. -func (o *ImmutableArray) TypeName() string { - return "immutable-array" -} - -func (o *ImmutableArray) String() string { - var elements []string - for _, e := range o.Value { - elements = append(elements, e.String()) - } - - return fmt.Sprintf("[%s]", strings.Join(elements, ", ")) -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *ImmutableArray) BinaryOp(op token.Token, rhs Object) (Object, error) { - if rhs, ok := rhs.(*ImmutableArray); ok { - switch op { - case token.Add: - return &Array{Value: append(o.Value, rhs.Value...)}, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *ImmutableArray) Copy() Object { - var c []Object - for _, elem := range o.Value { - c = append(c, elem.Copy()) - } - - return &Array{Value: c} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *ImmutableArray) IsFalsy() bool { - return len(o.Value) == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *ImmutableArray) Equals(x Object) bool { - var xVal []Object - switch x := x.(type) { - case *Array: - xVal = x.Value - case *ImmutableArray: - xVal = x.Value - default: - return false - } - - if len(o.Value) != len(xVal) { - return false - } - - for i, e := range o.Value { - if !e.Equals(xVal[i]) { - return false - } - } - - return true -} - -// IndexGet returns an element at a given index. -func (o *ImmutableArray) IndexGet(index Object) (res Object, err error) { - intIdx, ok := index.(*Int) - if !ok { - err = ErrInvalidIndexType - return - } - - idxVal := int(intIdx.Value) - - if idxVal < 0 || idxVal >= len(o.Value) { - res = UndefinedValue - return - } - - res = o.Value[idxVal] - - return -} - -// Iterate creates an array iterator. -func (o *ImmutableArray) Iterate() Iterator { - return &ArrayIterator{ - v: o.Value, - l: len(o.Value), - } -} diff --git a/vendor/github.com/d5/tengo/objects/immutable_map.go b/vendor/github.com/d5/tengo/objects/immutable_map.go deleted file mode 100644 index 8f58701b..00000000 --- a/vendor/github.com/d5/tengo/objects/immutable_map.go +++ /dev/null @@ -1,105 +0,0 @@ -package objects - -import ( - "fmt" - "strings" - - "github.com/d5/tengo/compiler/token" -) - -// ImmutableMap represents an immutable map object. -type ImmutableMap struct { - Value map[string]Object -} - -// TypeName returns the name of the type. -func (o *ImmutableMap) TypeName() string { - return "immutable-map" -} - -func (o *ImmutableMap) String() string { - var pairs []string - for k, v := range o.Value { - pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String())) - } - - return fmt.Sprintf("{%s}", strings.Join(pairs, ", ")) -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *ImmutableMap) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *ImmutableMap) Copy() Object { - c := make(map[string]Object) - for k, v := range o.Value { - c[k] = v.Copy() - } - - return &Map{Value: c} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *ImmutableMap) IsFalsy() bool { - return len(o.Value) == 0 -} - -// IndexGet returns the value for the given key. -func (o *ImmutableMap) IndexGet(index Object) (res Object, err error) { - strIdx, ok := ToString(index) - if !ok { - err = ErrInvalidIndexType - return - } - - val, ok := o.Value[strIdx] - if !ok { - val = UndefinedValue - } - - return val, nil -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *ImmutableMap) Equals(x Object) bool { - var xVal map[string]Object - switch x := x.(type) { - case *Map: - xVal = x.Value - case *ImmutableMap: - xVal = x.Value - default: - return false - } - - if len(o.Value) != len(xVal) { - return false - } - - for k, v := range o.Value { - tv := xVal[k] - if !v.Equals(tv) { - return false - } - } - - return true -} - -// Iterate creates an immutable map iterator. -func (o *ImmutableMap) Iterate() Iterator { - var keys []string - for k := range o.Value { - keys = append(keys, k) - } - - return &MapIterator{ - v: o.Value, - k: keys, - l: len(keys), - } -} diff --git a/vendor/github.com/d5/tengo/objects/importable.go b/vendor/github.com/d5/tengo/objects/importable.go deleted file mode 100644 index 9fd86ae8..00000000 --- a/vendor/github.com/d5/tengo/objects/importable.go +++ /dev/null @@ -1,7 +0,0 @@ -package objects - -// Importable interface represents importable module instance. -type Importable interface { - // Import should return either an Object or module source code ([]byte). - Import(moduleName string) (interface{}, error) -} diff --git a/vendor/github.com/d5/tengo/objects/index_assignable.go b/vendor/github.com/d5/tengo/objects/index_assignable.go deleted file mode 100644 index a1c6cbff..00000000 --- a/vendor/github.com/d5/tengo/objects/index_assignable.go +++ /dev/null @@ -1,9 +0,0 @@ -package objects - -// IndexAssignable is an object that can take an index and a value -// on the left-hand side of the assignment statement. -type IndexAssignable interface { - // IndexSet should take an index Object and a value Object. - // If an error is returned, it will be treated as a run-time error. - IndexSet(index, value Object) error -} diff --git a/vendor/github.com/d5/tengo/objects/indexable.go b/vendor/github.com/d5/tengo/objects/indexable.go deleted file mode 100644 index bbc81633..00000000 --- a/vendor/github.com/d5/tengo/objects/indexable.go +++ /dev/null @@ -1,9 +0,0 @@ -package objects - -// Indexable is an object that can take an index and return an object. -type Indexable interface { - // IndexGet should take an index Object and return a result Object or an error. - // If error is returned, the runtime will treat it as a run-time error and ignore returned value. - // If nil is returned as value, it will be converted to Undefined value by the runtime. - IndexGet(index Object) (value Object, err error) -} diff --git a/vendor/github.com/d5/tengo/objects/int.go b/vendor/github.com/d5/tengo/objects/int.go deleted file mode 100644 index e902c93a..00000000 --- a/vendor/github.com/d5/tengo/objects/int.go +++ /dev/null @@ -1,198 +0,0 @@ -package objects - -import ( - "strconv" - - "github.com/d5/tengo/compiler/token" -) - -// Int represents an integer value. -type Int struct { - Value int64 -} - -func (o *Int) String() string { - return strconv.FormatInt(o.Value, 10) -} - -// TypeName returns the name of the type. -func (o *Int) TypeName() string { - return "int" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch rhs := rhs.(type) { - case *Int: - switch op { - case token.Add: - r := o.Value + rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Sub: - r := o.Value - rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Mul: - r := o.Value * rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Quo: - r := o.Value / rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Rem: - r := o.Value % rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.And: - r := o.Value & rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Or: - r := o.Value | rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Xor: - r := o.Value ^ rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.AndNot: - r := o.Value &^ rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Shl: - r := o.Value << uint64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Shr: - r := o.Value >> uint64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Less: - if o.Value < rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value > rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value <= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value >= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - } - case *Float: - switch op { - case token.Add: - return &Float{float64(o.Value) + rhs.Value}, nil - case token.Sub: - return &Float{float64(o.Value) - rhs.Value}, nil - case token.Mul: - return &Float{float64(o.Value) * rhs.Value}, nil - case token.Quo: - return &Float{float64(o.Value) / rhs.Value}, nil - case token.Less: - if float64(o.Value) < rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if float64(o.Value) > rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if float64(o.Value) <= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if float64(o.Value) >= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - } - case *Char: - switch op { - case token.Add: - return &Char{rune(o.Value) + rhs.Value}, nil - case token.Sub: - return &Char{rune(o.Value) - rhs.Value}, nil - case token.Less: - if o.Value < int64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value > int64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value <= int64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value >= int64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Int) Copy() Object { - return &Int{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Int) IsFalsy() bool { - return o.Value == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Int) Equals(x Object) bool { - t, ok := x.(*Int) - if !ok { - return false - } - - return o.Value == t.Value -} diff --git a/vendor/github.com/d5/tengo/objects/iterable.go b/vendor/github.com/d5/tengo/objects/iterable.go deleted file mode 100644 index e431d3d7..00000000 --- a/vendor/github.com/d5/tengo/objects/iterable.go +++ /dev/null @@ -1,7 +0,0 @@ -package objects - -// Iterable represents an object that has iterator. -type Iterable interface { - // Iterate should return an Iterator for the type. - Iterate() Iterator -} diff --git a/vendor/github.com/d5/tengo/objects/iterator.go b/vendor/github.com/d5/tengo/objects/iterator.go deleted file mode 100644 index 01522ba5..00000000 --- a/vendor/github.com/d5/tengo/objects/iterator.go +++ /dev/null @@ -1,15 +0,0 @@ -package objects - -// Iterator represents an iterator for underlying data type. -type Iterator interface { - Object - - // Next returns true if there are more elements to iterate. - Next() bool - - // Key returns the key or index value of the current element. - Key() Object - - // Value returns the value of the current element. - Value() Object -} diff --git a/vendor/github.com/d5/tengo/objects/map.go b/vendor/github.com/d5/tengo/objects/map.go deleted file mode 100644 index 9208872c..00000000 --- a/vendor/github.com/d5/tengo/objects/map.go +++ /dev/null @@ -1,118 +0,0 @@ -package objects - -import ( - "fmt" - "strings" - - "github.com/d5/tengo/compiler/token" -) - -// Map represents a map of objects. -type Map struct { - Value map[string]Object -} - -// TypeName returns the name of the type. -func (o *Map) TypeName() string { - return "map" -} - -func (o *Map) String() string { - var pairs []string - for k, v := range o.Value { - pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String())) - } - - return fmt.Sprintf("{%s}", strings.Join(pairs, ", ")) -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Map) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Map) Copy() Object { - c := make(map[string]Object) - for k, v := range o.Value { - c[k] = v.Copy() - } - - return &Map{Value: c} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Map) IsFalsy() bool { - return len(o.Value) == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Map) Equals(x Object) bool { - var xVal map[string]Object - switch x := x.(type) { - case *Map: - xVal = x.Value - case *ImmutableMap: - xVal = x.Value - default: - return false - } - - if len(o.Value) != len(xVal) { - return false - } - - for k, v := range o.Value { - tv := xVal[k] - if !v.Equals(tv) { - return false - } - } - - return true -} - -// IndexGet returns the value for the given key. -func (o *Map) IndexGet(index Object) (res Object, err error) { - strIdx, ok := ToString(index) - if !ok { - err = ErrInvalidIndexType - return - } - - val, ok := o.Value[strIdx] - if !ok { - val = UndefinedValue - } - - return val, nil -} - -// IndexSet sets the value for the given key. -func (o *Map) IndexSet(index, value Object) (err error) { - strIdx, ok := ToString(index) - if !ok { - err = ErrInvalidIndexType - return - } - - o.Value[strIdx] = value - - return nil -} - -// Iterate creates a map iterator. -func (o *Map) Iterate() Iterator { - var keys []string - for k := range o.Value { - keys = append(keys, k) - } - - return &MapIterator{ - v: o.Value, - k: keys, - l: len(keys), - } -} diff --git a/vendor/github.com/d5/tengo/objects/map_iterator.go b/vendor/github.com/d5/tengo/objects/map_iterator.go deleted file mode 100644 index d60dd0e1..00000000 --- a/vendor/github.com/d5/tengo/objects/map_iterator.go +++ /dev/null @@ -1,62 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// MapIterator represents an iterator for the map. -type MapIterator struct { - v map[string]Object - k []string - i int - l int -} - -// TypeName returns the name of the type. -func (i *MapIterator) TypeName() string { - return "map-iterator" -} - -func (i *MapIterator) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (i *MapIterator) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (i *MapIterator) IsFalsy() bool { - return true -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (i *MapIterator) Equals(Object) bool { - return false -} - -// Copy returns a copy of the type. -func (i *MapIterator) Copy() Object { - return &MapIterator{v: i.v, k: i.k, i: i.i, l: i.l} -} - -// Next returns true if there are more elements to iterate. -func (i *MapIterator) Next() bool { - i.i++ - return i.i <= i.l -} - -// Key returns the key or index value of the current element. -func (i *MapIterator) Key() Object { - k := i.k[i.i-1] - - return &String{Value: k} -} - -// Value returns the value of the current element. -func (i *MapIterator) Value() Object { - k := i.k[i.i-1] - - return i.v[k] -} diff --git a/vendor/github.com/d5/tengo/objects/module_map.go b/vendor/github.com/d5/tengo/objects/module_map.go deleted file mode 100644 index 874b8a2b..00000000 --- a/vendor/github.com/d5/tengo/objects/module_map.go +++ /dev/null @@ -1,77 +0,0 @@ -package objects - -// ModuleMap represents a set of named modules. -// Use NewModuleMap to create a new module map. -type ModuleMap struct { - m map[string]Importable -} - -// NewModuleMap creates a new module map. -func NewModuleMap() *ModuleMap { - return &ModuleMap{ - m: make(map[string]Importable), - } -} - -// Add adds an import module. -func (m *ModuleMap) Add(name string, module Importable) { - m.m[name] = module -} - -// AddBuiltinModule adds a builtin module. -func (m *ModuleMap) AddBuiltinModule(name string, attrs map[string]Object) { - m.m[name] = &BuiltinModule{Attrs: attrs} -} - -// AddSourceModule adds a source module. -func (m *ModuleMap) AddSourceModule(name string, src []byte) { - m.m[name] = &SourceModule{Src: src} -} - -// Remove removes a named module. -func (m *ModuleMap) Remove(name string) { - delete(m.m, name) -} - -// Get returns an import module identified by name. -// It returns if the name is not found. -func (m *ModuleMap) Get(name string) Importable { - return m.m[name] -} - -// GetBuiltinModule returns a builtin module identified by name. -// It returns if the name is not found or the module is not a builtin module. -func (m *ModuleMap) GetBuiltinModule(name string) *BuiltinModule { - mod, _ := m.m[name].(*BuiltinModule) - return mod -} - -// GetSourceModule returns a source module identified by name. -// It returns if the name is not found or the module is not a source module. -func (m *ModuleMap) GetSourceModule(name string) *SourceModule { - mod, _ := m.m[name].(*SourceModule) - return mod -} - -// Copy creates a copy of the module map. -func (m *ModuleMap) Copy() *ModuleMap { - c := &ModuleMap{ - m: make(map[string]Importable), - } - for name, mod := range m.m { - c.m[name] = mod - } - return c -} - -// Len returns the number of named modules. -func (m *ModuleMap) Len() int { - return len(m.m) -} - -// AddMap adds named modules from another module map. -func (m *ModuleMap) AddMap(o *ModuleMap) { - for name, mod := range o.m { - m.m[name] = mod - } -} diff --git a/vendor/github.com/d5/tengo/objects/object.go b/vendor/github.com/d5/tengo/objects/object.go deleted file mode 100644 index 4c5aa7ae..00000000 --- a/vendor/github.com/d5/tengo/objects/object.go +++ /dev/null @@ -1,30 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// Object represents an object in the VM. -type Object interface { - // TypeName should return the name of the type. - TypeName() string - - // String should return a string representation of the type's value. - String() string - - // BinaryOp should return another object that is the result of - // a given binary operator and a right-hand side object. - // If BinaryOp returns an error, the VM will treat it as a run-time error. - BinaryOp(op token.Token, rhs Object) (Object, error) - - // IsFalsy should return true if the value of the type - // should be considered as falsy. - IsFalsy() bool - - // Equals should return true if the value of the type - // should be considered as equal to the value of another object. - Equals(another Object) bool - - // Copy should return a copy of the type (and its value). - // Copy function will be used for copy() builtin function - // which is expected to deep-copy the values generally. - Copy() Object -} diff --git a/vendor/github.com/d5/tengo/objects/object_ptr.go b/vendor/github.com/d5/tengo/objects/object_ptr.go deleted file mode 100644 index 2c87c561..00000000 --- a/vendor/github.com/d5/tengo/objects/object_ptr.go +++ /dev/null @@ -1,41 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// ObjectPtr represents a free variable. -type ObjectPtr struct { - Value *Object -} - -func (o *ObjectPtr) String() string { - return "free-var" -} - -// TypeName returns the name of the type. -func (o *ObjectPtr) TypeName() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *ObjectPtr) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *ObjectPtr) Copy() Object { - return o -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *ObjectPtr) IsFalsy() bool { - return o.Value == nil -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *ObjectPtr) Equals(x Object) bool { - return o == x -} diff --git a/vendor/github.com/d5/tengo/objects/objects.go b/vendor/github.com/d5/tengo/objects/objects.go deleted file mode 100644 index f3878b11..00000000 --- a/vendor/github.com/d5/tengo/objects/objects.go +++ /dev/null @@ -1,12 +0,0 @@ -package objects - -var ( - // TrueValue represents a true value. - TrueValue Object = &Bool{value: true} - - // FalseValue represents a false value. - FalseValue Object = &Bool{value: false} - - // UndefinedValue represents an undefined value. - UndefinedValue Object = &Undefined{} -) diff --git a/vendor/github.com/d5/tengo/objects/source_module.go b/vendor/github.com/d5/tengo/objects/source_module.go deleted file mode 100644 index 577fddf2..00000000 --- a/vendor/github.com/d5/tengo/objects/source_module.go +++ /dev/null @@ -1,11 +0,0 @@ -package objects - -// SourceModule is an importable module that's written in Tengo. -type SourceModule struct { - Src []byte -} - -// Import returns a module source code. -func (m *SourceModule) Import(_ string) (interface{}, error) { - return m.Src, nil -} diff --git a/vendor/github.com/d5/tengo/objects/string.go b/vendor/github.com/d5/tengo/objects/string.go deleted file mode 100644 index c25b0502..00000000 --- a/vendor/github.com/d5/tengo/objects/string.go +++ /dev/null @@ -1,103 +0,0 @@ -package objects - -import ( - "strconv" - - "github.com/d5/tengo" - "github.com/d5/tengo/compiler/token" -) - -// String represents a string value. -type String struct { - Value string - runeStr []rune -} - -// TypeName returns the name of the type. -func (o *String) TypeName() string { - return "string" -} - -func (o *String) String() string { - return strconv.Quote(o.Value) -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch op { - case token.Add: - switch rhs := rhs.(type) { - case *String: - if len(o.Value)+len(rhs.Value) > tengo.MaxStringLen { - return nil, ErrStringLimit - } - return &String{Value: o.Value + rhs.Value}, nil - default: - rhsStr := rhs.String() - if len(o.Value)+len(rhsStr) > tengo.MaxStringLen { - return nil, ErrStringLimit - } - return &String{Value: o.Value + rhsStr}, nil - } - } - - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *String) IsFalsy() bool { - return len(o.Value) == 0 -} - -// Copy returns a copy of the type. -func (o *String) Copy() Object { - return &String{Value: o.Value} -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *String) Equals(x Object) bool { - t, ok := x.(*String) - if !ok { - return false - } - - return o.Value == t.Value -} - -// IndexGet returns a character at a given index. -func (o *String) IndexGet(index Object) (res Object, err error) { - intIdx, ok := index.(*Int) - if !ok { - err = ErrInvalidIndexType - return - } - - idxVal := int(intIdx.Value) - - if o.runeStr == nil { - o.runeStr = []rune(o.Value) - } - - if idxVal < 0 || idxVal >= len(o.runeStr) { - res = UndefinedValue - return - } - - res = &Char{Value: o.runeStr[idxVal]} - - return -} - -// Iterate creates a string iterator. -func (o *String) Iterate() Iterator { - if o.runeStr == nil { - o.runeStr = []rune(o.Value) - } - - return &StringIterator{ - v: o.runeStr, - l: len(o.runeStr), - } -} diff --git a/vendor/github.com/d5/tengo/objects/string_iterator.go b/vendor/github.com/d5/tengo/objects/string_iterator.go deleted file mode 100644 index 8bc95eb5..00000000 --- a/vendor/github.com/d5/tengo/objects/string_iterator.go +++ /dev/null @@ -1,57 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// StringIterator represents an iterator for a string. -type StringIterator struct { - v []rune - i int - l int -} - -// TypeName returns the name of the type. -func (i *StringIterator) TypeName() string { - return "string-iterator" -} - -func (i *StringIterator) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (i *StringIterator) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (i *StringIterator) IsFalsy() bool { - return true -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (i *StringIterator) Equals(Object) bool { - return false -} - -// Copy returns a copy of the type. -func (i *StringIterator) Copy() Object { - return &StringIterator{v: i.v, i: i.i, l: i.l} -} - -// Next returns true if there are more elements to iterate. -func (i *StringIterator) Next() bool { - i.i++ - return i.i <= i.l -} - -// Key returns the key or index value of the current element. -func (i *StringIterator) Key() Object { - return &Int{Value: int64(i.i - 1)} -} - -// Value returns the value of the current element. -func (i *StringIterator) Value() Object { - return &Char{Value: i.v[i.i-1]} -} diff --git a/vendor/github.com/d5/tengo/objects/time.go b/vendor/github.com/d5/tengo/objects/time.go deleted file mode 100644 index 4e783cc8..00000000 --- a/vendor/github.com/d5/tengo/objects/time.go +++ /dev/null @@ -1,89 +0,0 @@ -package objects - -import ( - "time" - - "github.com/d5/tengo/compiler/token" -) - -// Time represents a time value. -type Time struct { - Value time.Time -} - -func (o *Time) String() string { - return o.Value.String() -} - -// TypeName returns the name of the type. -func (o *Time) TypeName() string { - return "time" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Time) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch rhs := rhs.(type) { - case *Int: - switch op { - case token.Add: // time + int => time - if rhs.Value == 0 { - return o, nil - } - return &Time{Value: o.Value.Add(time.Duration(rhs.Value))}, nil - case token.Sub: // time - int => time - if rhs.Value == 0 { - return o, nil - } - return &Time{Value: o.Value.Add(time.Duration(-rhs.Value))}, nil - } - case *Time: - switch op { - case token.Sub: // time - time => int (duration) - return &Int{Value: int64(o.Value.Sub(rhs.Value))}, nil - case token.Less: // time < time => bool - if o.Value.Before(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value.After(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value.Equal(rhs.Value) || o.Value.Before(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value.Equal(rhs.Value) || o.Value.After(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Time) Copy() Object { - return &Time{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Time) IsFalsy() bool { - return o.Value.IsZero() -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Time) Equals(x Object) bool { - t, ok := x.(*Time) - if !ok { - return false - } - - return o.Value.Equal(t.Value) -} diff --git a/vendor/github.com/d5/tengo/objects/undefined.go b/vendor/github.com/d5/tengo/objects/undefined.go deleted file mode 100644 index 0fdbc084..00000000 --- a/vendor/github.com/d5/tengo/objects/undefined.go +++ /dev/null @@ -1,62 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// Undefined represents an undefined value. -type Undefined struct{} - -// TypeName returns the name of the type. -func (o *Undefined) TypeName() string { - return "undefined" -} - -func (o *Undefined) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Undefined) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Undefined) Copy() Object { - return o -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Undefined) IsFalsy() bool { - return true -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Undefined) Equals(x Object) bool { - return o == x -} - -// IndexGet returns an element at a given index. -func (o *Undefined) IndexGet(index Object) (Object, error) { - return UndefinedValue, nil -} - -// Iterate creates a map iterator. -func (o *Undefined) Iterate() Iterator { - return o -} - -// Next returns true if there are more elements to iterate. -func (o *Undefined) Next() bool { - return false -} - -// Key returns the key or index value of the current element. -func (o *Undefined) Key() Object { - return o -} - -// Value returns the value of the current element. -func (o *Undefined) Value() Object { - return o -} diff --git a/vendor/github.com/d5/tengo/objects/user_function.go b/vendor/github.com/d5/tengo/objects/user_function.go deleted file mode 100644 index a896788b..00000000 --- a/vendor/github.com/d5/tengo/objects/user_function.go +++ /dev/null @@ -1,48 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// UserFunction represents a user function. -type UserFunction struct { - Name string - Value CallableFunc - EncodingID string -} - -// TypeName returns the name of the type. -func (o *UserFunction) TypeName() string { - return "user-function:" + o.Name -} - -func (o *UserFunction) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *UserFunction) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *UserFunction) Copy() Object { - return &UserFunction{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *UserFunction) IsFalsy() bool { - return false -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *UserFunction) Equals(x Object) bool { - return false -} - -// Call invokes a user function. -func (o *UserFunction) Call(args ...Object) (Object, error) { - return o.Value(args...) -} diff --git a/vendor/github.com/d5/tengo/runtime/errors.go b/vendor/github.com/d5/tengo/runtime/errors.go deleted file mode 100644 index fc7ca0e9..00000000 --- a/vendor/github.com/d5/tengo/runtime/errors.go +++ /dev/null @@ -1,11 +0,0 @@ -package runtime - -import ( - "errors" -) - -// ErrStackOverflow is a stack overflow error. -var ErrStackOverflow = errors.New("stack overflow") - -// ErrObjectAllocLimit is an objects allocation limit error. -var ErrObjectAllocLimit = errors.New("object allocation limit exceeded") diff --git a/vendor/github.com/d5/tengo/runtime/frame.go b/vendor/github.com/d5/tengo/runtime/frame.go deleted file mode 100644 index cbaadbdb..00000000 --- a/vendor/github.com/d5/tengo/runtime/frame.go +++ /dev/null @@ -1,13 +0,0 @@ -package runtime - -import ( - "github.com/d5/tengo/objects" -) - -// Frame represents a function call frame. -type Frame struct { - fn *objects.CompiledFunction - freeVars []*objects.ObjectPtr - ip int - basePointer int -} diff --git a/vendor/github.com/d5/tengo/runtime/vm.go b/vendor/github.com/d5/tengo/runtime/vm.go deleted file mode 100644 index 07f6e530..00000000 --- a/vendor/github.com/d5/tengo/runtime/vm.go +++ /dev/null @@ -1,1092 +0,0 @@ -package runtime - -import ( - "fmt" - "sync/atomic" - - "github.com/d5/tengo/compiler" - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" - "github.com/d5/tengo/objects" -) - -const ( - // StackSize is the maximum stack size. - StackSize = 2048 - - // GlobalsSize is the maximum number of global variables. - GlobalsSize = 1024 - - // MaxFrames is the maximum number of function frames. - MaxFrames = 1024 -) - -// VM is a virtual machine that executes the bytecode compiled by Compiler. -type VM struct { - constants []objects.Object - stack [StackSize]objects.Object - sp int - globals []objects.Object - fileSet *source.FileSet - frames [MaxFrames]Frame - framesIndex int - curFrame *Frame - curInsts []byte - ip int - aborting int64 - maxAllocs int64 - allocs int64 - err error -} - -// NewVM creates a VM. -func NewVM(bytecode *compiler.Bytecode, globals []objects.Object, maxAllocs int64) *VM { - if globals == nil { - globals = make([]objects.Object, GlobalsSize) - } - - v := &VM{ - constants: bytecode.Constants, - sp: 0, - globals: globals, - fileSet: bytecode.FileSet, - framesIndex: 1, - ip: -1, - maxAllocs: maxAllocs, - } - - v.frames[0].fn = bytecode.MainFunction - v.frames[0].ip = -1 - v.curFrame = &v.frames[0] - v.curInsts = v.curFrame.fn.Instructions - - return v -} - -// Abort aborts the execution. -func (v *VM) Abort() { - atomic.StoreInt64(&v.aborting, 1) -} - -// Run starts the execution. -func (v *VM) Run() (err error) { - // reset VM states - v.sp = 0 - v.curFrame = &(v.frames[0]) - v.curInsts = v.curFrame.fn.Instructions - v.framesIndex = 1 - v.ip = -1 - v.allocs = v.maxAllocs + 1 - - v.run() - - atomic.StoreInt64(&v.aborting, 0) - - err = v.err - if err != nil { - filePos := v.fileSet.Position(v.curFrame.fn.SourcePos(v.ip - 1)) - err = fmt.Errorf("Runtime Error: %s\n\tat %s", err.Error(), filePos) - for v.framesIndex > 1 { - v.framesIndex-- - v.curFrame = &v.frames[v.framesIndex-1] - - filePos = v.fileSet.Position(v.curFrame.fn.SourcePos(v.curFrame.ip - 1)) - err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos) - } - return err - } - - return nil -} - -func (v *VM) run() { - defer func() { - if r := recover(); r != nil { - if v.sp >= StackSize || v.framesIndex >= MaxFrames { - v.err = ErrStackOverflow - return - } - - if v.ip < len(v.curInsts)-1 { - if err, ok := r.(error); ok { - v.err = err - } else { - v.err = fmt.Errorf("panic: %v", r) - } - } - } - }() - - for atomic.LoadInt64(&v.aborting) == 0 { - v.ip++ - - switch v.curInsts[v.ip] { - case compiler.OpConstant: - v.ip += 2 - cidx := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - - v.stack[v.sp] = v.constants[cidx] - v.sp++ - - case compiler.OpNull: - v.stack[v.sp] = objects.UndefinedValue - v.sp++ - - case compiler.OpBinaryOp: - v.ip++ - right := v.stack[v.sp-1] - left := v.stack[v.sp-2] - - tok := token.Token(v.curInsts[v.ip]) - res, e := left.BinaryOp(tok, right) - if e != nil { - v.sp -= 2 - - if e == objects.ErrInvalidOperator { - v.err = fmt.Errorf("invalid operation: %s %s %s", - left.TypeName(), tok.String(), right.TypeName()) - return - } - - v.err = e - return - } - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp-2] = res - v.sp-- - - case compiler.OpEqual: - right := v.stack[v.sp-1] - left := v.stack[v.sp-2] - v.sp -= 2 - - if left.Equals(right) { - v.stack[v.sp] = objects.TrueValue - } else { - v.stack[v.sp] = objects.FalseValue - } - v.sp++ - - case compiler.OpNotEqual: - right := v.stack[v.sp-1] - left := v.stack[v.sp-2] - v.sp -= 2 - - if left.Equals(right) { - v.stack[v.sp] = objects.FalseValue - } else { - v.stack[v.sp] = objects.TrueValue - } - v.sp++ - - case compiler.OpPop: - v.sp-- - - case compiler.OpTrue: - v.stack[v.sp] = objects.TrueValue - v.sp++ - - case compiler.OpFalse: - v.stack[v.sp] = objects.FalseValue - v.sp++ - - case compiler.OpLNot: - operand := v.stack[v.sp-1] - v.sp-- - - if operand.IsFalsy() { - v.stack[v.sp] = objects.TrueValue - } else { - v.stack[v.sp] = objects.FalseValue - } - v.sp++ - - case compiler.OpBComplement: - operand := v.stack[v.sp-1] - v.sp-- - - switch x := operand.(type) { - case *objects.Int: - var res objects.Object = &objects.Int{Value: ^x.Value} - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = res - v.sp++ - default: - v.err = fmt.Errorf("invalid operation: ^%s", operand.TypeName()) - return - } - - case compiler.OpMinus: - operand := v.stack[v.sp-1] - v.sp-- - - switch x := operand.(type) { - case *objects.Int: - var res objects.Object = &objects.Int{Value: -x.Value} - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = res - v.sp++ - case *objects.Float: - var res objects.Object = &objects.Float{Value: -x.Value} - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = res - v.sp++ - default: - v.err = fmt.Errorf("invalid operation: -%s", operand.TypeName()) - return - } - - case compiler.OpJumpFalsy: - v.ip += 2 - v.sp-- - if v.stack[v.sp].IsFalsy() { - pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - v.ip = pos - 1 - } - - case compiler.OpAndJump: - v.ip += 2 - - if v.stack[v.sp-1].IsFalsy() { - pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - v.ip = pos - 1 - } else { - v.sp-- - } - - case compiler.OpOrJump: - v.ip += 2 - - if v.stack[v.sp-1].IsFalsy() { - v.sp-- - } else { - pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - v.ip = pos - 1 - } - - case compiler.OpJump: - pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 - v.ip = pos - 1 - - case compiler.OpSetGlobal: - v.ip += 2 - v.sp-- - - globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - v.globals[globalIndex] = v.stack[v.sp] - - case compiler.OpSetSelGlobal: - v.ip += 3 - globalIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8 - numSelectors := int(v.curInsts[v.ip]) - - // selectors and RHS value - selectors := make([]objects.Object, numSelectors) - for i := 0; i < numSelectors; i++ { - selectors[i] = v.stack[v.sp-numSelectors+i] - } - - val := v.stack[v.sp-numSelectors-1] - v.sp -= numSelectors + 1 - - if e := indexAssign(v.globals[globalIndex], val, selectors); e != nil { - v.err = e - return - } - - case compiler.OpGetGlobal: - v.ip += 2 - globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - - val := v.globals[globalIndex] - - v.stack[v.sp] = val - v.sp++ - - case compiler.OpArray: - v.ip += 2 - numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - - var elements []objects.Object - for i := v.sp - numElements; i < v.sp; i++ { - elements = append(elements, v.stack[i]) - } - v.sp -= numElements - - var arr objects.Object = &objects.Array{Value: elements} - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = arr - v.sp++ - - case compiler.OpMap: - v.ip += 2 - numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - - kv := make(map[string]objects.Object) - for i := v.sp - numElements; i < v.sp; i += 2 { - key := v.stack[i] - value := v.stack[i+1] - kv[key.(*objects.String).Value] = value - } - v.sp -= numElements - - var m objects.Object = &objects.Map{Value: kv} - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = m - v.sp++ - - case compiler.OpError: - value := v.stack[v.sp-1] - - var e objects.Object = &objects.Error{ - Value: value, - } - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp-1] = e - - case compiler.OpImmutable: - value := v.stack[v.sp-1] - - switch value := value.(type) { - case *objects.Array: - var immutableArray objects.Object = &objects.ImmutableArray{ - Value: value.Value, - } - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp-1] = immutableArray - case *objects.Map: - var immutableMap objects.Object = &objects.ImmutableMap{ - Value: value.Value, - } - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp-1] = immutableMap - } - - case compiler.OpIndex: - index := v.stack[v.sp-1] - left := v.stack[v.sp-2] - v.sp -= 2 - - switch left := left.(type) { - case objects.Indexable: - val, e := left.IndexGet(index) - if e != nil { - - if e == objects.ErrInvalidIndexType { - v.err = fmt.Errorf("invalid index type: %s", index.TypeName()) - return - } - - v.err = e - return - } - if val == nil { - val = objects.UndefinedValue - } - - v.stack[v.sp] = val - v.sp++ - - case *objects.Error: // e.value - key, ok := index.(*objects.String) - if !ok || key.Value != "value" { - v.err = fmt.Errorf("invalid index on error") - return - } - - v.stack[v.sp] = left.Value - v.sp++ - - default: - v.err = fmt.Errorf("not indexable: %s", left.TypeName()) - return - } - - case compiler.OpSliceIndex: - high := v.stack[v.sp-1] - low := v.stack[v.sp-2] - left := v.stack[v.sp-3] - v.sp -= 3 - - var lowIdx int64 - if low != objects.UndefinedValue { - if low, ok := low.(*objects.Int); ok { - lowIdx = low.Value - } else { - v.err = fmt.Errorf("invalid slice index type: %s", low.TypeName()) - return - } - } - - switch left := left.(type) { - case *objects.Array: - numElements := int64(len(left.Value)) - var highIdx int64 - if high == objects.UndefinedValue { - highIdx = numElements - } else if high, ok := high.(*objects.Int); ok { - highIdx = high.Value - } else { - v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName()) - return - } - - if lowIdx > highIdx { - v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx) - return - } - - if lowIdx < 0 { - lowIdx = 0 - } else if lowIdx > numElements { - lowIdx = numElements - } - - if highIdx < 0 { - highIdx = 0 - } else if highIdx > numElements { - highIdx = numElements - } - - var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]} - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = val - v.sp++ - - case *objects.ImmutableArray: - numElements := int64(len(left.Value)) - var highIdx int64 - if high == objects.UndefinedValue { - highIdx = numElements - } else if high, ok := high.(*objects.Int); ok { - highIdx = high.Value - } else { - v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName()) - return - } - - if lowIdx > highIdx { - v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx) - return - } - - if lowIdx < 0 { - lowIdx = 0 - } else if lowIdx > numElements { - lowIdx = numElements - } - - if highIdx < 0 { - highIdx = 0 - } else if highIdx > numElements { - highIdx = numElements - } - - var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]} - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = val - v.sp++ - - case *objects.String: - numElements := int64(len(left.Value)) - var highIdx int64 - if high == objects.UndefinedValue { - highIdx = numElements - } else if high, ok := high.(*objects.Int); ok { - highIdx = high.Value - } else { - v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName()) - return - } - - if lowIdx > highIdx { - v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx) - return - } - - if lowIdx < 0 { - lowIdx = 0 - } else if lowIdx > numElements { - lowIdx = numElements - } - - if highIdx < 0 { - highIdx = 0 - } else if highIdx > numElements { - highIdx = numElements - } - - var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]} - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = val - v.sp++ - - case *objects.Bytes: - numElements := int64(len(left.Value)) - var highIdx int64 - if high == objects.UndefinedValue { - highIdx = numElements - } else if high, ok := high.(*objects.Int); ok { - highIdx = high.Value - } else { - v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName()) - return - } - - if lowIdx > highIdx { - v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx) - return - } - - if lowIdx < 0 { - lowIdx = 0 - } else if lowIdx > numElements { - lowIdx = numElements - } - - if highIdx < 0 { - highIdx = 0 - } else if highIdx > numElements { - highIdx = numElements - } - - var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]} - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = val - v.sp++ - } - - case compiler.OpCall: - numArgs := int(v.curInsts[v.ip+1]) - v.ip++ - - value := v.stack[v.sp-1-numArgs] - - switch callee := value.(type) { - case *objects.Closure: - if callee.Fn.VarArgs { - // if the closure is variadic, - // roll up all variadic parameters into an array - realArgs := callee.Fn.NumParameters - 1 - varArgs := numArgs - realArgs - if varArgs >= 0 { - numArgs = realArgs + 1 - args := make([]objects.Object, varArgs) - spStart := v.sp - varArgs - for i := spStart; i < v.sp; i++ { - args[i-spStart] = v.stack[i] - } - v.stack[spStart] = &objects.Array{Value: args} - v.sp = spStart + 1 - } - } - - if numArgs != callee.Fn.NumParameters { - if callee.Fn.VarArgs { - v.err = fmt.Errorf("wrong number of arguments: want>=%d, got=%d", - callee.Fn.NumParameters-1, numArgs) - } else { - v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d", - callee.Fn.NumParameters, numArgs) - } - return - } - - // test if it's tail-call - if callee.Fn == v.curFrame.fn { // recursion - nextOp := v.curInsts[v.ip+1] - if nextOp == compiler.OpReturn || - (nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) { - for p := 0; p < numArgs; p++ { - v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p] - } - v.sp -= numArgs + 1 - v.ip = -1 // reset IP to beginning of the frame - continue - } - } - - // update call frame - v.curFrame.ip = v.ip // store current ip before call - v.curFrame = &(v.frames[v.framesIndex]) - v.curFrame.fn = callee.Fn - v.curFrame.freeVars = callee.Free - v.curFrame.basePointer = v.sp - numArgs - v.curInsts = callee.Fn.Instructions - v.ip = -1 - v.framesIndex++ - v.sp = v.sp - numArgs + callee.Fn.NumLocals - - case *objects.CompiledFunction: - if callee.VarArgs { - // if the closure is variadic, - // roll up all variadic parameters into an array - realArgs := callee.NumParameters - 1 - varArgs := numArgs - realArgs - if varArgs >= 0 { - numArgs = realArgs + 1 - args := make([]objects.Object, varArgs) - spStart := v.sp - varArgs - for i := spStart; i < v.sp; i++ { - args[i-spStart] = v.stack[i] - } - v.stack[spStart] = &objects.Array{Value: args} - v.sp = spStart + 1 - } - } - - if numArgs != callee.NumParameters { - if callee.VarArgs { - v.err = fmt.Errorf("wrong number of arguments: want>=%d, got=%d", - callee.NumParameters-1, numArgs) - } else { - v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d", - callee.NumParameters, numArgs) - } - return - } - - // test if it's tail-call - if callee == v.curFrame.fn { // recursion - nextOp := v.curInsts[v.ip+1] - if nextOp == compiler.OpReturn || - (nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) { - for p := 0; p < numArgs; p++ { - v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p] - } - v.sp -= numArgs + 1 - v.ip = -1 // reset IP to beginning of the frame - continue - } - } - - // update call frame - v.curFrame.ip = v.ip // store current ip before call - v.curFrame = &(v.frames[v.framesIndex]) - v.curFrame.fn = callee - v.curFrame.freeVars = nil - v.curFrame.basePointer = v.sp - numArgs - v.curInsts = callee.Instructions - v.ip = -1 - v.framesIndex++ - v.sp = v.sp - numArgs + callee.NumLocals - - case objects.Callable: - var args []objects.Object - args = append(args, v.stack[v.sp-numArgs:v.sp]...) - - ret, e := callee.Call(args...) - v.sp -= numArgs + 1 - - // runtime error - if e != nil { - if e == objects.ErrWrongNumArguments { - v.err = fmt.Errorf("wrong number of arguments in call to '%s'", - value.TypeName()) - return - } - - if e, ok := e.(objects.ErrInvalidArgumentType); ok { - v.err = fmt.Errorf("invalid type for argument '%s' in call to '%s': expected %s, found %s", - e.Name, value.TypeName(), e.Expected, e.Found) - return - } - - v.err = e - return - } - - // nil return -> undefined - if ret == nil { - ret = objects.UndefinedValue - } - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = ret - v.sp++ - - default: - v.err = fmt.Errorf("not callable: %s", callee.TypeName()) - return - } - - case compiler.OpReturn: - v.ip++ - var retVal objects.Object - if int(v.curInsts[v.ip]) == 1 { - retVal = v.stack[v.sp-1] - } else { - retVal = objects.UndefinedValue - } - //v.sp-- - - v.framesIndex-- - v.curFrame = &v.frames[v.framesIndex-1] - v.curInsts = v.curFrame.fn.Instructions - v.ip = v.curFrame.ip - - //v.sp = lastFrame.basePointer - 1 - v.sp = v.frames[v.framesIndex].basePointer - - // skip stack overflow check because (newSP) <= (oldSP) - v.stack[v.sp-1] = retVal - //v.sp++ - - case compiler.OpDefineLocal: - v.ip++ - localIndex := int(v.curInsts[v.ip]) - - sp := v.curFrame.basePointer + localIndex - - // local variables can be mutated by other actions - // so always store the copy of popped value - val := v.stack[v.sp-1] - v.sp-- - - v.stack[sp] = val - - case compiler.OpSetLocal: - localIndex := int(v.curInsts[v.ip+1]) - v.ip++ - - sp := v.curFrame.basePointer + localIndex - - // update pointee of v.stack[sp] instead of replacing the pointer itself. - // this is needed because there can be free variables referencing the same local variables. - val := v.stack[v.sp-1] - v.sp-- - - if obj, ok := v.stack[sp].(*objects.ObjectPtr); ok { - *obj.Value = val - val = obj - } - v.stack[sp] = val // also use a copy of popped value - - case compiler.OpSetSelLocal: - localIndex := int(v.curInsts[v.ip+1]) - numSelectors := int(v.curInsts[v.ip+2]) - v.ip += 2 - - // selectors and RHS value - selectors := make([]objects.Object, numSelectors) - for i := 0; i < numSelectors; i++ { - selectors[i] = v.stack[v.sp-numSelectors+i] - } - - val := v.stack[v.sp-numSelectors-1] - v.sp -= numSelectors + 1 - - dst := v.stack[v.curFrame.basePointer+localIndex] - if obj, ok := dst.(*objects.ObjectPtr); ok { - dst = *obj.Value - } - - if e := indexAssign(dst, val, selectors); e != nil { - v.err = e - return - } - - case compiler.OpGetLocal: - v.ip++ - localIndex := int(v.curInsts[v.ip]) - - val := v.stack[v.curFrame.basePointer+localIndex] - - if obj, ok := val.(*objects.ObjectPtr); ok { - val = *obj.Value - } - - v.stack[v.sp] = val - v.sp++ - - case compiler.OpGetBuiltin: - v.ip++ - builtinIndex := int(v.curInsts[v.ip]) - - v.stack[v.sp] = objects.Builtins[builtinIndex] - v.sp++ - - case compiler.OpClosure: - v.ip += 3 - constIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8 - numFree := int(v.curInsts[v.ip]) - - fn, ok := v.constants[constIndex].(*objects.CompiledFunction) - if !ok { - v.err = fmt.Errorf("not function: %s", fn.TypeName()) - return - } - - free := make([]*objects.ObjectPtr, numFree) - for i := 0; i < numFree; i++ { - switch freeVar := (v.stack[v.sp-numFree+i]).(type) { - case *objects.ObjectPtr: - free[i] = freeVar - default: - free[i] = &objects.ObjectPtr{Value: &v.stack[v.sp-numFree+i]} - } - } - - v.sp -= numFree - - var cl = &objects.Closure{ - Fn: fn, - Free: free, - } - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = cl - v.sp++ - - case compiler.OpGetFreePtr: - v.ip++ - freeIndex := int(v.curInsts[v.ip]) - - val := v.curFrame.freeVars[freeIndex] - - v.stack[v.sp] = val - v.sp++ - - case compiler.OpGetFree: - v.ip++ - freeIndex := int(v.curInsts[v.ip]) - - val := *v.curFrame.freeVars[freeIndex].Value - - v.stack[v.sp] = val - v.sp++ - - case compiler.OpSetFree: - v.ip++ - freeIndex := int(v.curInsts[v.ip]) - - *v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1] - - v.sp-- - - case compiler.OpGetLocalPtr: - v.ip++ - localIndex := int(v.curInsts[v.ip]) - - sp := v.curFrame.basePointer + localIndex - val := v.stack[sp] - - var freeVar *objects.ObjectPtr - if obj, ok := val.(*objects.ObjectPtr); ok { - freeVar = obj - } else { - freeVar = &objects.ObjectPtr{Value: &val} - v.stack[sp] = freeVar - } - - v.stack[v.sp] = freeVar - v.sp++ - - case compiler.OpSetSelFree: - v.ip += 2 - freeIndex := int(v.curInsts[v.ip-1]) - numSelectors := int(v.curInsts[v.ip]) - - // selectors and RHS value - selectors := make([]objects.Object, numSelectors) - for i := 0; i < numSelectors; i++ { - selectors[i] = v.stack[v.sp-numSelectors+i] - } - val := v.stack[v.sp-numSelectors-1] - v.sp -= numSelectors + 1 - - if e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, val, selectors); e != nil { - v.err = e - return - } - - case compiler.OpIteratorInit: - var iterator objects.Object - - dst := v.stack[v.sp-1] - v.sp-- - - iterable, ok := dst.(objects.Iterable) - if !ok { - v.err = fmt.Errorf("not iterable: %s", dst.TypeName()) - return - } - - iterator = iterable.Iterate() - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp] = iterator - v.sp++ - - case compiler.OpIteratorNext: - iterator := v.stack[v.sp-1] - v.sp-- - - hasMore := iterator.(objects.Iterator).Next() - - if hasMore { - v.stack[v.sp] = objects.TrueValue - } else { - v.stack[v.sp] = objects.FalseValue - } - v.sp++ - - case compiler.OpIteratorKey: - iterator := v.stack[v.sp-1] - v.sp-- - - val := iterator.(objects.Iterator).Key() - - v.stack[v.sp] = val - v.sp++ - - case compiler.OpIteratorValue: - iterator := v.stack[v.sp-1] - v.sp-- - - val := iterator.(objects.Iterator).Value() - - v.stack[v.sp] = val - v.sp++ - - default: - v.err = fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip]) - return - } - } -} - -// IsStackEmpty tests if the stack is empty or not. -func (v *VM) IsStackEmpty() bool { - return v.sp == 0 -} - -func indexAssign(dst, src objects.Object, selectors []objects.Object) error { - numSel := len(selectors) - - for sidx := numSel - 1; sidx > 0; sidx-- { - indexable, ok := dst.(objects.Indexable) - if !ok { - return fmt.Errorf("not indexable: %s", dst.TypeName()) - } - - next, err := indexable.IndexGet(selectors[sidx]) - if err != nil { - if err == objects.ErrInvalidIndexType { - return fmt.Errorf("invalid index type: %s", selectors[sidx].TypeName()) - } - - return err - } - - dst = next - } - - indexAssignable, ok := dst.(objects.IndexAssignable) - if !ok { - return fmt.Errorf("not index-assignable: %s", dst.TypeName()) - } - - if err := indexAssignable.IndexSet(selectors[0], src); err != nil { - if err == objects.ErrInvalidIndexValueType { - return fmt.Errorf("invaid index value type: %s", src.TypeName()) - } - - return err - } - - return nil -} diff --git a/vendor/github.com/d5/tengo/script/compiled.go b/vendor/github.com/d5/tengo/script/compiled.go deleted file mode 100644 index ce50f498..00000000 --- a/vendor/github.com/d5/tengo/script/compiled.go +++ /dev/null @@ -1,159 +0,0 @@ -package script - -import ( - "context" - "fmt" - "sync" - - "github.com/d5/tengo/compiler" - "github.com/d5/tengo/objects" - "github.com/d5/tengo/runtime" -) - -// Compiled is a compiled instance of the user script. -// Use Script.Compile() to create Compiled object. -type Compiled struct { - globalIndexes map[string]int // global symbol name to index - bytecode *compiler.Bytecode - globals []objects.Object - maxAllocs int64 - lock sync.RWMutex -} - -// Run executes the compiled script in the virtual machine. -func (c *Compiled) Run() error { - c.lock.Lock() - defer c.lock.Unlock() - - v := runtime.NewVM(c.bytecode, c.globals, c.maxAllocs) - - return v.Run() -} - -// RunContext is like Run but includes a context. -func (c *Compiled) RunContext(ctx context.Context) (err error) { - c.lock.Lock() - defer c.lock.Unlock() - - v := runtime.NewVM(c.bytecode, c.globals, c.maxAllocs) - - ch := make(chan error, 1) - - go func() { - ch <- v.Run() - }() - - select { - case <-ctx.Done(): - v.Abort() - <-ch - err = ctx.Err() - case err = <-ch: - } - - return -} - -// Clone creates a new copy of Compiled. -// Cloned copies are safe for concurrent use by multiple goroutines. -func (c *Compiled) Clone() *Compiled { - c.lock.Lock() - defer c.lock.Unlock() - - clone := &Compiled{ - globalIndexes: c.globalIndexes, - bytecode: c.bytecode, - globals: make([]objects.Object, len(c.globals)), - maxAllocs: c.maxAllocs, - } - - // copy global objects - for idx, g := range c.globals { - if g != nil { - clone.globals[idx] = g - } - } - - return clone -} - -// IsDefined returns true if the variable name is defined (has value) before or after the execution. -func (c *Compiled) IsDefined(name string) bool { - c.lock.RLock() - defer c.lock.RUnlock() - - idx, ok := c.globalIndexes[name] - if !ok { - return false - } - - v := c.globals[idx] - if v == nil { - return false - } - - return v != objects.UndefinedValue -} - -// Get returns a variable identified by the name. -func (c *Compiled) Get(name string) *Variable { - c.lock.RLock() - defer c.lock.RUnlock() - - value := objects.UndefinedValue - - if idx, ok := c.globalIndexes[name]; ok { - value = c.globals[idx] - if value == nil { - value = objects.UndefinedValue - } - } - - return &Variable{ - name: name, - value: value, - } -} - -// GetAll returns all the variables that are defined by the compiled script. -func (c *Compiled) GetAll() []*Variable { - c.lock.RLock() - defer c.lock.RUnlock() - - var vars []*Variable - - for name, idx := range c.globalIndexes { - value := c.globals[idx] - if value == nil { - value = objects.UndefinedValue - } - - vars = append(vars, &Variable{ - name: name, - value: value, - }) - } - - return vars -} - -// Set replaces the value of a global variable identified by the name. -// An error will be returned if the name was not defined during compilation. -func (c *Compiled) Set(name string, value interface{}) error { - c.lock.Lock() - defer c.lock.Unlock() - - obj, err := objects.FromInterface(value) - if err != nil { - return err - } - - idx, ok := c.globalIndexes[name] - if !ok { - return fmt.Errorf("'%s' is not defined", name) - } - - c.globals[idx] = obj - - return nil -} diff --git a/vendor/github.com/d5/tengo/script/script.go b/vendor/github.com/d5/tengo/script/script.go deleted file mode 100644 index 2ee67b61..00000000 --- a/vendor/github.com/d5/tengo/script/script.go +++ /dev/null @@ -1,185 +0,0 @@ -package script - -import ( - "context" - "fmt" - - "github.com/d5/tengo/compiler" - "github.com/d5/tengo/compiler/parser" - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/objects" - "github.com/d5/tengo/runtime" -) - -// Script can simplify compilation and execution of embedded scripts. -type Script struct { - variables map[string]*Variable - modules *objects.ModuleMap - input []byte - maxAllocs int64 - maxConstObjects int - enableFileImport bool -} - -// New creates a Script instance with an input script. -func New(input []byte) *Script { - return &Script{ - variables: make(map[string]*Variable), - input: input, - maxAllocs: -1, - maxConstObjects: -1, - } -} - -// Add adds a new variable or updates an existing variable to the script. -func (s *Script) Add(name string, value interface{}) error { - obj, err := objects.FromInterface(value) - if err != nil { - return err - } - - s.variables[name] = &Variable{ - name: name, - value: obj, - } - - return nil -} - -// Remove removes (undefines) an existing variable for the script. -// It returns false if the variable name is not defined. -func (s *Script) Remove(name string) bool { - if _, ok := s.variables[name]; !ok { - return false - } - - delete(s.variables, name) - - return true -} - -// SetImports sets import modules. -func (s *Script) SetImports(modules *objects.ModuleMap) { - s.modules = modules -} - -// SetMaxAllocs sets the maximum number of objects allocations during the run time. -// Compiled script will return runtime.ErrObjectAllocLimit error if it exceeds this limit. -func (s *Script) SetMaxAllocs(n int64) { - s.maxAllocs = n -} - -// SetMaxConstObjects sets the maximum number of objects in the compiled constants. -func (s *Script) SetMaxConstObjects(n int) { - s.maxConstObjects = n -} - -// EnableFileImport enables or disables module loading from local files. -// Local file modules are disabled by default. -func (s *Script) EnableFileImport(enable bool) { - s.enableFileImport = enable -} - -// Compile compiles the script with all the defined variables, and, returns Compiled object. -func (s *Script) Compile() (*Compiled, error) { - symbolTable, globals, err := s.prepCompile() - if err != nil { - return nil, err - } - - fileSet := source.NewFileSet() - srcFile := fileSet.AddFile("(main)", -1, len(s.input)) - - p := parser.NewParser(srcFile, s.input, nil) - file, err := p.ParseFile() - if err != nil { - return nil, err - } - - c := compiler.NewCompiler(srcFile, symbolTable, nil, s.modules, nil) - c.EnableFileImport(s.enableFileImport) - if err := c.Compile(file); err != nil { - return nil, err - } - - // reduce globals size - globals = globals[:symbolTable.MaxSymbols()+1] - - // global symbol names to indexes - globalIndexes := make(map[string]int, len(globals)) - for _, name := range symbolTable.Names() { - symbol, _, _ := symbolTable.Resolve(name) - if symbol.Scope == compiler.ScopeGlobal { - globalIndexes[name] = symbol.Index - } - } - - // remove duplicates from constants - bytecode := c.Bytecode() - bytecode.RemoveDuplicates() - - // check the constant objects limit - if s.maxConstObjects >= 0 { - cnt := bytecode.CountObjects() - if cnt > s.maxConstObjects { - return nil, fmt.Errorf("exceeding constant objects limit: %d", cnt) - } - } - - return &Compiled{ - globalIndexes: globalIndexes, - bytecode: bytecode, - globals: globals, - maxAllocs: s.maxAllocs, - }, nil -} - -// Run compiles and runs the scripts. -// Use returned compiled object to access global variables. -func (s *Script) Run() (compiled *Compiled, err error) { - compiled, err = s.Compile() - if err != nil { - return - } - - err = compiled.Run() - - return -} - -// RunContext is like Run but includes a context. -func (s *Script) RunContext(ctx context.Context) (compiled *Compiled, err error) { - compiled, err = s.Compile() - if err != nil { - return - } - - err = compiled.RunContext(ctx) - - return -} - -func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, globals []objects.Object, err error) { - var names []string - for name := range s.variables { - names = append(names, name) - } - - symbolTable = compiler.NewSymbolTable() - for idx, fn := range objects.Builtins { - symbolTable.DefineBuiltin(idx, fn.Name) - } - - globals = make([]objects.Object, runtime.GlobalsSize) - - for idx, name := range names { - symbol := symbolTable.Define(name) - if symbol.Index != idx { - panic(fmt.Errorf("wrong symbol index: %d != %d", idx, symbol.Index)) - } - - globals[symbol.Index] = s.variables[name].value - } - - return -} diff --git a/vendor/github.com/d5/tengo/script/variable.go b/vendor/github.com/d5/tengo/script/variable.go deleted file mode 100644 index df345115..00000000 --- a/vendor/github.com/d5/tengo/script/variable.go +++ /dev/null @@ -1,149 +0,0 @@ -package script - -import ( - "errors" - - "github.com/d5/tengo/objects" -) - -// Variable is a user-defined variable for the script. -type Variable struct { - name string - value objects.Object -} - -// NewVariable creates a Variable. -func NewVariable(name string, value interface{}) (*Variable, error) { - obj, err := objects.FromInterface(value) - if err != nil { - return nil, err - } - - return &Variable{ - name: name, - value: obj, - }, nil -} - -// Name returns the name of the variable. -func (v *Variable) Name() string { - return v.name -} - -// Value returns an empty interface of the variable value. -func (v *Variable) Value() interface{} { - return objects.ToInterface(v.value) -} - -// ValueType returns the name of the value type. -func (v *Variable) ValueType() string { - return v.value.TypeName() -} - -// Int returns int value of the variable value. -// It returns 0 if the value is not convertible to int. -func (v *Variable) Int() int { - c, _ := objects.ToInt(v.value) - - return c -} - -// Int64 returns int64 value of the variable value. -// It returns 0 if the value is not convertible to int64. -func (v *Variable) Int64() int64 { - c, _ := objects.ToInt64(v.value) - - return c -} - -// Float returns float64 value of the variable value. -// It returns 0.0 if the value is not convertible to float64. -func (v *Variable) Float() float64 { - c, _ := objects.ToFloat64(v.value) - - return c -} - -// Char returns rune value of the variable value. -// It returns 0 if the value is not convertible to rune. -func (v *Variable) Char() rune { - c, _ := objects.ToRune(v.value) - - return c -} - -// Bool returns bool value of the variable value. -// It returns 0 if the value is not convertible to bool. -func (v *Variable) Bool() bool { - c, _ := objects.ToBool(v.value) - - return c -} - -// Array returns []interface value of the variable value. -// It returns 0 if the value is not convertible to []interface. -func (v *Variable) Array() []interface{} { - switch val := v.value.(type) { - case *objects.Array: - var arr []interface{} - for _, e := range val.Value { - arr = append(arr, objects.ToInterface(e)) - } - return arr - } - - return nil -} - -// Map returns map[string]interface{} value of the variable value. -// It returns 0 if the value is not convertible to map[string]interface{}. -func (v *Variable) Map() map[string]interface{} { - switch val := v.value.(type) { - case *objects.Map: - kv := make(map[string]interface{}) - for mk, mv := range val.Value { - kv[mk] = objects.ToInterface(mv) - } - return kv - } - - return nil -} - -// String returns string value of the variable value. -// It returns 0 if the value is not convertible to string. -func (v *Variable) String() string { - c, _ := objects.ToString(v.value) - - return c -} - -// Bytes returns a byte slice of the variable value. -// It returns nil if the value is not convertible to byte slice. -func (v *Variable) Bytes() []byte { - c, _ := objects.ToByteSlice(v.value) - - return c -} - -// Error returns an error if the underlying value is error object. -// If not, this returns nil. -func (v *Variable) Error() error { - err, ok := v.value.(*objects.Error) - if ok { - return errors.New(err.String()) - } - - return nil -} - -// Object returns an underlying Object of the variable value. -// Note that returned Object is a copy of an actual Object used in the script. -func (v *Variable) Object() objects.Object { - return v.value -} - -// IsUndefined returns true if the underlying value is undefined. -func (v *Variable) IsUndefined() bool { - return v.value == objects.UndefinedValue -} diff --git a/vendor/github.com/d5/tengo/stdlib/base64.go b/vendor/github.com/d5/tengo/stdlib/base64.go deleted file mode 100644 index 40a746ce..00000000 --- a/vendor/github.com/d5/tengo/stdlib/base64.go +++ /dev/null @@ -1,20 +0,0 @@ -package stdlib - -import ( - "encoding/base64" - "github.com/d5/tengo/objects" -) - -var base64Module = map[string]objects.Object{ - "encode": &objects.UserFunction{Value: FuncAYRS(base64.StdEncoding.EncodeToString)}, - "decode": &objects.UserFunction{Value: FuncASRYE(base64.StdEncoding.DecodeString)}, - - "raw_encode": &objects.UserFunction{Value: FuncAYRS(base64.RawStdEncoding.EncodeToString)}, - "raw_decode": &objects.UserFunction{Value: FuncASRYE(base64.RawStdEncoding.DecodeString)}, - - "url_encode": &objects.UserFunction{Value: FuncAYRS(base64.URLEncoding.EncodeToString)}, - "url_decode": &objects.UserFunction{Value: FuncASRYE(base64.URLEncoding.DecodeString)}, - - "raw_url_encode": &objects.UserFunction{Value: FuncAYRS(base64.RawURLEncoding.EncodeToString)}, - "raw_url_decode": &objects.UserFunction{Value: FuncASRYE(base64.RawURLEncoding.DecodeString)}, -} diff --git a/vendor/github.com/d5/tengo/stdlib/builtin_modules.go b/vendor/github.com/d5/tengo/stdlib/builtin_modules.go deleted file mode 100644 index 722461b2..00000000 --- a/vendor/github.com/d5/tengo/stdlib/builtin_modules.go +++ /dev/null @@ -1,16 +0,0 @@ -package stdlib - -import "github.com/d5/tengo/objects" - -// BuiltinModules are builtin type standard library modules. -var BuiltinModules = map[string]map[string]objects.Object{ - "math": mathModule, - "os": osModule, - "text": textModule, - "times": timesModule, - "rand": randModule, - "fmt": fmtModule, - "json": jsonModule, - "base64": base64Module, - "hex": hexModule, -} diff --git a/vendor/github.com/d5/tengo/stdlib/errors.go b/vendor/github.com/d5/tengo/stdlib/errors.go deleted file mode 100644 index a2942bb0..00000000 --- a/vendor/github.com/d5/tengo/stdlib/errors.go +++ /dev/null @@ -1,11 +0,0 @@ -package stdlib - -import "github.com/d5/tengo/objects" - -func wrapError(err error) objects.Object { - if err == nil { - return objects.TrueValue - } - - return &objects.Error{Value: &objects.String{Value: err.Error()}} -} diff --git a/vendor/github.com/d5/tengo/stdlib/fmt.go b/vendor/github.com/d5/tengo/stdlib/fmt.go deleted file mode 100644 index b8f64278..00000000 --- a/vendor/github.com/d5/tengo/stdlib/fmt.go +++ /dev/null @@ -1,110 +0,0 @@ -package stdlib - -import ( - "fmt" - - "github.com/d5/tengo" - "github.com/d5/tengo/objects" -) - -var fmtModule = map[string]objects.Object{ - "print": &objects.UserFunction{Name: "print", Value: fmtPrint}, - "printf": &objects.UserFunction{Name: "printf", Value: fmtPrintf}, - "println": &objects.UserFunction{Name: "println", Value: fmtPrintln}, - "sprintf": &objects.UserFunction{Name: "sprintf", Value: fmtSprintf}, -} - -func fmtPrint(args ...objects.Object) (ret objects.Object, err error) { - printArgs, err := getPrintArgs(args...) - if err != nil { - return nil, err - } - - _, _ = fmt.Print(printArgs...) - - return nil, nil -} - -func fmtPrintf(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs == 0 { - return nil, objects.ErrWrongNumArguments - } - - format, ok := args[0].(*objects.String) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "format", - Expected: "string", - Found: args[0].TypeName(), - } - } - if numArgs == 1 { - fmt.Print(format) - return nil, nil - } - - s, err := objects.Format(format.Value, args[1:]...) - if err != nil { - return nil, err - } - - fmt.Print(s) - - return nil, nil -} - -func fmtPrintln(args ...objects.Object) (ret objects.Object, err error) { - printArgs, err := getPrintArgs(args...) - if err != nil { - return nil, err - } - - printArgs = append(printArgs, "\n") - _, _ = fmt.Print(printArgs...) - - return nil, nil -} - -func fmtSprintf(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs == 0 { - return nil, objects.ErrWrongNumArguments - } - - format, ok := args[0].(*objects.String) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "format", - Expected: "string", - Found: args[0].TypeName(), - } - } - if numArgs == 1 { - return format, nil // okay to return 'format' directly as String is immutable - } - - s, err := objects.Format(format.Value, args[1:]...) - if err != nil { - return nil, err - } - - return &objects.String{Value: s}, nil -} - -func getPrintArgs(args ...objects.Object) ([]interface{}, error) { - var printArgs []interface{} - l := 0 - for _, arg := range args { - s, _ := objects.ToString(arg) - slen := len(s) - if l+slen > tengo.MaxStringLen { // make sure length does not exceed the limit - return nil, objects.ErrStringLimit - } - l += slen - - printArgs = append(printArgs, s) - } - - return printArgs, nil -} diff --git a/vendor/github.com/d5/tengo/stdlib/func_typedefs.go b/vendor/github.com/d5/tengo/stdlib/func_typedefs.go deleted file mode 100644 index c7bd11fa..00000000 --- a/vendor/github.com/d5/tengo/stdlib/func_typedefs.go +++ /dev/null @@ -1,1178 +0,0 @@ -package stdlib - -import ( - "fmt" - - "github.com/d5/tengo" - "github.com/d5/tengo/objects" -) - -// FuncAR transform a function of 'func()' signature -// into CallableFunc type. -func FuncAR(fn func()) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - fn() - - return objects.UndefinedValue, nil - } -} - -// FuncARI transform a function of 'func() int' signature -// into CallableFunc type. -func FuncARI(fn func() int) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - return &objects.Int{Value: int64(fn())}, nil - } -} - -// FuncARI64 transform a function of 'func() int64' signature -// into CallableFunc type. -func FuncARI64(fn func() int64) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - return &objects.Int{Value: fn()}, nil - } -} - -// FuncAI64RI64 transform a function of 'func(int64) int64' signature -// into CallableFunc type. -func FuncAI64RI64(fn func(int64) int64) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - return &objects.Int{Value: fn(i1)}, nil - } -} - -// FuncAI64R transform a function of 'func(int64)' signature -// into CallableFunc type. -func FuncAI64R(fn func(int64)) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - fn(i1) - - return objects.UndefinedValue, nil - } -} - -// FuncARB transform a function of 'func() bool' signature -// into CallableFunc type. -func FuncARB(fn func() bool) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - if fn() { - return objects.TrueValue, nil - } - - return objects.FalseValue, nil - } -} - -// FuncARE transform a function of 'func() error' signature -// into CallableFunc type. -func FuncARE(fn func() error) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - return wrapError(fn()), nil - } -} - -// FuncARS transform a function of 'func() string' signature -// into CallableFunc type. -func FuncARS(fn func() string) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - s := fn() - - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} - -// FuncARSE transform a function of 'func() (string, error)' signature -// into CallableFunc type. -func FuncARSE(fn func() (string, error)) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - res, err := fn() - if err != nil { - return wrapError(err), nil - } - - if len(res) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: res}, nil - } -} - -// FuncARYE transform a function of 'func() ([]byte, error)' signature -// into CallableFunc type. -func FuncARYE(fn func() ([]byte, error)) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - res, err := fn() - if err != nil { - return wrapError(err), nil - } - - if len(res) > tengo.MaxBytesLen { - return nil, objects.ErrBytesLimit - } - - return &objects.Bytes{Value: res}, nil - } -} - -// FuncARF transform a function of 'func() float64' signature -// into CallableFunc type. -func FuncARF(fn func() float64) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - return &objects.Float{Value: fn()}, nil - } -} - -// FuncARSs transform a function of 'func() []string' signature -// into CallableFunc type. -func FuncARSs(fn func() []string) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - arr := &objects.Array{} - for _, elem := range fn() { - if len(elem) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: elem}) - } - - return arr, nil - } -} - -// FuncARIsE transform a function of 'func() ([]int, error)' signature -// into CallableFunc type. -func FuncARIsE(fn func() ([]int, error)) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - res, err := fn() - if err != nil { - return wrapError(err), nil - } - - arr := &objects.Array{} - for _, v := range res { - arr.Value = append(arr.Value, &objects.Int{Value: int64(v)}) - } - - return arr, nil - } -} - -// FuncAIRIs transform a function of 'func(int) []int' signature -// into CallableFunc type. -func FuncAIRIs(fn func(int) []int) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - res := fn(i1) - - arr := &objects.Array{} - for _, v := range res { - arr.Value = append(arr.Value, &objects.Int{Value: int64(v)}) - } - - return arr, nil - } -} - -// FuncAFRF transform a function of 'func(float64) float64' signature -// into CallableFunc type. -func FuncAFRF(fn func(float64) float64) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - f1, ok := objects.ToFloat64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(compatible)", - Found: args[0].TypeName(), - } - } - - return &objects.Float{Value: fn(f1)}, nil - } -} - -// FuncAIR transform a function of 'func(int)' signature -// into CallableFunc type. -func FuncAIR(fn func(int)) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - fn(i1) - - return objects.UndefinedValue, nil - } -} - -// FuncAIRF transform a function of 'func(int) float64' signature -// into CallableFunc type. -func FuncAIRF(fn func(int) float64) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - return &objects.Float{Value: fn(i1)}, nil - } -} - -// FuncAFRI transform a function of 'func(float64) int' signature -// into CallableFunc type. -func FuncAFRI(fn func(float64) int) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - f1, ok := objects.ToFloat64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(compatible)", - Found: args[0].TypeName(), - } - } - - return &objects.Int{Value: int64(fn(f1))}, nil - } -} - -// FuncAFFRF transform a function of 'func(float64, float64) float64' signature -// into CallableFunc type. -func FuncAFFRF(fn func(float64, float64) float64) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - f1, ok := objects.ToFloat64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(compatible)", - Found: args[0].TypeName(), - } - } - - f2, ok := objects.ToFloat64(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "float(compatible)", - Found: args[1].TypeName(), - } - } - - return &objects.Float{Value: fn(f1, f2)}, nil - } -} - -// FuncAIFRF transform a function of 'func(int, float64) float64' signature -// into CallableFunc type. -func FuncAIFRF(fn func(int, float64) float64) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - f2, ok := objects.ToFloat64(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "float(compatible)", - Found: args[1].TypeName(), - } - } - - return &objects.Float{Value: fn(i1, f2)}, nil - } -} - -// FuncAFIRF transform a function of 'func(float64, int) float64' signature -// into CallableFunc type. -func FuncAFIRF(fn func(float64, int) float64) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - f1, ok := objects.ToFloat64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(compatible)", - Found: args[0].TypeName(), - } - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - } - - return &objects.Float{Value: fn(f1, i2)}, nil - } -} - -// FuncAFIRB transform a function of 'func(float64, int) bool' signature -// into CallableFunc type. -func FuncAFIRB(fn func(float64, int) bool) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - f1, ok := objects.ToFloat64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(compatible)", - Found: args[0].TypeName(), - } - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - } - - if fn(f1, i2) { - return objects.TrueValue, nil - } - - return objects.FalseValue, nil - } -} - -// FuncAFRB transform a function of 'func(float64) bool' signature -// into CallableFunc type. -func FuncAFRB(fn func(float64) bool) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - f1, ok := objects.ToFloat64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(compatible)", - Found: args[0].TypeName(), - } - } - - if fn(f1) { - return objects.TrueValue, nil - } - - return objects.FalseValue, nil - } -} - -// FuncASRS transform a function of 'func(string) string' signature into CallableFunc type. -// User function will return 'true' if underlying native function returns nil. -func FuncASRS(fn func(string) string) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - s := fn(s1) - - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} - -// FuncASRSs transform a function of 'func(string) []string' signature into CallableFunc type. -func FuncASRSs(fn func(string) []string) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res := fn(s1) - - arr := &objects.Array{} - for _, elem := range res { - if len(elem) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: elem}) - } - - return arr, nil - } -} - -// FuncASRSE transform a function of 'func(string) (string, error)' signature into CallableFunc type. -// User function will return 'true' if underlying native function returns nil. -func FuncASRSE(fn func(string) (string, error)) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := fn(s1) - if err != nil { - return wrapError(err), nil - } - - if len(res) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: res}, nil - } -} - -// FuncASRE transform a function of 'func(string) error' signature into CallableFunc type. -// User function will return 'true' if underlying native function returns nil. -func FuncASRE(fn func(string) error) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - return wrapError(fn(s1)), nil - } -} - -// FuncASSRE transform a function of 'func(string, string) error' signature into CallableFunc type. -// User function will return 'true' if underlying native function returns nil. -func FuncASSRE(fn func(string, string) error) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - return wrapError(fn(s1, s2)), nil - } -} - -// FuncASSRSs transform a function of 'func(string, string) []string' signature into CallableFunc type. -func FuncASSRSs(fn func(string, string) []string) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - arr := &objects.Array{} - for _, res := range fn(s1, s2) { - if len(res) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: res}) - } - - return arr, nil - } -} - -// FuncASSIRSs transform a function of 'func(string, string, int) []string' signature into CallableFunc type. -func FuncASSIRSs(fn func(string, string, int) []string) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 3 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - i3, ok := objects.ToInt(args[2]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - } - - arr := &objects.Array{} - for _, res := range fn(s1, s2, i3) { - if len(res) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: res}) - } - - return arr, nil - } -} - -// FuncASSRI transform a function of 'func(string, string) int' signature into CallableFunc type. -func FuncASSRI(fn func(string, string) int) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - return &objects.Int{Value: int64(fn(s1, s2))}, nil - } -} - -// FuncASSRS transform a function of 'func(string, string) string' signature into CallableFunc type. -func FuncASSRS(fn func(string, string) string) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - s := fn(s1, s2) - - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} - -// FuncASSRB transform a function of 'func(string, string) bool' signature into CallableFunc type. -func FuncASSRB(fn func(string, string) bool) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - if fn(s1, s2) { - return objects.TrueValue, nil - } - - return objects.FalseValue, nil - } -} - -// FuncASsSRS transform a function of 'func([]string, string) string' signature into CallableFunc type. -func FuncASsSRS(fn func([]string, string) string) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - var ss1 []string - switch arg0 := args[0].(type) { - case *objects.Array: - for idx, a := range arg0.Value { - as, ok := objects.ToString(a) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: fmt.Sprintf("first[%d]", idx), - Expected: "string(compatible)", - Found: a.TypeName(), - } - } - ss1 = append(ss1, as) - } - case *objects.ImmutableArray: - for idx, a := range arg0.Value { - as, ok := objects.ToString(a) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: fmt.Sprintf("first[%d]", idx), - Expected: "string(compatible)", - Found: a.TypeName(), - } - } - ss1 = append(ss1, as) - } - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "array", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - s := fn(ss1, s2) - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} - -// FuncASI64RE transform a function of 'func(string, int64) error' signature -// into CallableFunc type. -func FuncASI64RE(fn func(string, int64) error) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - i2, ok := objects.ToInt64(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - } - - return wrapError(fn(s1, i2)), nil - } -} - -// FuncAIIRE transform a function of 'func(int, int) error' signature -// into CallableFunc type. -func FuncAIIRE(fn func(int, int) error) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - } - - return wrapError(fn(i1, i2)), nil - } -} - -// FuncASIRS transform a function of 'func(string, int) string' signature -// into CallableFunc type. -func FuncASIRS(fn func(string, int) string) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - } - - s := fn(s1, i2) - - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} - -// FuncASIIRE transform a function of 'func(string, int, int) error' signature -// into CallableFunc type. -func FuncASIIRE(fn func(string, int, int) error) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 3 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - } - - i3, ok := objects.ToInt(args[2]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - } - - return wrapError(fn(s1, i2, i3)), nil - } -} - -// FuncAYRIE transform a function of 'func([]byte) (int, error)' signature -// into CallableFunc type. -func FuncAYRIE(fn func([]byte) (int, error)) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - y1, ok := objects.ToByteSlice(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := fn(y1) - if err != nil { - return wrapError(err), nil - } - - return &objects.Int{Value: int64(res)}, nil - } -} - -// FuncAYRS transform a function of 'func([]byte) string' signature -// into CallableFunc type. -func FuncAYRS(fn func([]byte) string) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - y1, ok := objects.ToByteSlice(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes(compatible)", - Found: args[0].TypeName(), - } - } - - res := fn(y1) - - return &objects.String{Value: res}, nil - } -} - -// FuncASRIE transform a function of 'func(string) (int, error)' signature -// into CallableFunc type. -func FuncASRIE(fn func(string) (int, error)) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := fn(s1) - if err != nil { - return wrapError(err), nil - } - - return &objects.Int{Value: int64(res)}, nil - } -} - -// FuncASRYE transform a function of 'func(string) ([]byte, error)' signature -// into CallableFunc type. -func FuncASRYE(fn func(string) ([]byte, error)) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := fn(s1) - if err != nil { - return wrapError(err), nil - } - - if len(res) > tengo.MaxBytesLen { - return nil, objects.ErrBytesLimit - } - - return &objects.Bytes{Value: res}, nil - } -} - -// FuncAIRSsE transform a function of 'func(int) ([]string, error)' signature -// into CallableFunc type. -func FuncAIRSsE(fn func(int) ([]string, error)) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := fn(i1) - if err != nil { - return wrapError(err), nil - } - - arr := &objects.Array{} - for _, r := range res { - if len(r) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: r}) - } - - return arr, nil - } -} - -// FuncAIRS transform a function of 'func(int) string' signature -// into CallableFunc type. -func FuncAIRS(fn func(int) string) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - s := fn(i1) - - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/hex.go b/vendor/github.com/d5/tengo/stdlib/hex.go deleted file mode 100644 index acc29e6a..00000000 --- a/vendor/github.com/d5/tengo/stdlib/hex.go +++ /dev/null @@ -1,11 +0,0 @@ -package stdlib - -import ( - "encoding/hex" - "github.com/d5/tengo/objects" -) - -var hexModule = map[string]objects.Object{ - "encode": &objects.UserFunction{Value: FuncAYRS(hex.EncodeToString)}, - "decode": &objects.UserFunction{Value: FuncASRYE(hex.DecodeString)}, -} diff --git a/vendor/github.com/d5/tengo/stdlib/json.go b/vendor/github.com/d5/tengo/stdlib/json.go deleted file mode 100644 index f913dc48..00000000 --- a/vendor/github.com/d5/tengo/stdlib/json.go +++ /dev/null @@ -1,126 +0,0 @@ -package stdlib - -import ( - "bytes" - gojson "encoding/json" - - "github.com/d5/tengo/objects" - "github.com/d5/tengo/stdlib/json" -) - -var jsonModule = map[string]objects.Object{ - "decode": &objects.UserFunction{Name: "decode", Value: jsonDecode}, - "encode": &objects.UserFunction{Name: "encode", Value: jsonEncode}, - "indent": &objects.UserFunction{Name: "encode", Value: jsonIndent}, - "html_escape": &objects.UserFunction{Name: "html_escape", Value: jsonHTMLEscape}, -} - -func jsonDecode(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - switch o := args[0].(type) { - case *objects.Bytes: - v, err := json.Decode(o.Value) - if err != nil { - return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil - } - return v, nil - case *objects.String: - v, err := json.Decode([]byte(o.Value)) - if err != nil { - return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil - } - return v, nil - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes/string", - Found: args[0].TypeName(), - } - } -} - -func jsonEncode(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - b, err := json.Encode(args[0]) - if err != nil { - return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil - } - - return &objects.Bytes{Value: b}, nil -} - -func jsonIndent(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 3 { - return nil, objects.ErrWrongNumArguments - } - - prefix, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "prefix", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - indent, ok := objects.ToString(args[2]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "indent", - Expected: "string(compatible)", - Found: args[2].TypeName(), - } - } - - switch o := args[0].(type) { - case *objects.Bytes: - var dst bytes.Buffer - err := gojson.Indent(&dst, o.Value, prefix, indent) - if err != nil { - return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil - } - return &objects.Bytes{Value: dst.Bytes()}, nil - case *objects.String: - var dst bytes.Buffer - err := gojson.Indent(&dst, []byte(o.Value), prefix, indent) - if err != nil { - return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil - } - return &objects.Bytes{Value: dst.Bytes()}, nil - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes/string", - Found: args[0].TypeName(), - } - } -} - -func jsonHTMLEscape(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - switch o := args[0].(type) { - case *objects.Bytes: - var dst bytes.Buffer - gojson.HTMLEscape(&dst, o.Value) - return &objects.Bytes{Value: dst.Bytes()}, nil - case *objects.String: - var dst bytes.Buffer - gojson.HTMLEscape(&dst, []byte(o.Value)) - return &objects.Bytes{Value: dst.Bytes()}, nil - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes/string", - Found: args[0].TypeName(), - } - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/json/decode.go b/vendor/github.com/d5/tengo/stdlib/json/decode.go deleted file mode 100644 index 5a3fe6c7..00000000 --- a/vendor/github.com/d5/tengo/stdlib/json/decode.go +++ /dev/null @@ -1,374 +0,0 @@ -// A modified version of Go's JSON implementation. - -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "strconv" - "unicode" - "unicode/utf16" - "unicode/utf8" - - "github.com/d5/tengo/objects" -) - -// Decode parses the JSON-encoded data and returns the result object. -func Decode(data []byte) (objects.Object, error) { - var d decodeState - err := checkValid(data, &d.scan) - if err != nil { - return nil, err - } - - d.init(data) - d.scan.reset() - d.scanWhile(scanSkipSpace) - - return d.value() -} - -// decodeState represents the state while decoding a JSON value. -type decodeState struct { - data []byte - off int // next read offset in data - opcode int // last read result - scan scanner -} - -// readIndex returns the position of the last byte read. -func (d *decodeState) readIndex() int { - return d.off - 1 -} - -const phasePanicMsg = "JSON decoder out of sync - data changing underfoot?" - -func (d *decodeState) init(data []byte) *decodeState { - d.data = data - d.off = 0 - return d -} - -// scanNext processes the byte at d.data[d.off]. -func (d *decodeState) scanNext() { - if d.off < len(d.data) { - d.opcode = d.scan.step(&d.scan, d.data[d.off]) - d.off++ - } else { - d.opcode = d.scan.eof() - d.off = len(d.data) + 1 // mark processed EOF with len+1 - } -} - -// scanWhile processes bytes in d.data[d.off:] until it -// receives a scan code not equal to op. -func (d *decodeState) scanWhile(op int) { - s, data, i := &d.scan, d.data, d.off - for i < len(data) { - newOp := s.step(s, data[i]) - i++ - if newOp != op { - d.opcode = newOp - d.off = i - return - } - } - - d.off = len(data) + 1 // mark processed EOF with len+1 - d.opcode = d.scan.eof() -} - -func (d *decodeState) value() (objects.Object, error) { - switch d.opcode { - default: - panic(phasePanicMsg) - - case scanBeginArray: - o, err := d.array() - if err != nil { - return nil, err - } - - d.scanNext() - - return o, nil - - case scanBeginObject: - o, err := d.object() - if err != nil { - return nil, err - } - - d.scanNext() - - return o, nil - - case scanBeginLiteral: - return d.literal() - } -} - -func (d *decodeState) array() (objects.Object, error) { - var arr []objects.Object - for { - // Look ahead for ] - can only happen on first iteration. - d.scanWhile(scanSkipSpace) - if d.opcode == scanEndArray { - break - } - - o, err := d.value() - if err != nil { - return nil, err - } - arr = append(arr, o) - - // Next token must be , or ]. - if d.opcode == scanSkipSpace { - d.scanWhile(scanSkipSpace) - } - if d.opcode == scanEndArray { - break - } - if d.opcode != scanArrayValue { - panic(phasePanicMsg) - } - } - - return &objects.Array{Value: arr}, nil -} - -func (d *decodeState) object() (objects.Object, error) { - m := make(map[string]objects.Object) - for { - // Read opening " of string key or closing }. - d.scanWhile(scanSkipSpace) - if d.opcode == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if d.opcode != scanBeginLiteral { - panic(phasePanicMsg) - } - - // Read string key. - start := d.readIndex() - d.scanWhile(scanContinue) - item := d.data[start:d.readIndex()] - key, ok := unquote(item) - if !ok { - panic(phasePanicMsg) - } - - // Read : before value. - if d.opcode == scanSkipSpace { - d.scanWhile(scanSkipSpace) - } - if d.opcode != scanObjectKey { - panic(phasePanicMsg) - } - d.scanWhile(scanSkipSpace) - - // Read value. - o, err := d.value() - if err != nil { - return nil, err - } - - m[key] = o - - // Next token must be , or }. - if d.opcode == scanSkipSpace { - d.scanWhile(scanSkipSpace) - } - if d.opcode == scanEndObject { - break - } - if d.opcode != scanObjectValue { - panic(phasePanicMsg) - } - } - - return &objects.Map{Value: m}, nil -} - -func (d *decodeState) literal() (objects.Object, error) { - // All bytes inside literal return scanContinue op code. - start := d.readIndex() - d.scanWhile(scanContinue) - - item := d.data[start:d.readIndex()] - - switch c := item[0]; c { - case 'n': // null - return objects.UndefinedValue, nil - - case 't', 'f': // true, false - if c == 't' { - return objects.TrueValue, nil - } - return objects.FalseValue, nil - - case '"': // string - s, ok := unquote(item) - if !ok { - panic(phasePanicMsg) - } - return &objects.String{Value: s}, nil - - default: // number - if c != '-' && (c < '0' || c > '9') { - panic(phasePanicMsg) - } - - n, _ := strconv.ParseFloat(string(item), 10) - return &objects.Float{Value: n}, nil - } -} - -// getu4 decodes \uXXXX from the beginning of s, returning the hex value, -// or it returns -1. -func getu4(s []byte) rune { - if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { - return -1 - } - var r rune - for _, c := range s[2:6] { - switch { - case '0' <= c && c <= '9': - c = c - '0' - case 'a' <= c && c <= 'f': - c = c - 'a' + 10 - case 'A' <= c && c <= 'F': - c = c - 'A' + 10 - default: - return -1 - } - r = r*16 + rune(c) - } - return r -} - -// unquote converts a quoted JSON string literal s into an actual string t. -// The rules are different than for Go, so cannot use strconv.Unquote. -func unquote(s []byte) (t string, ok bool) { - s, ok = unquoteBytes(s) - t = string(s) - return -} - -func unquoteBytes(s []byte) (t []byte, ok bool) { - if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { - return - } - s = s[1 : len(s)-1] - - // Check for unusual characters. If there are none, - // then no unquoting is needed, so return a slice of the - // original bytes. - r := 0 - for r < len(s) { - c := s[r] - if c == '\\' || c == '"' || c < ' ' { - break - } - if c < utf8.RuneSelf { - r++ - continue - } - rr, size := utf8.DecodeRune(s[r:]) - if rr == utf8.RuneError && size == 1 { - break - } - r += size - } - if r == len(s) { - return s, true - } - - b := make([]byte, len(s)+2*utf8.UTFMax) - w := copy(b, s[0:r]) - for r < len(s) { - // Out of room? Can only happen if s is full of - // malformed UTF-8 and we're replacing each - // byte with RuneError. - if w >= len(b)-2*utf8.UTFMax { - nb := make([]byte, (len(b)+utf8.UTFMax)*2) - copy(nb, b[0:w]) - b = nb - } - switch c := s[r]; { - case c == '\\': - r++ - if r >= len(s) { - return - } - switch s[r] { - default: - return - case '"', '\\', '/', '\'': - b[w] = s[r] - r++ - w++ - case 'b': - b[w] = '\b' - r++ - w++ - case 'f': - b[w] = '\f' - r++ - w++ - case 'n': - b[w] = '\n' - r++ - w++ - case 'r': - b[w] = '\r' - r++ - w++ - case 't': - b[w] = '\t' - r++ - w++ - case 'u': - r-- - rr := getu4(s[r:]) - if rr < 0 { - return - } - r += 6 - if utf16.IsSurrogate(rr) { - rr1 := getu4(s[r:]) - if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { - // A valid pair; consume. - r += 6 - w += utf8.EncodeRune(b[w:], dec) - break - } - // Invalid surrogate; fall back to replacement rune. - rr = unicode.ReplacementChar - } - w += utf8.EncodeRune(b[w:], rr) - } - - // Quote, control characters are invalid. - case c == '"', c < ' ': - return - - // ASCII - case c < utf8.RuneSelf: - b[w] = c - r++ - w++ - - // Coerce to well-formed UTF-8. - default: - rr, size := utf8.DecodeRune(s[r:]) - r += size - w += utf8.EncodeRune(b[w:], rr) - } - } - return b[0:w], true -} diff --git a/vendor/github.com/d5/tengo/stdlib/json/encode.go b/vendor/github.com/d5/tengo/stdlib/json/encode.go deleted file mode 100644 index 2b8b17eb..00000000 --- a/vendor/github.com/d5/tengo/stdlib/json/encode.go +++ /dev/null @@ -1,147 +0,0 @@ -// A modified version of Go's JSON implementation. - -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import ( - "encoding/base64" - "errors" - "math" - "strconv" - - "github.com/d5/tengo/objects" -) - -// Encode returns the JSON encoding of the object. -func Encode(o objects.Object) ([]byte, error) { - var b []byte - - switch o := o.(type) { - case *objects.Array: - b = append(b, '[') - len1 := len(o.Value) - 1 - for idx, elem := range o.Value { - eb, err := Encode(elem) - if err != nil { - return nil, err - } - b = append(b, eb...) - if idx < len1 { - b = append(b, ',') - } - } - b = append(b, ']') - case *objects.ImmutableArray: - b = append(b, '[') - len1 := len(o.Value) - 1 - for idx, elem := range o.Value { - eb, err := Encode(elem) - if err != nil { - return nil, err - } - b = append(b, eb...) - if idx < len1 { - b = append(b, ',') - } - } - b = append(b, ']') - case *objects.Map: - b = append(b, '{') - len1 := len(o.Value) - 1 - idx := 0 - for key, value := range o.Value { - b = strconv.AppendQuote(b, key) - b = append(b, ':') - eb, err := Encode(value) - if err != nil { - return nil, err - } - b = append(b, eb...) - if idx < len1 { - b = append(b, ',') - } - idx++ - } - b = append(b, '}') - case *objects.ImmutableMap: - b = append(b, '{') - len1 := len(o.Value) - 1 - idx := 0 - for key, value := range o.Value { - b = strconv.AppendQuote(b, key) - b = append(b, ':') - eb, err := Encode(value) - if err != nil { - return nil, err - } - b = append(b, eb...) - if idx < len1 { - b = append(b, ',') - } - idx++ - } - b = append(b, '}') - case *objects.Bool: - if o.IsFalsy() { - b = strconv.AppendBool(b, false) - } else { - b = strconv.AppendBool(b, true) - } - case *objects.Bytes: - b = append(b, '"') - encodedLen := base64.StdEncoding.EncodedLen(len(o.Value)) - dst := make([]byte, encodedLen) - base64.StdEncoding.Encode(dst, o.Value) - b = append(b, dst...) - b = append(b, '"') - case *objects.Char: - b = strconv.AppendInt(b, int64(o.Value), 10) - case *objects.Float: - var y []byte - - f := o.Value - if math.IsInf(f, 0) || math.IsNaN(f) { - return nil, errors.New("unsupported float value") - } - - // Convert as if by ES6 number to string conversion. - // This matches most other JSON generators. - abs := math.Abs(f) - fmt := byte('f') - if abs != 0 { - if abs < 1e-6 || abs >= 1e21 { - fmt = 'e' - } - } - y = strconv.AppendFloat(y, f, fmt, -1, 64) - if fmt == 'e' { - // clean up e-09 to e-9 - n := len(y) - if n >= 4 && y[n-4] == 'e' && y[n-3] == '-' && y[n-2] == '0' { - y[n-2] = y[n-1] - y = y[:n-1] - } - } - - b = append(b, y...) - case *objects.Int: - b = strconv.AppendInt(b, o.Value, 10) - case *objects.String: - b = strconv.AppendQuote(b, o.Value) - case *objects.Time: - y, err := o.Value.MarshalJSON() - if err != nil { - return nil, err - } - b = append(b, y...) - case *objects.Undefined: - b = append(b, "null"...) - default: - // unknown type: ignore - } - - return b, nil -} diff --git a/vendor/github.com/d5/tengo/stdlib/json/scanner.go b/vendor/github.com/d5/tengo/stdlib/json/scanner.go deleted file mode 100644 index 8fc6776d..00000000 --- a/vendor/github.com/d5/tengo/stdlib/json/scanner.go +++ /dev/null @@ -1,559 +0,0 @@ -// A modified version of Go's JSON implementation. - -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package json - -import "strconv" - -func checkValid(data []byte, scan *scanner) error { - scan.reset() - for _, c := range data { - scan.bytes++ - if scan.step(scan, c) == scanError { - return scan.err - } - } - if scan.eof() == scanError { - return scan.err - } - return nil -} - -// A SyntaxError is a description of a JSON syntax error. -type SyntaxError struct { - msg string // description of error - Offset int64 // error occurred after reading Offset bytes -} - -func (e *SyntaxError) Error() string { return e.msg } - -// A scanner is a JSON scanning state machine. -// Callers call scan.reset() and then pass bytes in one at a time -// by calling scan.step(&scan, c) for each byte. -// The return value, referred to as an opcode, tells the -// caller about significant parsing events like beginning -// and ending literals, objects, and arrays, so that the -// caller can follow along if it wishes. -// The return value scanEnd indicates that a single top-level -// JSON value has been completed, *before* the byte that -// just got passed in. (The indication must be delayed in order -// to recognize the end of numbers: is 123 a whole value or -// the beginning of 12345e+6?). -type scanner struct { - // The step is a func to be called to execute the next transition. - // Also tried using an integer constant and a single func - // with a switch, but using the func directly was 10% faster - // on a 64-bit Mac Mini, and it's nicer to read. - step func(*scanner, byte) int - - // Reached end of top-level value. - endTop bool - - // Stack of what we're in the middle of - array values, object keys, object values. - parseState []int - - // Error that happened, if any. - err error - - // total bytes consumed, updated by decoder.Decode - bytes int64 -} - -// These values are returned by the state transition functions -// assigned to scanner.state and the method scanner.eof. -// They give details about the current state of the scan that -// callers might be interested to know about. -// It is okay to ignore the return value of any particular -// call to scanner.state: if one call returns scanError, -// every subsequent call will return scanError too. -const ( - // Continue. - scanContinue = iota // uninteresting byte - scanBeginLiteral // end implied by next result != scanContinue - scanBeginObject // begin object - scanObjectKey // just finished object key (string) - scanObjectValue // just finished non-last object value - scanEndObject // end object (implies scanObjectValue if possible) - scanBeginArray // begin array - scanArrayValue // just finished array value - scanEndArray // end array (implies scanArrayValue if possible) - scanSkipSpace // space byte; can skip; known to be last "continue" result - - // Stop. - scanEnd // top-level value ended *before* this byte; known to be first "stop" result - scanError // hit an error, scanner.err. -) - -// These values are stored in the parseState stack. -// They give the current state of a composite value -// being scanned. If the parser is inside a nested value -// the parseState describes the nested state, outermost at entry 0. -const ( - parseObjectKey = iota // parsing object key (before colon) - parseObjectValue // parsing object value (after colon) - parseArrayValue // parsing array value -) - -// reset prepares the scanner for use. -// It must be called before calling s.step. -func (s *scanner) reset() { - s.step = stateBeginValue - s.parseState = s.parseState[0:0] - s.err = nil - s.endTop = false -} - -// eof tells the scanner that the end of input has been reached. -// It returns a scan status just as s.step does. -func (s *scanner) eof() int { - if s.err != nil { - return scanError - } - if s.endTop { - return scanEnd - } - s.step(s, ' ') - if s.endTop { - return scanEnd - } - if s.err == nil { - s.err = &SyntaxError{"unexpected end of JSON input", s.bytes} - } - return scanError -} - -// pushParseState pushes a new parse state p onto the parse stack. -func (s *scanner) pushParseState(p int) { - s.parseState = append(s.parseState, p) -} - -// popParseState pops a parse state (already obtained) off the stack -// and updates s.step accordingly. -func (s *scanner) popParseState() { - n := len(s.parseState) - 1 - s.parseState = s.parseState[0:n] - if n == 0 { - s.step = stateEndTop - s.endTop = true - } else { - s.step = stateEndValue - } -} - -func isSpace(c byte) bool { - return c == ' ' || c == '\t' || c == '\r' || c == '\n' -} - -// stateBeginValueOrEmpty is the state after reading `[`. -func stateBeginValueOrEmpty(s *scanner, c byte) int { - if c <= ' ' && isSpace(c) { - return scanSkipSpace - } - if c == ']' { - return stateEndValue(s, c) - } - return stateBeginValue(s, c) -} - -// stateBeginValue is the state at the beginning of the input. -func stateBeginValue(s *scanner, c byte) int { - if c <= ' ' && isSpace(c) { - return scanSkipSpace - } - switch c { - case '{': - s.step = stateBeginStringOrEmpty - s.pushParseState(parseObjectKey) - return scanBeginObject - case '[': - s.step = stateBeginValueOrEmpty - s.pushParseState(parseArrayValue) - return scanBeginArray - case '"': - s.step = stateInString - return scanBeginLiteral - case '-': - s.step = stateNeg - return scanBeginLiteral - case '0': // beginning of 0.123 - s.step = state0 - return scanBeginLiteral - case 't': // beginning of true - s.step = stateT - return scanBeginLiteral - case 'f': // beginning of false - s.step = stateF - return scanBeginLiteral - case 'n': // beginning of null - s.step = stateN - return scanBeginLiteral - } - if '1' <= c && c <= '9' { // beginning of 1234.5 - s.step = state1 - return scanBeginLiteral - } - return s.error(c, "looking for beginning of value") -} - -// stateBeginStringOrEmpty is the state after reading `{`. -func stateBeginStringOrEmpty(s *scanner, c byte) int { - if c <= ' ' && isSpace(c) { - return scanSkipSpace - } - if c == '}' { - n := len(s.parseState) - s.parseState[n-1] = parseObjectValue - return stateEndValue(s, c) - } - return stateBeginString(s, c) -} - -// stateBeginString is the state after reading `{"key": value,`. -func stateBeginString(s *scanner, c byte) int { - if c <= ' ' && isSpace(c) { - return scanSkipSpace - } - if c == '"' { - s.step = stateInString - return scanBeginLiteral - } - return s.error(c, "looking for beginning of object key string") -} - -// stateEndValue is the state after completing a value, -// such as after reading `{}` or `true` or `["x"`. -func stateEndValue(s *scanner, c byte) int { - n := len(s.parseState) - if n == 0 { - // Completed top-level before the current byte. - s.step = stateEndTop - s.endTop = true - return stateEndTop(s, c) - } - if c <= ' ' && isSpace(c) { - s.step = stateEndValue - return scanSkipSpace - } - ps := s.parseState[n-1] - switch ps { - case parseObjectKey: - if c == ':' { - s.parseState[n-1] = parseObjectValue - s.step = stateBeginValue - return scanObjectKey - } - return s.error(c, "after object key") - case parseObjectValue: - if c == ',' { - s.parseState[n-1] = parseObjectKey - s.step = stateBeginString - return scanObjectValue - } - if c == '}' { - s.popParseState() - return scanEndObject - } - return s.error(c, "after object key:value pair") - case parseArrayValue: - if c == ',' { - s.step = stateBeginValue - return scanArrayValue - } - if c == ']' { - s.popParseState() - return scanEndArray - } - return s.error(c, "after array element") - } - return s.error(c, "") -} - -// stateEndTop is the state after finishing the top-level value, -// such as after reading `{}` or `[1,2,3]`. -// Only space characters should be seen now. -func stateEndTop(s *scanner, c byte) int { - if !isSpace(c) { - // Complain about non-space byte on next call. - s.error(c, "after top-level value") - } - return scanEnd -} - -// stateInString is the state after reading `"`. -func stateInString(s *scanner, c byte) int { - if c == '"' { - s.step = stateEndValue - return scanContinue - } - if c == '\\' { - s.step = stateInStringEsc - return scanContinue - } - if c < 0x20 { - return s.error(c, "in string literal") - } - return scanContinue -} - -// stateInStringEsc is the state after reading `"\` during a quoted string. -func stateInStringEsc(s *scanner, c byte) int { - switch c { - case 'b', 'f', 'n', 'r', 't', '\\', '/', '"': - s.step = stateInString - return scanContinue - case 'u': - s.step = stateInStringEscU - return scanContinue - } - return s.error(c, "in string escape code") -} - -// stateInStringEscU is the state after reading `"\u` during a quoted string. -func stateInStringEscU(s *scanner, c byte) int { - if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { - s.step = stateInStringEscU1 - return scanContinue - } - // numbers - return s.error(c, "in \\u hexadecimal character escape") -} - -// stateInStringEscU1 is the state after reading `"\u1` during a quoted string. -func stateInStringEscU1(s *scanner, c byte) int { - if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { - s.step = stateInStringEscU12 - return scanContinue - } - // numbers - return s.error(c, "in \\u hexadecimal character escape") -} - -// stateInStringEscU12 is the state after reading `"\u12` during a quoted string. -func stateInStringEscU12(s *scanner, c byte) int { - if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { - s.step = stateInStringEscU123 - return scanContinue - } - // numbers - return s.error(c, "in \\u hexadecimal character escape") -} - -// stateInStringEscU123 is the state after reading `"\u123` during a quoted string. -func stateInStringEscU123(s *scanner, c byte) int { - if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { - s.step = stateInString - return scanContinue - } - // numbers - return s.error(c, "in \\u hexadecimal character escape") -} - -// stateNeg is the state after reading `-` during a number. -func stateNeg(s *scanner, c byte) int { - if c == '0' { - s.step = state0 - return scanContinue - } - if '1' <= c && c <= '9' { - s.step = state1 - return scanContinue - } - return s.error(c, "in numeric literal") -} - -// state1 is the state after reading a non-zero integer during a number, -// such as after reading `1` or `100` but not `0`. -func state1(s *scanner, c byte) int { - if '0' <= c && c <= '9' { - s.step = state1 - return scanContinue - } - return state0(s, c) -} - -// state0 is the state after reading `0` during a number. -func state0(s *scanner, c byte) int { - if c == '.' { - s.step = stateDot - return scanContinue - } - if c == 'e' || c == 'E' { - s.step = stateE - return scanContinue - } - return stateEndValue(s, c) -} - -// stateDot is the state after reading the integer and decimal point in a number, -// such as after reading `1.`. -func stateDot(s *scanner, c byte) int { - if '0' <= c && c <= '9' { - s.step = stateDot0 - return scanContinue - } - return s.error(c, "after decimal point in numeric literal") -} - -// stateDot0 is the state after reading the integer, decimal point, and subsequent -// digits of a number, such as after reading `3.14`. -func stateDot0(s *scanner, c byte) int { - if '0' <= c && c <= '9' { - return scanContinue - } - if c == 'e' || c == 'E' { - s.step = stateE - return scanContinue - } - return stateEndValue(s, c) -} - -// stateE is the state after reading the mantissa and e in a number, -// such as after reading `314e` or `0.314e`. -func stateE(s *scanner, c byte) int { - if c == '+' || c == '-' { - s.step = stateESign - return scanContinue - } - return stateESign(s, c) -} - -// stateESign is the state after reading the mantissa, e, and sign in a number, -// such as after reading `314e-` or `0.314e+`. -func stateESign(s *scanner, c byte) int { - if '0' <= c && c <= '9' { - s.step = stateE0 - return scanContinue - } - return s.error(c, "in exponent of numeric literal") -} - -// stateE0 is the state after reading the mantissa, e, optional sign, -// and at least one digit of the exponent in a number, -// such as after reading `314e-2` or `0.314e+1` or `3.14e0`. -func stateE0(s *scanner, c byte) int { - if '0' <= c && c <= '9' { - return scanContinue - } - return stateEndValue(s, c) -} - -// stateT is the state after reading `t`. -func stateT(s *scanner, c byte) int { - if c == 'r' { - s.step = stateTr - return scanContinue - } - return s.error(c, "in literal true (expecting 'r')") -} - -// stateTr is the state after reading `tr`. -func stateTr(s *scanner, c byte) int { - if c == 'u' { - s.step = stateTru - return scanContinue - } - return s.error(c, "in literal true (expecting 'u')") -} - -// stateTru is the state after reading `tru`. -func stateTru(s *scanner, c byte) int { - if c == 'e' { - s.step = stateEndValue - return scanContinue - } - return s.error(c, "in literal true (expecting 'e')") -} - -// stateF is the state after reading `f`. -func stateF(s *scanner, c byte) int { - if c == 'a' { - s.step = stateFa - return scanContinue - } - return s.error(c, "in literal false (expecting 'a')") -} - -// stateFa is the state after reading `fa`. -func stateFa(s *scanner, c byte) int { - if c == 'l' { - s.step = stateFal - return scanContinue - } - return s.error(c, "in literal false (expecting 'l')") -} - -// stateFal is the state after reading `fal`. -func stateFal(s *scanner, c byte) int { - if c == 's' { - s.step = stateFals - return scanContinue - } - return s.error(c, "in literal false (expecting 's')") -} - -// stateFals is the state after reading `fals`. -func stateFals(s *scanner, c byte) int { - if c == 'e' { - s.step = stateEndValue - return scanContinue - } - return s.error(c, "in literal false (expecting 'e')") -} - -// stateN is the state after reading `n`. -func stateN(s *scanner, c byte) int { - if c == 'u' { - s.step = stateNu - return scanContinue - } - return s.error(c, "in literal null (expecting 'u')") -} - -// stateNu is the state after reading `nu`. -func stateNu(s *scanner, c byte) int { - if c == 'l' { - s.step = stateNul - return scanContinue - } - return s.error(c, "in literal null (expecting 'l')") -} - -// stateNul is the state after reading `nul`. -func stateNul(s *scanner, c byte) int { - if c == 'l' { - s.step = stateEndValue - return scanContinue - } - return s.error(c, "in literal null (expecting 'l')") -} - -// stateError is the state after reaching a syntax error, -// such as after reading `[1}` or `5.1.2`. -func stateError(s *scanner, c byte) int { - return scanError -} - -// error records an error and switches to the error state. -func (s *scanner) error(c byte, context string) int { - s.step = stateError - s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes} - return scanError -} - -// quoteChar formats c as a quoted character literal -func quoteChar(c byte) string { - // special cases - different from quoted strings - if c == '\'' { - return `'\''` - } - if c == '"' { - return `'"'` - } - - // use quoted string with different quotation marks - s := strconv.Quote(string(c)) - return "'" + s[1:len(s)-1] + "'" -} diff --git a/vendor/github.com/d5/tengo/stdlib/math.go b/vendor/github.com/d5/tengo/stdlib/math.go deleted file mode 100644 index 08d82bdf..00000000 --- a/vendor/github.com/d5/tengo/stdlib/math.go +++ /dev/null @@ -1,74 +0,0 @@ -package stdlib - -import ( - "math" - - "github.com/d5/tengo/objects" -) - -var mathModule = map[string]objects.Object{ - "e": &objects.Float{Value: math.E}, - "pi": &objects.Float{Value: math.Pi}, - "phi": &objects.Float{Value: math.Phi}, - "sqrt2": &objects.Float{Value: math.Sqrt2}, - "sqrtE": &objects.Float{Value: math.SqrtE}, - "sqrtPi": &objects.Float{Value: math.SqrtPi}, - "sqrtPhi": &objects.Float{Value: math.SqrtPhi}, - "ln2": &objects.Float{Value: math.Ln2}, - "log2E": &objects.Float{Value: math.Log2E}, - "ln10": &objects.Float{Value: math.Ln10}, - "log10E": &objects.Float{Value: math.Log10E}, - "abs": &objects.UserFunction{Name: "abs", Value: FuncAFRF(math.Abs)}, - "acos": &objects.UserFunction{Name: "acos", Value: FuncAFRF(math.Acos)}, - "acosh": &objects.UserFunction{Name: "acosh", Value: FuncAFRF(math.Acosh)}, - "asin": &objects.UserFunction{Name: "asin", Value: FuncAFRF(math.Asin)}, - "asinh": &objects.UserFunction{Name: "asinh", Value: FuncAFRF(math.Asinh)}, - "atan": &objects.UserFunction{Name: "atan", Value: FuncAFRF(math.Atan)}, - "atan2": &objects.UserFunction{Name: "atan2", Value: FuncAFFRF(math.Atan2)}, - "atanh": &objects.UserFunction{Name: "atanh", Value: FuncAFRF(math.Atanh)}, - "cbrt": &objects.UserFunction{Name: "cbrt", Value: FuncAFRF(math.Cbrt)}, - "ceil": &objects.UserFunction{Name: "ceil", Value: FuncAFRF(math.Ceil)}, - "copysign": &objects.UserFunction{Name: "copysign", Value: FuncAFFRF(math.Copysign)}, - "cos": &objects.UserFunction{Name: "cos", Value: FuncAFRF(math.Cos)}, - "cosh": &objects.UserFunction{Name: "cosh", Value: FuncAFRF(math.Cosh)}, - "dim": &objects.UserFunction{Name: "dim", Value: FuncAFFRF(math.Dim)}, - "erf": &objects.UserFunction{Name: "erf", Value: FuncAFRF(math.Erf)}, - "erfc": &objects.UserFunction{Name: "erfc", Value: FuncAFRF(math.Erfc)}, - "exp": &objects.UserFunction{Name: "exp", Value: FuncAFRF(math.Exp)}, - "exp2": &objects.UserFunction{Name: "exp2", Value: FuncAFRF(math.Exp2)}, - "expm1": &objects.UserFunction{Name: "expm1", Value: FuncAFRF(math.Expm1)}, - "floor": &objects.UserFunction{Name: "floor", Value: FuncAFRF(math.Floor)}, - "gamma": &objects.UserFunction{Name: "gamma", Value: FuncAFRF(math.Gamma)}, - "hypot": &objects.UserFunction{Name: "hypot", Value: FuncAFFRF(math.Hypot)}, - "ilogb": &objects.UserFunction{Name: "ilogb", Value: FuncAFRI(math.Ilogb)}, - "inf": &objects.UserFunction{Name: "inf", Value: FuncAIRF(math.Inf)}, - "is_inf": &objects.UserFunction{Name: "is_inf", Value: FuncAFIRB(math.IsInf)}, - "is_nan": &objects.UserFunction{Name: "is_nan", Value: FuncAFRB(math.IsNaN)}, - "j0": &objects.UserFunction{Name: "j0", Value: FuncAFRF(math.J0)}, - "j1": &objects.UserFunction{Name: "j1", Value: FuncAFRF(math.J1)}, - "jn": &objects.UserFunction{Name: "jn", Value: FuncAIFRF(math.Jn)}, - "ldexp": &objects.UserFunction{Name: "ldexp", Value: FuncAFIRF(math.Ldexp)}, - "log": &objects.UserFunction{Name: "log", Value: FuncAFRF(math.Log)}, - "log10": &objects.UserFunction{Name: "log10", Value: FuncAFRF(math.Log10)}, - "log1p": &objects.UserFunction{Name: "log1p", Value: FuncAFRF(math.Log1p)}, - "log2": &objects.UserFunction{Name: "log2", Value: FuncAFRF(math.Log2)}, - "logb": &objects.UserFunction{Name: "logb", Value: FuncAFRF(math.Logb)}, - "max": &objects.UserFunction{Name: "max", Value: FuncAFFRF(math.Max)}, - "min": &objects.UserFunction{Name: "min", Value: FuncAFFRF(math.Min)}, - "mod": &objects.UserFunction{Name: "mod", Value: FuncAFFRF(math.Mod)}, - "nan": &objects.UserFunction{Name: "nan", Value: FuncARF(math.NaN)}, - "nextafter": &objects.UserFunction{Name: "nextafter", Value: FuncAFFRF(math.Nextafter)}, - "pow": &objects.UserFunction{Name: "pow", Value: FuncAFFRF(math.Pow)}, - "pow10": &objects.UserFunction{Name: "pow10", Value: FuncAIRF(math.Pow10)}, - "remainder": &objects.UserFunction{Name: "remainder", Value: FuncAFFRF(math.Remainder)}, - "signbit": &objects.UserFunction{Name: "signbit", Value: FuncAFRB(math.Signbit)}, - "sin": &objects.UserFunction{Name: "sin", Value: FuncAFRF(math.Sin)}, - "sinh": &objects.UserFunction{Name: "sinh", Value: FuncAFRF(math.Sinh)}, - "sqrt": &objects.UserFunction{Name: "sqrt", Value: FuncAFRF(math.Sqrt)}, - "tan": &objects.UserFunction{Name: "tan", Value: FuncAFRF(math.Tan)}, - "tanh": &objects.UserFunction{Name: "tanh", Value: FuncAFRF(math.Tanh)}, - "trunc": &objects.UserFunction{Name: "trunc", Value: FuncAFRF(math.Trunc)}, - "y0": &objects.UserFunction{Name: "y0", Value: FuncAFRF(math.Y0)}, - "y1": &objects.UserFunction{Name: "y1", Value: FuncAFRF(math.Y1)}, - "yn": &objects.UserFunction{Name: "yn", Value: FuncAIFRF(math.Yn)}, -} diff --git a/vendor/github.com/d5/tengo/stdlib/os.go b/vendor/github.com/d5/tengo/stdlib/os.go deleted file mode 100644 index a7890cc1..00000000 --- a/vendor/github.com/d5/tengo/stdlib/os.go +++ /dev/null @@ -1,492 +0,0 @@ -package stdlib - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - - "github.com/d5/tengo" - "github.com/d5/tengo/objects" -) - -var osModule = map[string]objects.Object{ - "o_rdonly": &objects.Int{Value: int64(os.O_RDONLY)}, - "o_wronly": &objects.Int{Value: int64(os.O_WRONLY)}, - "o_rdwr": &objects.Int{Value: int64(os.O_RDWR)}, - "o_append": &objects.Int{Value: int64(os.O_APPEND)}, - "o_create": &objects.Int{Value: int64(os.O_CREATE)}, - "o_excl": &objects.Int{Value: int64(os.O_EXCL)}, - "o_sync": &objects.Int{Value: int64(os.O_SYNC)}, - "o_trunc": &objects.Int{Value: int64(os.O_TRUNC)}, - "mode_dir": &objects.Int{Value: int64(os.ModeDir)}, - "mode_append": &objects.Int{Value: int64(os.ModeAppend)}, - "mode_exclusive": &objects.Int{Value: int64(os.ModeExclusive)}, - "mode_temporary": &objects.Int{Value: int64(os.ModeTemporary)}, - "mode_symlink": &objects.Int{Value: int64(os.ModeSymlink)}, - "mode_device": &objects.Int{Value: int64(os.ModeDevice)}, - "mode_named_pipe": &objects.Int{Value: int64(os.ModeNamedPipe)}, - "mode_socket": &objects.Int{Value: int64(os.ModeSocket)}, - "mode_setuid": &objects.Int{Value: int64(os.ModeSetuid)}, - "mode_setgui": &objects.Int{Value: int64(os.ModeSetgid)}, - "mode_char_device": &objects.Int{Value: int64(os.ModeCharDevice)}, - "mode_sticky": &objects.Int{Value: int64(os.ModeSticky)}, - "mode_type": &objects.Int{Value: int64(os.ModeType)}, - "mode_perm": &objects.Int{Value: int64(os.ModePerm)}, - "path_separator": &objects.Char{Value: os.PathSeparator}, - "path_list_separator": &objects.Char{Value: os.PathListSeparator}, - "dev_null": &objects.String{Value: os.DevNull}, - "seek_set": &objects.Int{Value: int64(io.SeekStart)}, - "seek_cur": &objects.Int{Value: int64(io.SeekCurrent)}, - "seek_end": &objects.Int{Value: int64(io.SeekEnd)}, - "args": &objects.UserFunction{Name: "args", Value: osArgs}, // args() => array(string) - "chdir": &objects.UserFunction{Name: "chdir", Value: FuncASRE(os.Chdir)}, // chdir(dir string) => error - "chmod": osFuncASFmRE("chmod", os.Chmod), // chmod(name string, mode int) => error - "chown": &objects.UserFunction{Name: "chown", Value: FuncASIIRE(os.Chown)}, // chown(name string, uid int, gid int) => error - "clearenv": &objects.UserFunction{Name: "clearenv", Value: FuncAR(os.Clearenv)}, // clearenv() - "environ": &objects.UserFunction{Name: "environ", Value: FuncARSs(os.Environ)}, // environ() => array(string) - "exit": &objects.UserFunction{Name: "exit", Value: FuncAIR(os.Exit)}, // exit(code int) - "expand_env": &objects.UserFunction{Name: "expand_env", Value: osExpandEnv}, // expand_env(s string) => string - "getegid": &objects.UserFunction{Name: "getegid", Value: FuncARI(os.Getegid)}, // getegid() => int - "getenv": &objects.UserFunction{Name: "getenv", Value: FuncASRS(os.Getenv)}, // getenv(s string) => string - "geteuid": &objects.UserFunction{Name: "geteuid", Value: FuncARI(os.Geteuid)}, // geteuid() => int - "getgid": &objects.UserFunction{Name: "getgid", Value: FuncARI(os.Getgid)}, // getgid() => int - "getgroups": &objects.UserFunction{Name: "getgroups", Value: FuncARIsE(os.Getgroups)}, // getgroups() => array(string)/error - "getpagesize": &objects.UserFunction{Name: "getpagesize", Value: FuncARI(os.Getpagesize)}, // getpagesize() => int - "getpid": &objects.UserFunction{Name: "getpid", Value: FuncARI(os.Getpid)}, // getpid() => int - "getppid": &objects.UserFunction{Name: "getppid", Value: FuncARI(os.Getppid)}, // getppid() => int - "getuid": &objects.UserFunction{Name: "getuid", Value: FuncARI(os.Getuid)}, // getuid() => int - "getwd": &objects.UserFunction{Name: "getwd", Value: FuncARSE(os.Getwd)}, // getwd() => string/error - "hostname": &objects.UserFunction{Name: "hostname", Value: FuncARSE(os.Hostname)}, // hostname() => string/error - "lchown": &objects.UserFunction{Name: "lchown", Value: FuncASIIRE(os.Lchown)}, // lchown(name string, uid int, gid int) => error - "link": &objects.UserFunction{Name: "link", Value: FuncASSRE(os.Link)}, // link(oldname string, newname string) => error - "lookup_env": &objects.UserFunction{Name: "lookup_env", Value: osLookupEnv}, // lookup_env(key string) => string/false - "mkdir": osFuncASFmRE("mkdir", os.Mkdir), // mkdir(name string, perm int) => error - "mkdir_all": osFuncASFmRE("mkdir_all", os.MkdirAll), // mkdir_all(name string, perm int) => error - "readlink": &objects.UserFunction{Name: "readlink", Value: FuncASRSE(os.Readlink)}, // readlink(name string) => string/error - "remove": &objects.UserFunction{Name: "remove", Value: FuncASRE(os.Remove)}, // remove(name string) => error - "remove_all": &objects.UserFunction{Name: "remove_all", Value: FuncASRE(os.RemoveAll)}, // remove_all(name string) => error - "rename": &objects.UserFunction{Name: "rename", Value: FuncASSRE(os.Rename)}, // rename(oldpath string, newpath string) => error - "setenv": &objects.UserFunction{Name: "setenv", Value: FuncASSRE(os.Setenv)}, // setenv(key string, value string) => error - "symlink": &objects.UserFunction{Name: "symlink", Value: FuncASSRE(os.Symlink)}, // symlink(oldname string newname string) => error - "temp_dir": &objects.UserFunction{Name: "temp_dir", Value: FuncARS(os.TempDir)}, // temp_dir() => string - "truncate": &objects.UserFunction{Name: "truncate", Value: FuncASI64RE(os.Truncate)}, // truncate(name string, size int) => error - "unsetenv": &objects.UserFunction{Name: "unsetenv", Value: FuncASRE(os.Unsetenv)}, // unsetenv(key string) => error - "create": &objects.UserFunction{Name: "create", Value: osCreate}, // create(name string) => imap(file)/error - "open": &objects.UserFunction{Name: "open", Value: osOpen}, // open(name string) => imap(file)/error - "open_file": &objects.UserFunction{Name: "open_file", Value: osOpenFile}, // open_file(name string, flag int, perm int) => imap(file)/error - "find_process": &objects.UserFunction{Name: "find_process", Value: osFindProcess}, // find_process(pid int) => imap(process)/error - "start_process": &objects.UserFunction{Name: "start_process", Value: osStartProcess}, // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error - "exec_look_path": &objects.UserFunction{Name: "exec_look_path", Value: FuncASRSE(exec.LookPath)}, // exec_look_path(file) => string/error - "exec": &objects.UserFunction{Name: "exec", Value: osExec}, // exec(name, args...) => command - "stat": &objects.UserFunction{Name: "stat", Value: osStat}, // stat(name) => imap(fileinfo)/error - "read_file": &objects.UserFunction{Name: "read_file", Value: osReadFile}, // readfile(name) => array(byte)/error -} - -func osReadFile(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - fname, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - bytes, err := ioutil.ReadFile(fname) - if err != nil { - return wrapError(err), nil - } - - if len(bytes) > tengo.MaxBytesLen { - return nil, objects.ErrBytesLimit - } - - return &objects.Bytes{Value: bytes}, nil -} - -func osStat(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - fname, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - stat, err := os.Stat(fname) - if err != nil { - return wrapError(err), nil - } - - fstat := &objects.ImmutableMap{ - Value: map[string]objects.Object{ - "name": &objects.String{Value: stat.Name()}, - "mtime": &objects.Time{Value: stat.ModTime()}, - "size": &objects.Int{Value: stat.Size()}, - "mode": &objects.Int{Value: int64(stat.Mode())}, - }, - } - - if stat.IsDir() { - fstat.Value["directory"] = objects.TrueValue - } else { - fstat.Value["directory"] = objects.FalseValue - } - - return fstat, nil -} - -func osCreate(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := os.Create(s1) - if err != nil { - return wrapError(err), nil - } - - return makeOSFile(res), nil -} - -func osOpen(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := os.Open(s1) - if err != nil { - return wrapError(err), nil - } - - return makeOSFile(res), nil -} - -func osOpenFile(args ...objects.Object) (objects.Object, error) { - if len(args) != 3 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - } - - i3, ok := objects.ToInt(args[2]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - } - - res, err := os.OpenFile(s1, i2, os.FileMode(i3)) - if err != nil { - return wrapError(err), nil - } - - return makeOSFile(res), nil -} - -func osArgs(args ...objects.Object) (objects.Object, error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - arr := &objects.Array{} - for _, osArg := range os.Args { - if len(osArg) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: osArg}) - } - - return arr, nil -} - -func osFuncASFmRE(name string, fn func(string, os.FileMode) error) *objects.UserFunction { - return &objects.UserFunction{ - Name: name, - Value: func(args ...objects.Object) (objects.Object, error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - i2, ok := objects.ToInt64(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - } - - return wrapError(fn(s1, os.FileMode(i2))), nil - }, - } -} - -func osLookupEnv(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res, ok := os.LookupEnv(s1) - if !ok { - return objects.FalseValue, nil - } - - if len(res) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: res}, nil -} - -func osExpandEnv(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - var vlen int - var failed bool - s := os.Expand(s1, func(k string) string { - if failed { - return "" - } - - v := os.Getenv(k) - - // this does not count the other texts that are not being replaced - // but the code checks the final length at the end - vlen += len(v) - if vlen > tengo.MaxStringLen { - failed = true - return "" - } - - return v - }) - - if failed || len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil -} - -func osExec(args ...objects.Object) (objects.Object, error) { - if len(args) == 0 { - return nil, objects.ErrWrongNumArguments - } - - name, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - var execArgs []string - for idx, arg := range args[1:] { - execArg, ok := objects.ToString(arg) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: fmt.Sprintf("args[%d]", idx), - Expected: "string(compatible)", - Found: args[1+idx].TypeName(), - } - } - - execArgs = append(execArgs, execArg) - } - - return makeOSExecCommand(exec.Command(name, execArgs...)), nil -} - -func osFindProcess(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - proc, err := os.FindProcess(i1) - if err != nil { - return wrapError(err), nil - } - - return makeOSProcess(proc), nil -} - -func osStartProcess(args ...objects.Object) (objects.Object, error) { - if len(args) != 4 { - return nil, objects.ErrWrongNumArguments - } - - name, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - var argv []string - var err error - switch arg1 := args[1].(type) { - case *objects.Array: - argv, err = stringArray(arg1.Value, "second") - if err != nil { - return nil, err - } - case *objects.ImmutableArray: - argv, err = stringArray(arg1.Value, "second") - if err != nil { - return nil, err - } - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "array", - Found: arg1.TypeName(), - } - } - - dir, ok := objects.ToString(args[2]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "string(compatible)", - Found: args[2].TypeName(), - } - } - - var env []string - switch arg3 := args[3].(type) { - case *objects.Array: - env, err = stringArray(arg3.Value, "fourth") - if err != nil { - return nil, err - } - case *objects.ImmutableArray: - env, err = stringArray(arg3.Value, "fourth") - if err != nil { - return nil, err - } - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "fourth", - Expected: "array", - Found: arg3.TypeName(), - } - } - - proc, err := os.StartProcess(name, argv, &os.ProcAttr{ - Dir: dir, - Env: env, - }) - if err != nil { - return wrapError(err), nil - } - - return makeOSProcess(proc), nil -} - -func stringArray(arr []objects.Object, argName string) ([]string, error) { - var sarr []string - for idx, elem := range arr { - str, ok := elem.(*objects.String) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: fmt.Sprintf("%s[%d]", argName, idx), - Expected: "string", - Found: elem.TypeName(), - } - } - - sarr = append(sarr, str.Value) - } - - return sarr, nil -} diff --git a/vendor/github.com/d5/tengo/stdlib/os_exec.go b/vendor/github.com/d5/tengo/stdlib/os_exec.go deleted file mode 100644 index 5274c36a..00000000 --- a/vendor/github.com/d5/tengo/stdlib/os_exec.go +++ /dev/null @@ -1,113 +0,0 @@ -package stdlib - -import ( - "os/exec" - - "github.com/d5/tengo/objects" -) - -func makeOSExecCommand(cmd *exec.Cmd) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - // combined_output() => bytes/error - "combined_output": &objects.UserFunction{Name: "combined_output", Value: FuncARYE(cmd.CombinedOutput)}, // - // output() => bytes/error - "output": &objects.UserFunction{Name: "output", Value: FuncARYE(cmd.Output)}, // - // run() => error - "run": &objects.UserFunction{Name: "run", Value: FuncARE(cmd.Run)}, // - // start() => error - "start": &objects.UserFunction{Name: "start", Value: FuncARE(cmd.Start)}, // - // wait() => error - "wait": &objects.UserFunction{Name: "wait", Value: FuncARE(cmd.Wait)}, // - // set_path(path string) - "set_path": &objects.UserFunction{ - Name: "set_path", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - cmd.Path = s1 - - return objects.UndefinedValue, nil - }, - }, - // set_dir(dir string) - "set_dir": &objects.UserFunction{ - Name: "set_dir", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - cmd.Dir = s1 - - return objects.UndefinedValue, nil - }, - }, - // set_env(env array(string)) - "set_env": &objects.UserFunction{ - Name: "set_env", - Value: func(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - var env []string - var err error - switch arg0 := args[0].(type) { - case *objects.Array: - env, err = stringArray(arg0.Value, "first") - if err != nil { - return nil, err - } - case *objects.ImmutableArray: - env, err = stringArray(arg0.Value, "first") - if err != nil { - return nil, err - } - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "array", - Found: arg0.TypeName(), - } - } - - cmd.Env = env - - return objects.UndefinedValue, nil - }, - }, - // process() => imap(process) - "process": &objects.UserFunction{ - Name: "process", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - return makeOSProcess(cmd.Process), nil - }, - }, - }, - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/os_file.go b/vendor/github.com/d5/tengo/stdlib/os_file.go deleted file mode 100644 index ee9f625a..00000000 --- a/vendor/github.com/d5/tengo/stdlib/os_file.go +++ /dev/null @@ -1,96 +0,0 @@ -package stdlib - -import ( - "os" - - "github.com/d5/tengo/objects" -) - -func makeOSFile(file *os.File) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - // chdir() => true/error - "chdir": &objects.UserFunction{Name: "chdir", Value: FuncARE(file.Chdir)}, // - // chown(uid int, gid int) => true/error - "chown": &objects.UserFunction{Name: "chown", Value: FuncAIIRE(file.Chown)}, // - // close() => error - "close": &objects.UserFunction{Name: "close", Value: FuncARE(file.Close)}, // - // name() => string - "name": &objects.UserFunction{Name: "name", Value: FuncARS(file.Name)}, // - // readdirnames(n int) => array(string)/error - "readdirnames": &objects.UserFunction{Name: "readdirnames", Value: FuncAIRSsE(file.Readdirnames)}, // - // sync() => error - "sync": &objects.UserFunction{Name: "sync", Value: FuncARE(file.Sync)}, // - // write(bytes) => int/error - "write": &objects.UserFunction{Name: "write", Value: FuncAYRIE(file.Write)}, // - // write(string) => int/error - "write_string": &objects.UserFunction{Name: "write_string", Value: FuncASRIE(file.WriteString)}, // - // read(bytes) => int/error - "read": &objects.UserFunction{Name: "read", Value: FuncAYRIE(file.Read)}, // - // chmod(mode int) => error - "chmod": &objects.UserFunction{ - Name: "chmod", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - return wrapError(file.Chmod(os.FileMode(i1))), nil - }, - }, - // seek(offset int, whence int) => int/error - "seek": &objects.UserFunction{ - Name: "seek", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - i2, ok := objects.ToInt(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - } - - res, err := file.Seek(i1, i2) - if err != nil { - return wrapError(err), nil - } - - return &objects.Int{Value: res}, nil - }, - }, - // stat() => imap(fileinfo)/error - "stat": &objects.UserFunction{ - Name: "start", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - return osStat(&objects.String{Value: file.Name()}) - }, - }, - }, - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/os_process.go b/vendor/github.com/d5/tengo/stdlib/os_process.go deleted file mode 100644 index 801ccdef..00000000 --- a/vendor/github.com/d5/tengo/stdlib/os_process.go +++ /dev/null @@ -1,62 +0,0 @@ -package stdlib - -import ( - "os" - "syscall" - - "github.com/d5/tengo/objects" -) - -func makeOSProcessState(state *os.ProcessState) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - "exited": &objects.UserFunction{Name: "exited", Value: FuncARB(state.Exited)}, // - "pid": &objects.UserFunction{Name: "pid", Value: FuncARI(state.Pid)}, // - "string": &objects.UserFunction{Name: "string", Value: FuncARS(state.String)}, // - "success": &objects.UserFunction{Name: "success", Value: FuncARB(state.Success)}, // - }, - } -} - -func makeOSProcess(proc *os.Process) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - "kill": &objects.UserFunction{Name: "kill", Value: FuncARE(proc.Kill)}, // - "release": &objects.UserFunction{Name: "release", Value: FuncARE(proc.Release)}, // - "signal": &objects.UserFunction{ - Name: "signal", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - return wrapError(proc.Signal(syscall.Signal(i1))), nil - }, - }, - "wait": &objects.UserFunction{ - Name: "wait", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - state, err := proc.Wait() - if err != nil { - return wrapError(err), nil - } - - return makeOSProcessState(state), nil - }, - }, - }, - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/rand.go b/vendor/github.com/d5/tengo/stdlib/rand.go deleted file mode 100644 index 6efe1de8..00000000 --- a/vendor/github.com/d5/tengo/stdlib/rand.go +++ /dev/null @@ -1,102 +0,0 @@ -package stdlib - -import ( - "math/rand" - - "github.com/d5/tengo/objects" -) - -var randModule = map[string]objects.Object{ - "int": &objects.UserFunction{Name: "int", Value: FuncARI64(rand.Int63)}, - "float": &objects.UserFunction{Name: "float", Value: FuncARF(rand.Float64)}, - "intn": &objects.UserFunction{Name: "intn", Value: FuncAI64RI64(rand.Int63n)}, - "exp_float": &objects.UserFunction{Name: "exp_float", Value: FuncARF(rand.ExpFloat64)}, - "norm_float": &objects.UserFunction{Name: "norm_float", Value: FuncARF(rand.NormFloat64)}, - "perm": &objects.UserFunction{Name: "perm", Value: FuncAIRIs(rand.Perm)}, - "seed": &objects.UserFunction{Name: "seed", Value: FuncAI64R(rand.Seed)}, - "read": &objects.UserFunction{ - Name: "read", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - y1, ok := args[0].(*objects.Bytes) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes", - Found: args[0].TypeName(), - } - } - - res, err := rand.Read(y1.Value) - if err != nil { - ret = wrapError(err) - return - } - - return &objects.Int{Value: int64(res)}, nil - }, - }, - "rand": &objects.UserFunction{ - Name: "rand", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - src := rand.NewSource(i1) - - return randRand(rand.New(src)), nil - }, - }, -} - -func randRand(r *rand.Rand) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - "int": &objects.UserFunction{Name: "int", Value: FuncARI64(r.Int63)}, - "float": &objects.UserFunction{Name: "float", Value: FuncARF(r.Float64)}, - "intn": &objects.UserFunction{Name: "intn", Value: FuncAI64RI64(r.Int63n)}, - "exp_float": &objects.UserFunction{Name: "exp_float", Value: FuncARF(r.ExpFloat64)}, - "norm_float": &objects.UserFunction{Name: "norm_float", Value: FuncARF(r.NormFloat64)}, - "perm": &objects.UserFunction{Name: "perm", Value: FuncAIRIs(r.Perm)}, - "seed": &objects.UserFunction{Name: "seed", Value: FuncAI64R(r.Seed)}, - "read": &objects.UserFunction{ - Name: "read", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - y1, ok := args[0].(*objects.Bytes) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes", - Found: args[0].TypeName(), - } - } - - res, err := r.Read(y1.Value) - if err != nil { - ret = wrapError(err) - return - } - - return &objects.Int{Value: int64(res)}, nil - }, - }, - }, - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/source_modules.go b/vendor/github.com/d5/tengo/stdlib/source_modules.go deleted file mode 100644 index ca69d7d1..00000000 --- a/vendor/github.com/d5/tengo/stdlib/source_modules.go +++ /dev/null @@ -1,8 +0,0 @@ -// Code generated using gensrcmods.go; DO NOT EDIT. - -package stdlib - -// SourceModules are source type standard library modules. -var SourceModules = map[string]string{ - "enum": "is_enumerable := func(x) {\n return is_array(x) || is_map(x) || is_immutable_array(x) || is_immutable_map(x)\n}\n\nis_array_like := func(x) {\n return is_array(x) || is_immutable_array(x)\n}\n\nexport {\n // all returns true if the given function `fn` evaluates to a truthy value on\n // all of the items in `x`. It returns undefined if `x` is not enumerable.\n all: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if !fn(k, v) { return false }\n }\n\n return true\n },\n // any returns true if the given function `fn` evaluates to a truthy value on\n // any of the items in `x`. It returns undefined if `x` is not enumerable.\n any: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return true }\n }\n\n return false\n },\n // chunk returns an array of elements split into groups the length of size.\n // If `x` can't be split evenly, the final chunk will be the remaining elements.\n // It returns undefined if `x` is not array.\n chunk: func(x, size) {\n if !is_array_like(x) || !size { return undefined }\n\n numElements := len(x)\n if !numElements { return [] }\n\n res := []\n idx := 0\n for idx < numElements {\n res = append(res, x[idx:idx+size])\n idx += size\n }\n\n return res\n },\n // at returns an element at the given index (if `x` is array) or\n // key (if `x` is map). It returns undefined if `x` is not enumerable.\n at: func(x, key) {\n if !is_enumerable(x) { return undefined }\n\n if is_array_like(x) {\n if !is_int(key) { return undefined }\n } else {\n if !is_string(key) { return undefined }\n }\n\n return x[key]\n },\n // each iterates over elements of `x` and invokes `fn` for each element. `fn` is\n // invoked with two arguments: `key` and `value`. `key` is an int index\n // if `x` is array. `key` is a string key if `x` is map. It does not iterate\n // and returns undefined if `x` is not enumerable.\n each: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n fn(k, v)\n }\n },\n // filter iterates over elements of `x`, returning an array of all elements `fn`\n // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.\n // `key` is an int index if `x` is array. `key` is a string key if `x` is map.\n // It returns undefined if `x` is not enumerable.\n filter: func(x, fn) {\n if !is_array_like(x) { return undefined }\n\n dst := []\n for k, v in x {\n if fn(k, v) { dst = append(dst, v) }\n }\n\n return dst\n },\n // find iterates over elements of `x`, returning value of the first element `fn`\n // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.\n // `key` is an int index if `x` is array. `key` is a string key if `x` is map.\n // It returns undefined if `x` is not enumerable.\n find: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return v }\n }\n },\n // find_key iterates over elements of `x`, returning key or index of the first\n // element `fn` returns truthy for. `fn` is invoked with two arguments: `key`\n // and `value`. `key` is an int index if `x` is array. `key` is a string key if\n // `x` is map. It returns undefined if `x` is not enumerable.\n find_key: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return k }\n }\n },\n // map creates an array of values by running each element in `x` through `fn`.\n // `fn` is invoked with two arguments: `key` and `value`. `key` is an int index\n // if `x` is array. `key` is a string key if `x` is map. It returns undefined\n // if `x` is not enumerable.\n map: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n dst := []\n for k, v in x {\n dst = append(dst, fn(k, v))\n }\n\n return dst\n },\n // key returns the first argument.\n key: func(k, _) { return k },\n // value returns the second argument.\n value: func(_, v) { return v }\n}\n", -} diff --git a/vendor/github.com/d5/tengo/stdlib/srcmod_enum.tengo b/vendor/github.com/d5/tengo/stdlib/srcmod_enum.tengo deleted file mode 100644 index 7a5ea637..00000000 --- a/vendor/github.com/d5/tengo/stdlib/srcmod_enum.tengo +++ /dev/null @@ -1,128 +0,0 @@ -is_enumerable := func(x) { - return is_array(x) || is_map(x) || is_immutable_array(x) || is_immutable_map(x) -} - -is_array_like := func(x) { - return is_array(x) || is_immutable_array(x) -} - -export { - // all returns true if the given function `fn` evaluates to a truthy value on - // all of the items in `x`. It returns undefined if `x` is not enumerable. - all: func(x, fn) { - if !is_enumerable(x) { return undefined } - - for k, v in x { - if !fn(k, v) { return false } - } - - return true - }, - // any returns true if the given function `fn` evaluates to a truthy value on - // any of the items in `x`. It returns undefined if `x` is not enumerable. - any: func(x, fn) { - if !is_enumerable(x) { return undefined } - - for k, v in x { - if fn(k, v) { return true } - } - - return false - }, - // chunk returns an array of elements split into groups the length of size. - // If `x` can't be split evenly, the final chunk will be the remaining elements. - // It returns undefined if `x` is not array. - chunk: func(x, size) { - if !is_array_like(x) || !size { return undefined } - - numElements := len(x) - if !numElements { return [] } - - res := [] - idx := 0 - for idx < numElements { - res = append(res, x[idx:idx+size]) - idx += size - } - - return res - }, - // at returns an element at the given index (if `x` is array) or - // key (if `x` is map). It returns undefined if `x` is not enumerable. - at: func(x, key) { - if !is_enumerable(x) { return undefined } - - if is_array_like(x) { - if !is_int(key) { return undefined } - } else { - if !is_string(key) { return undefined } - } - - return x[key] - }, - // each iterates over elements of `x` and invokes `fn` for each element. `fn` is - // invoked with two arguments: `key` and `value`. `key` is an int index - // if `x` is array. `key` is a string key if `x` is map. It does not iterate - // and returns undefined if `x` is not enumerable. - each: func(x, fn) { - if !is_enumerable(x) { return undefined } - - for k, v in x { - fn(k, v) - } - }, - // filter iterates over elements of `x`, returning an array of all elements `fn` - // returns truthy for. `fn` is invoked with two arguments: `key` and `value`. - // `key` is an int index if `x` is array. `key` is a string key if `x` is map. - // It returns undefined if `x` is not enumerable. - filter: func(x, fn) { - if !is_array_like(x) { return undefined } - - dst := [] - for k, v in x { - if fn(k, v) { dst = append(dst, v) } - } - - return dst - }, - // find iterates over elements of `x`, returning value of the first element `fn` - // returns truthy for. `fn` is invoked with two arguments: `key` and `value`. - // `key` is an int index if `x` is array. `key` is a string key if `x` is map. - // It returns undefined if `x` is not enumerable. - find: func(x, fn) { - if !is_enumerable(x) { return undefined } - - for k, v in x { - if fn(k, v) { return v } - } - }, - // find_key iterates over elements of `x`, returning key or index of the first - // element `fn` returns truthy for. `fn` is invoked with two arguments: `key` - // and `value`. `key` is an int index if `x` is array. `key` is a string key if - // `x` is map. It returns undefined if `x` is not enumerable. - find_key: func(x, fn) { - if !is_enumerable(x) { return undefined } - - for k, v in x { - if fn(k, v) { return k } - } - }, - // map creates an array of values by running each element in `x` through `fn`. - // `fn` is invoked with two arguments: `key` and `value`. `key` is an int index - // if `x` is array. `key` is a string key if `x` is map. It returns undefined - // if `x` is not enumerable. - map: func(x, fn) { - if !is_enumerable(x) { return undefined } - - dst := [] - for k, v in x { - dst = append(dst, fn(k, v)) - } - - return dst - }, - // key returns the first argument. - key: func(k, _) { return k }, - // value returns the second argument. - value: func(_, v) { return v } -} diff --git a/vendor/github.com/d5/tengo/stdlib/stdlib.go b/vendor/github.com/d5/tengo/stdlib/stdlib.go deleted file mode 100644 index aad220ee..00000000 --- a/vendor/github.com/d5/tengo/stdlib/stdlib.go +++ /dev/null @@ -1,34 +0,0 @@ -package stdlib - -//go:generate go run gensrcmods.go - -import "github.com/d5/tengo/objects" - -// AllModuleNames returns a list of all default module names. -func AllModuleNames() []string { - var names []string - for name := range BuiltinModules { - names = append(names, name) - } - for name := range SourceModules { - names = append(names, name) - } - return names -} - -// GetModuleMap returns the module map that includes all modules -// for the given module names. -func GetModuleMap(names ...string) *objects.ModuleMap { - modules := objects.NewModuleMap() - - for _, name := range names { - if mod := BuiltinModules[name]; mod != nil { - modules.AddBuiltinModule(name, mod) - } - if mod := SourceModules[name]; mod != "" { - modules.AddSourceModule(name, []byte(mod)) - } - } - - return modules -} diff --git a/vendor/github.com/d5/tengo/stdlib/text.go b/vendor/github.com/d5/tengo/stdlib/text.go deleted file mode 100644 index 4b5729ec..00000000 --- a/vendor/github.com/d5/tengo/stdlib/text.go +++ /dev/null @@ -1,930 +0,0 @@ -package stdlib - -import ( - "fmt" - "regexp" - "strconv" - "strings" - "unicode/utf8" - - "github.com/d5/tengo" - "github.com/d5/tengo/objects" -) - -var textModule = map[string]objects.Object{ - "re_match": &objects.UserFunction{Name: "re_match", Value: textREMatch}, // re_match(pattern, text) => bool/error - "re_find": &objects.UserFunction{Name: "re_find", Value: textREFind}, // re_find(pattern, text, count) => [[{text:,begin:,end:}]]/undefined - "re_replace": &objects.UserFunction{Name: "re_replace", Value: textREReplace}, // re_replace(pattern, text, repl) => string/error - "re_split": &objects.UserFunction{Name: "re_split", Value: textRESplit}, // re_split(pattern, text, count) => [string]/error - "re_compile": &objects.UserFunction{Name: "re_compile", Value: textRECompile}, // re_compile(pattern) => Regexp/error - "compare": &objects.UserFunction{Name: "compare", Value: FuncASSRI(strings.Compare)}, // compare(a, b) => int - "contains": &objects.UserFunction{Name: "contains", Value: FuncASSRB(strings.Contains)}, // contains(s, substr) => bool - "contains_any": &objects.UserFunction{Name: "contains_any", Value: FuncASSRB(strings.ContainsAny)}, // contains_any(s, chars) => bool - "count": &objects.UserFunction{Name: "count", Value: FuncASSRI(strings.Count)}, // count(s, substr) => int - "equal_fold": &objects.UserFunction{Name: "equal_fold", Value: FuncASSRB(strings.EqualFold)}, // "equal_fold(s, t) => bool - "fields": &objects.UserFunction{Name: "fields", Value: FuncASRSs(strings.Fields)}, // fields(s) => [string] - "has_prefix": &objects.UserFunction{Name: "has_prefix", Value: FuncASSRB(strings.HasPrefix)}, // has_prefix(s, prefix) => bool - "has_suffix": &objects.UserFunction{Name: "has_suffix", Value: FuncASSRB(strings.HasSuffix)}, // has_suffix(s, suffix) => bool - "index": &objects.UserFunction{Name: "index", Value: FuncASSRI(strings.Index)}, // index(s, substr) => int - "index_any": &objects.UserFunction{Name: "index_any", Value: FuncASSRI(strings.IndexAny)}, // index_any(s, chars) => int - "join": &objects.UserFunction{Name: "join", Value: textJoin}, // join(arr, sep) => string - "last_index": &objects.UserFunction{Name: "last_index", Value: FuncASSRI(strings.LastIndex)}, // last_index(s, substr) => int - "last_index_any": &objects.UserFunction{Name: "last_index_any", Value: FuncASSRI(strings.LastIndexAny)}, // last_index_any(s, chars) => int - "repeat": &objects.UserFunction{Name: "repeat", Value: textRepeat}, // repeat(s, count) => string - "replace": &objects.UserFunction{Name: "replace", Value: textReplace}, // replace(s, old, new, n) => string - "substr": &objects.UserFunction{Name: "substr", Value: textSubstring}, // substr(s, lower, upper) => string - "split": &objects.UserFunction{Name: "split", Value: FuncASSRSs(strings.Split)}, // split(s, sep) => [string] - "split_after": &objects.UserFunction{Name: "split_after", Value: FuncASSRSs(strings.SplitAfter)}, // split_after(s, sep) => [string] - "split_after_n": &objects.UserFunction{Name: "split_after_n", Value: FuncASSIRSs(strings.SplitAfterN)}, // split_after_n(s, sep, n) => [string] - "split_n": &objects.UserFunction{Name: "split_n", Value: FuncASSIRSs(strings.SplitN)}, // split_n(s, sep, n) => [string] - "title": &objects.UserFunction{Name: "title", Value: FuncASRS(strings.Title)}, // title(s) => string - "to_lower": &objects.UserFunction{Name: "to_lower", Value: FuncASRS(strings.ToLower)}, // to_lower(s) => string - "to_title": &objects.UserFunction{Name: "to_title", Value: FuncASRS(strings.ToTitle)}, // to_title(s) => string - "to_upper": &objects.UserFunction{Name: "to_upper", Value: FuncASRS(strings.ToUpper)}, // to_upper(s) => string - "pad_left": &objects.UserFunction{Name: "pad_left", Value: textPadLeft}, // pad_left(s, pad_len, pad_with) => string - "pad_right": &objects.UserFunction{Name: "pad_right", Value: textPadRight}, // pad_right(s, pad_len, pad_with) => string - "trim": &objects.UserFunction{Name: "trim", Value: FuncASSRS(strings.Trim)}, // trim(s, cutset) => string - "trim_left": &objects.UserFunction{Name: "trim_left", Value: FuncASSRS(strings.TrimLeft)}, // trim_left(s, cutset) => string - "trim_prefix": &objects.UserFunction{Name: "trim_prefix", Value: FuncASSRS(strings.TrimPrefix)}, // trim_prefix(s, prefix) => string - "trim_right": &objects.UserFunction{Name: "trim_right", Value: FuncASSRS(strings.TrimRight)}, // trim_right(s, cutset) => string - "trim_space": &objects.UserFunction{Name: "trim_space", Value: FuncASRS(strings.TrimSpace)}, // trim_space(s) => string - "trim_suffix": &objects.UserFunction{Name: "trim_suffix", Value: FuncASSRS(strings.TrimSuffix)}, // trim_suffix(s, suffix) => string - "atoi": &objects.UserFunction{Name: "atoi", Value: FuncASRIE(strconv.Atoi)}, // atoi(str) => int/error - "format_bool": &objects.UserFunction{Name: "format_bool", Value: textFormatBool}, // format_bool(b) => string - "format_float": &objects.UserFunction{Name: "format_float", Value: textFormatFloat}, // format_float(f, fmt, prec, bits) => string - "format_int": &objects.UserFunction{Name: "format_int", Value: textFormatInt}, // format_int(i, base) => string - "itoa": &objects.UserFunction{Name: "itoa", Value: FuncAIRS(strconv.Itoa)}, // itoa(i) => string - "parse_bool": &objects.UserFunction{Name: "parse_bool", Value: textParseBool}, // parse_bool(str) => bool/error - "parse_float": &objects.UserFunction{Name: "parse_float", Value: textParseFloat}, // parse_float(str, bits) => float/error - "parse_int": &objects.UserFunction{Name: "parse_int", Value: textParseInt}, // parse_int(str, base, bits) => int/error - "quote": &objects.UserFunction{Name: "quote", Value: FuncASRS(strconv.Quote)}, // quote(str) => string - "unquote": &objects.UserFunction{Name: "unquote", Value: FuncASRSE(strconv.Unquote)}, // unquote(str) => string/error -} - -func textREMatch(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - return - } - - matched, err := regexp.MatchString(s1, s2) - if err != nil { - ret = wrapError(err) - return - } - - if matched { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return -} - -func textREFind(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs != 2 && numArgs != 3 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - re, err := regexp.Compile(s1) - if err != nil { - ret = wrapError(err) - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - return - } - - if numArgs < 3 { - m := re.FindStringSubmatchIndex(s2) - if m == nil { - ret = objects.UndefinedValue - return - } - - arr := &objects.Array{} - for i := 0; i < len(m); i += 2 { - arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ - "text": &objects.String{Value: s2[m[i]:m[i+1]]}, - "begin": &objects.Int{Value: int64(m[i])}, - "end": &objects.Int{Value: int64(m[i+1])}, - }}) - } - - ret = &objects.Array{Value: []objects.Object{arr}} - - return - } - - i3, ok := objects.ToInt(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - return - } - m := re.FindAllStringSubmatchIndex(s2, i3) - if m == nil { - ret = objects.UndefinedValue - return - } - - arr := &objects.Array{} - for _, m := range m { - subMatch := &objects.Array{} - for i := 0; i < len(m); i += 2 { - subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ - "text": &objects.String{Value: s2[m[i]:m[i+1]]}, - "begin": &objects.Int{Value: int64(m[i])}, - "end": &objects.Int{Value: int64(m[i+1])}, - }}) - } - - arr.Value = append(arr.Value, subMatch) - } - - ret = arr - - return -} - -func textREReplace(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 3 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - return - } - - s3, ok := objects.ToString(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "string(compatible)", - Found: args[2].TypeName(), - } - return - } - - re, err := regexp.Compile(s1) - if err != nil { - ret = wrapError(err) - } else { - s, ok := doTextRegexpReplace(re, s2, s3) - if !ok { - return nil, objects.ErrStringLimit - } - - ret = &objects.String{Value: s} - } - - return -} - -func textRESplit(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs != 2 && numArgs != 3 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - return - } - - var i3 = -1 - if numArgs > 2 { - i3, ok = objects.ToInt(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - return - } - } - - re, err := regexp.Compile(s1) - if err != nil { - ret = wrapError(err) - return - } - - arr := &objects.Array{} - for _, s := range re.Split(s2, i3) { - arr.Value = append(arr.Value, &objects.String{Value: s}) - } - - ret = arr - - return -} - -func textRECompile(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - re, err := regexp.Compile(s1) - if err != nil { - ret = wrapError(err) - } else { - ret = makeTextRegexp(re) - } - - return -} - -func textReplace(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 4 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - return - } - - s3, ok := objects.ToString(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "string(compatible)", - Found: args[2].TypeName(), - } - return - } - - i4, ok := objects.ToInt(args[3]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "fourth", - Expected: "int(compatible)", - Found: args[3].TypeName(), - } - return - } - - s, ok := doTextReplace(s1, s2, s3, i4) - if !ok { - err = objects.ErrStringLimit - return - } - - ret = &objects.String{Value: s} - - return -} - -func textSubstring(args ...objects.Object) (ret objects.Object, err error) { - argslen := len(args) - if argslen != 2 && argslen != 3 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - strlen := len(s1) - i3 := strlen - if argslen == 3 { - i3, ok = objects.ToInt(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - return - } - } - - if i2 > i3 { - err = objects.ErrInvalidIndexType - return - } - - if i2 < 0 { - i2 = 0 - } else if i2 > strlen { - i2 = strlen - } - - if i3 < 0 { - i3 = 0 - } else if i3 > strlen { - i3 = strlen - } - - ret = &objects.String{Value: s1[i2:i3]} - - return -} - -func textPadLeft(args ...objects.Object) (ret objects.Object, err error) { - argslen := len(args) - if argslen != 2 && argslen != 3 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - if i2 > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - sLen := len(s1) - if sLen >= i2 { - ret = &objects.String{Value: s1} - return - } - - s3 := " " - if argslen == 3 { - s3, ok = objects.ToString(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "string(compatible)", - Found: args[2].TypeName(), - } - return - } - } - - padStrLen := len(s3) - if padStrLen == 0 { - ret = &objects.String{Value: s1} - return - } - - padCount := ((i2 - padStrLen) / padStrLen) + 1 - retStr := strings.Repeat(s3, int(padCount)) + s1 - ret = &objects.String{Value: retStr[len(retStr)-i2:]} - - return -} - -func textPadRight(args ...objects.Object) (ret objects.Object, err error) { - argslen := len(args) - if argslen != 2 && argslen != 3 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - if i2 > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - sLen := len(s1) - if sLen >= i2 { - ret = &objects.String{Value: s1} - return - } - - s3 := " " - if argslen == 3 { - s3, ok = objects.ToString(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "string(compatible)", - Found: args[2].TypeName(), - } - return - } - } - - padStrLen := len(s3) - if padStrLen == 0 { - ret = &objects.String{Value: s1} - return - } - - padCount := ((i2 - padStrLen) / padStrLen) + 1 - retStr := s1 + strings.Repeat(s3, int(padCount)) - ret = &objects.String{Value: retStr[:i2]} - - return -} - -func textRepeat(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - s1, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - } - - if len(s1)*i2 > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: strings.Repeat(s1, i2)}, nil -} - -func textJoin(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - var slen int - var ss1 []string - switch arg0 := args[0].(type) { - case *objects.Array: - for idx, a := range arg0.Value { - as, ok := objects.ToString(a) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: fmt.Sprintf("first[%d]", idx), - Expected: "string(compatible)", - Found: a.TypeName(), - } - } - slen += len(as) - ss1 = append(ss1, as) - } - case *objects.ImmutableArray: - for idx, a := range arg0.Value { - as, ok := objects.ToString(a) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: fmt.Sprintf("first[%d]", idx), - Expected: "string(compatible)", - Found: a.TypeName(), - } - } - slen += len(as) - ss1 = append(ss1, as) - } - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "array", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - // make sure output length does not exceed the limit - if slen+len(s2)*(len(ss1)-1) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: strings.Join(ss1, s2)}, nil -} - -func textFormatBool(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - b1, ok := args[0].(*objects.Bool) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bool", - Found: args[0].TypeName(), - } - return - } - - if b1 == objects.TrueValue { - ret = &objects.String{Value: "true"} - } else { - ret = &objects.String{Value: "false"} - } - - return -} - -func textFormatFloat(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 4 { - err = objects.ErrWrongNumArguments - return - } - - f1, ok := args[0].(*objects.Float) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "float", - Found: args[0].TypeName(), - } - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - return - } - - i3, ok := objects.ToInt(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - return - } - - i4, ok := objects.ToInt(args[3]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "fourth", - Expected: "int(compatible)", - Found: args[3].TypeName(), - } - return - } - - ret = &objects.String{Value: strconv.FormatFloat(f1.Value, s2[0], i3, i4)} - - return -} - -func textFormatInt(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := args[0].(*objects.Int) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - ret = &objects.String{Value: strconv.FormatInt(i1.Value, i2)} - - return -} - -func textParseBool(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := args[0].(*objects.String) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string", - Found: args[0].TypeName(), - } - return - } - - parsed, err := strconv.ParseBool(s1.Value) - if err != nil { - ret = wrapError(err) - return - } - - if parsed { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return -} - -func textParseFloat(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := args[0].(*objects.String) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - parsed, err := strconv.ParseFloat(s1.Value, i2) - if err != nil { - ret = wrapError(err) - return - } - - ret = &objects.Float{Value: parsed} - - return -} - -func textParseInt(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 3 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := args[0].(*objects.String) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - i3, ok := objects.ToInt(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - return - } - - parsed, err := strconv.ParseInt(s1.Value, i2, i3) - if err != nil { - ret = wrapError(err) - return - } - - ret = &objects.Int{Value: parsed} - - return -} - -// Modified implementation of strings.Replace -// to limit the maximum length of output string. -func doTextReplace(s, old, new string, n int) (string, bool) { - if old == new || n == 0 { - return s, true // avoid allocation - } - - // Compute number of replacements. - if m := strings.Count(s, old); m == 0 { - return s, true // avoid allocation - } else if n < 0 || m < n { - n = m - } - - // Apply replacements to buffer. - t := make([]byte, len(s)+n*(len(new)-len(old))) - w := 0 - start := 0 - for i := 0; i < n; i++ { - j := start - if len(old) == 0 { - if i > 0 { - _, wid := utf8.DecodeRuneInString(s[start:]) - j += wid - } - } else { - j += strings.Index(s[start:], old) - } - - ssj := s[start:j] - if w+len(ssj)+len(new) > tengo.MaxStringLen { - return "", false - } - - w += copy(t[w:], ssj) - w += copy(t[w:], new) - start = j + len(old) - } - - ss := s[start:] - if w+len(ss) > tengo.MaxStringLen { - return "", false - } - - w += copy(t[w:], ss) - - return string(t[0:w]), true -} diff --git a/vendor/github.com/d5/tengo/stdlib/text_regexp.go b/vendor/github.com/d5/tengo/stdlib/text_regexp.go deleted file mode 100644 index 16f135bf..00000000 --- a/vendor/github.com/d5/tengo/stdlib/text_regexp.go +++ /dev/null @@ -1,228 +0,0 @@ -package stdlib - -import ( - "regexp" - - "github.com/d5/tengo" - "github.com/d5/tengo/objects" -) - -func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - // match(text) => bool - "match": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - if re.MatchString(s1) { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return - }, - }, - - // find(text) => array(array({text:,begin:,end:}))/undefined - // find(text, maxCount) => array(array({text:,begin:,end:}))/undefined - "find": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs != 1 && numArgs != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - if numArgs == 1 { - m := re.FindStringSubmatchIndex(s1) - if m == nil { - ret = objects.UndefinedValue - return - } - - arr := &objects.Array{} - for i := 0; i < len(m); i += 2 { - arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ - "text": &objects.String{Value: s1[m[i]:m[i+1]]}, - "begin": &objects.Int{Value: int64(m[i])}, - "end": &objects.Int{Value: int64(m[i+1])}, - }}) - } - - ret = &objects.Array{Value: []objects.Object{arr}} - - return - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - m := re.FindAllStringSubmatchIndex(s1, i2) - if m == nil { - ret = objects.UndefinedValue - return - } - - arr := &objects.Array{} - for _, m := range m { - subMatch := &objects.Array{} - for i := 0; i < len(m); i += 2 { - subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ - "text": &objects.String{Value: s1[m[i]:m[i+1]]}, - "begin": &objects.Int{Value: int64(m[i])}, - "end": &objects.Int{Value: int64(m[i+1])}, - }}) - } - - arr.Value = append(arr.Value, subMatch) - } - - ret = arr - - return - }, - }, - - // replace(src, repl) => string - "replace": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - return - } - - s, ok := doTextRegexpReplace(re, s1, s2) - if !ok { - return nil, objects.ErrStringLimit - } - - ret = &objects.String{Value: s} - - return - }, - }, - - // split(text) => array(string) - // split(text, maxCount) => array(string) - "split": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs != 1 && numArgs != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - var i2 = -1 - if numArgs > 1 { - i2, ok = objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - } - - arr := &objects.Array{} - for _, s := range re.Split(s1, i2) { - arr.Value = append(arr.Value, &objects.String{Value: s}) - } - - ret = arr - - return - }, - }, - }, - } -} - -// Size-limit checking implementation of regexp.ReplaceAllString. -func doTextRegexpReplace(re *regexp.Regexp, src, repl string) (string, bool) { - idx := 0 - out := "" - - for _, m := range re.FindAllStringSubmatchIndex(src, -1) { - var exp []byte - exp = re.ExpandString(exp, repl, src, m) - - if len(out)+m[0]-idx+len(exp) > tengo.MaxStringLen { - return "", false - } - - out += src[idx:m[0]] + string(exp) - idx = m[1] - } - if idx < len(src) { - if len(out)+len(src)-idx > tengo.MaxStringLen { - return "", false - } - - out += src[idx:] - } - - return string(out), true -} diff --git a/vendor/github.com/d5/tengo/stdlib/times.go b/vendor/github.com/d5/tengo/stdlib/times.go deleted file mode 100644 index 111c877a..00000000 --- a/vendor/github.com/d5/tengo/stdlib/times.go +++ /dev/null @@ -1,989 +0,0 @@ -package stdlib - -import ( - "time" - - "github.com/d5/tengo" - "github.com/d5/tengo/objects" -) - -var timesModule = map[string]objects.Object{ - "format_ansic": &objects.String{Value: time.ANSIC}, - "format_unix_date": &objects.String{Value: time.UnixDate}, - "format_ruby_date": &objects.String{Value: time.RubyDate}, - "format_rfc822": &objects.String{Value: time.RFC822}, - "format_rfc822z": &objects.String{Value: time.RFC822Z}, - "format_rfc850": &objects.String{Value: time.RFC850}, - "format_rfc1123": &objects.String{Value: time.RFC1123}, - "format_rfc1123z": &objects.String{Value: time.RFC1123Z}, - "format_rfc3339": &objects.String{Value: time.RFC3339}, - "format_rfc3339_nano": &objects.String{Value: time.RFC3339Nano}, - "format_kitchen": &objects.String{Value: time.Kitchen}, - "format_stamp": &objects.String{Value: time.Stamp}, - "format_stamp_milli": &objects.String{Value: time.StampMilli}, - "format_stamp_micro": &objects.String{Value: time.StampMicro}, - "format_stamp_nano": &objects.String{Value: time.StampNano}, - "nanosecond": &objects.Int{Value: int64(time.Nanosecond)}, - "microsecond": &objects.Int{Value: int64(time.Microsecond)}, - "millisecond": &objects.Int{Value: int64(time.Millisecond)}, - "second": &objects.Int{Value: int64(time.Second)}, - "minute": &objects.Int{Value: int64(time.Minute)}, - "hour": &objects.Int{Value: int64(time.Hour)}, - "january": &objects.Int{Value: int64(time.January)}, - "february": &objects.Int{Value: int64(time.February)}, - "march": &objects.Int{Value: int64(time.March)}, - "april": &objects.Int{Value: int64(time.April)}, - "may": &objects.Int{Value: int64(time.May)}, - "june": &objects.Int{Value: int64(time.June)}, - "july": &objects.Int{Value: int64(time.July)}, - "august": &objects.Int{Value: int64(time.August)}, - "september": &objects.Int{Value: int64(time.September)}, - "october": &objects.Int{Value: int64(time.October)}, - "november": &objects.Int{Value: int64(time.November)}, - "december": &objects.Int{Value: int64(time.December)}, - "sleep": &objects.UserFunction{Name: "sleep", Value: timesSleep}, // sleep(int) - "parse_duration": &objects.UserFunction{Name: "parse_duration", Value: timesParseDuration}, // parse_duration(str) => int - "since": &objects.UserFunction{Name: "since", Value: timesSince}, // since(time) => int - "until": &objects.UserFunction{Name: "until", Value: timesUntil}, // until(time) => int - "duration_hours": &objects.UserFunction{Name: "duration_hours", Value: timesDurationHours}, // duration_hours(int) => float - "duration_minutes": &objects.UserFunction{Name: "duration_minutes", Value: timesDurationMinutes}, // duration_minutes(int) => float - "duration_nanoseconds": &objects.UserFunction{Name: "duration_nanoseconds", Value: timesDurationNanoseconds}, // duration_nanoseconds(int) => int - "duration_seconds": &objects.UserFunction{Name: "duration_seconds", Value: timesDurationSeconds}, // duration_seconds(int) => float - "duration_string": &objects.UserFunction{Name: "duration_string", Value: timesDurationString}, // duration_string(int) => string - "month_string": &objects.UserFunction{Name: "month_string", Value: timesMonthString}, // month_string(int) => string - "date": &objects.UserFunction{Name: "date", Value: timesDate}, // date(year, month, day, hour, min, sec, nsec) => time - "now": &objects.UserFunction{Name: "now", Value: timesNow}, // now() => time - "parse": &objects.UserFunction{Name: "parse", Value: timesParse}, // parse(format, str) => time - "unix": &objects.UserFunction{Name: "unix", Value: timesUnix}, // unix(sec, nsec) => time - "add": &objects.UserFunction{Name: "add", Value: timesAdd}, // add(time, int) => time - "add_date": &objects.UserFunction{Name: "add_date", Value: timesAddDate}, // add_date(time, years, months, days) => time - "sub": &objects.UserFunction{Name: "sub", Value: timesSub}, // sub(t time, u time) => int - "after": &objects.UserFunction{Name: "after", Value: timesAfter}, // after(t time, u time) => bool - "before": &objects.UserFunction{Name: "before", Value: timesBefore}, // before(t time, u time) => bool - "time_year": &objects.UserFunction{Name: "time_year", Value: timesTimeYear}, // time_year(time) => int - "time_month": &objects.UserFunction{Name: "time_month", Value: timesTimeMonth}, // time_month(time) => int - "time_day": &objects.UserFunction{Name: "time_day", Value: timesTimeDay}, // time_day(time) => int - "time_weekday": &objects.UserFunction{Name: "time_weekday", Value: timesTimeWeekday}, // time_weekday(time) => int - "time_hour": &objects.UserFunction{Name: "time_hour", Value: timesTimeHour}, // time_hour(time) => int - "time_minute": &objects.UserFunction{Name: "time_minute", Value: timesTimeMinute}, // time_minute(time) => int - "time_second": &objects.UserFunction{Name: "time_second", Value: timesTimeSecond}, // time_second(time) => int - "time_nanosecond": &objects.UserFunction{Name: "time_nanosecond", Value: timesTimeNanosecond}, // time_nanosecond(time) => int - "time_unix": &objects.UserFunction{Name: "time_unix", Value: timesTimeUnix}, // time_unix(time) => int - "time_unix_nano": &objects.UserFunction{Name: "time_unix_nano", Value: timesTimeUnixNano}, // time_unix_nano(time) => int - "time_format": &objects.UserFunction{Name: "time_format", Value: timesTimeFormat}, // time_format(time, format) => string - "time_location": &objects.UserFunction{Name: "time_location", Value: timesTimeLocation}, // time_location(time) => string - "time_string": &objects.UserFunction{Name: "time_string", Value: timesTimeString}, // time_string(time) => string - "is_zero": &objects.UserFunction{Name: "is_zero", Value: timesIsZero}, // is_zero(time) => bool - "to_local": &objects.UserFunction{Name: "to_local", Value: timesToLocal}, // to_local(time) => time - "to_utc": &objects.UserFunction{Name: "to_utc", Value: timesToUTC}, // to_utc(time) => time -} - -func timesSleep(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - time.Sleep(time.Duration(i1)) - ret = objects.UndefinedValue - - return -} - -func timesParseDuration(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - dur, err := time.ParseDuration(s1) - if err != nil { - ret = wrapError(err) - return - } - - ret = &objects.Int{Value: int64(dur)} - - return -} - -func timesSince(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(time.Since(t1))} - - return -} - -func timesUntil(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(time.Until(t1))} - - return -} - -func timesDurationHours(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Float{Value: time.Duration(i1).Hours()} - - return -} - -func timesDurationMinutes(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Float{Value: time.Duration(i1).Minutes()} - - return -} - -func timesDurationNanoseconds(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: time.Duration(i1).Nanoseconds()} - - return -} - -func timesDurationSeconds(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Float{Value: time.Duration(i1).Seconds()} - - return -} - -func timesDurationString(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.String{Value: time.Duration(i1).String()} - - return -} - -func timesMonthString(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.String{Value: time.Month(i1).String()} - - return -} - -func timesDate(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 7 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - i3, ok := objects.ToInt(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - return - } - i4, ok := objects.ToInt(args[3]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "fourth", - Expected: "int(compatible)", - Found: args[3].TypeName(), - } - return - } - i5, ok := objects.ToInt(args[4]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "fifth", - Expected: "int(compatible)", - Found: args[4].TypeName(), - } - return - } - i6, ok := objects.ToInt(args[5]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "sixth", - Expected: "int(compatible)", - Found: args[5].TypeName(), - } - return - } - i7, ok := objects.ToInt(args[6]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "seventh", - Expected: "int(compatible)", - Found: args[6].TypeName(), - } - return - } - - ret = &objects.Time{Value: time.Date(i1, time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location())} - - return -} - -func timesNow(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - err = objects.ErrWrongNumArguments - return - } - - ret = &objects.Time{Value: time.Now()} - - return -} - -func timesParse(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - return - } - - parsed, err := time.Parse(s1, s2) - if err != nil { - ret = wrapError(err) - return - } - - ret = &objects.Time{Value: parsed} - - return -} - -func timesUnix(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt64(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - ret = &objects.Time{Value: time.Unix(i1, i2)} - - return -} - -func timesAdd(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt64(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - ret = &objects.Time{Value: t1.Add(time.Duration(i2))} - - return -} - -func timesSub(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - t2, ok := objects.ToTime(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "time(compatible)", - Found: args[1].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Sub(t2))} - - return -} - -func timesAddDate(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 4 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - i3, ok := objects.ToInt(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - return - } - - i4, ok := objects.ToInt(args[3]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "fourth", - Expected: "int(compatible)", - Found: args[3].TypeName(), - } - return - } - - ret = &objects.Time{Value: t1.AddDate(i2, i3, i4)} - - return -} - -func timesAfter(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - t2, ok := objects.ToTime(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "time(compatible)", - Found: args[1].TypeName(), - } - return - } - - if t1.After(t2) { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return -} - -func timesBefore(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - t2, ok := objects.ToTime(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - if t1.Before(t2) { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return -} - -func timesTimeYear(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Year())} - - return -} - -func timesTimeMonth(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Month())} - - return -} - -func timesTimeDay(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Day())} - - return -} - -func timesTimeWeekday(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Weekday())} - - return -} - -func timesTimeHour(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Hour())} - - return -} - -func timesTimeMinute(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Minute())} - - return -} - -func timesTimeSecond(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Second())} - - return -} - -func timesTimeNanosecond(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Nanosecond())} - - return -} - -func timesTimeUnix(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Unix())} - - return -} - -func timesTimeUnixNano(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.UnixNano())} - - return -} - -func timesTimeFormat(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - return - } - - s := t1.Format(s2) - if len(s) > tengo.MaxStringLen { - - return nil, objects.ErrStringLimit - } - - ret = &objects.String{Value: s} - - return -} - -func timesIsZero(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - if t1.IsZero() { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return -} - -func timesToLocal(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Time{Value: t1.Local()} - - return -} - -func timesToUTC(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Time{Value: t1.UTC()} - - return -} - -func timesTimeLocation(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.String{Value: t1.Location().String()} - - return -} - -func timesTimeString(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.String{Value: t1.String()} - - return -} diff --git a/vendor/github.com/d5/tengo/tengo.go b/vendor/github.com/d5/tengo/tengo.go deleted file mode 100644 index a883bbd7..00000000 --- a/vendor/github.com/d5/tengo/tengo.go +++ /dev/null @@ -1,11 +0,0 @@ -package tengo - -var ( - // MaxStringLen is the maximum byte-length for string value. - // Note this limit applies to all compiler/VM instances in the process. - MaxStringLen = 2147483647 - - // MaxBytesLen is the maximum length for bytes value. - // Note this limit applies to all compiler/VM instances in the process. - MaxBytesLen = 2147483647 -) diff --git a/vendor/github.com/d5/tengo/v2/.gitignore b/vendor/github.com/d5/tengo/v2/.gitignore new file mode 100644 index 00000000..77738287 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/.gitignore @@ -0,0 +1 @@ +dist/ \ No newline at end of file diff --git a/vendor/github.com/d5/tengo/v2/.goreleaser.yml b/vendor/github.com/d5/tengo/v2/.goreleaser.yml new file mode 100644 index 00000000..1bd14324 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/.goreleaser.yml @@ -0,0 +1,20 @@ +env: + - GO111MODULE=on +before: + hooks: + - go mod tidy +builds: + - env: + - CGO_ENABLED=0 + main: ./cmd/tengo/main.go + goos: + - darwin + - linux + - windows +archive: + files: + - none* +checksum: + name_template: 'checksums.txt' +changelog: + sort: asc diff --git a/vendor/github.com/d5/tengo/v2/LICENSE b/vendor/github.com/d5/tengo/v2/LICENSE new file mode 100644 index 00000000..2516341a --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Daniel Kang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/d5/tengo/v2/Makefile b/vendor/github.com/d5/tengo/v2/Makefile new file mode 100644 index 00000000..793bc129 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/Makefile @@ -0,0 +1,11 @@ +generate: + go generate ./... + +lint: + golint -set_exit_status ./... + +test: generate lint + go test -race -cover ./... + +fmt: + go fmt ./... diff --git a/vendor/github.com/d5/tengo/v2/README.md b/vendor/github.com/d5/tengo/v2/README.md new file mode 100644 index 00000000..92277cfe --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/README.md @@ -0,0 +1,135 @@ +

+ +

+ +# The Tengo Language + +[![GoDoc](https://godoc.org/github.com/d5/tengo?status.svg)](https://godoc.org/github.com/d5/tengo) +[![Go Report Card](https://goreportcard.com/badge/github.com/d5/tengo)](https://goreportcard.com/report/github.com/d5/tengo) +[![CircleCI](https://circleci.com/gh/d5/tengo.svg?style=svg)](https://circleci.com/gh/d5/tengo) +[![Sourcegraph](https://sourcegraph.com/github.com/d5/tengo/-/badge.svg)](https://sourcegraph.com/github.com/d5/tengo?badge) + +**Tengo is a small, dynamic, fast, secure script language for Go.** + +Tengo is **[fast](#benchmark)** and secure because it's compiled/executed as +bytecode on stack-based VM that's written in native Go. + +```golang +/* The Tengo Language */ +fmt := import("fmt") + +each := func(seq, fn) { + for x in seq { fn(x) } +} + +sum := func(init, seq) { + each(seq, func(x) { init += x }) + return init +} + +fmt.println(sum(0, [1, 2, 3])) // "6" +fmt.println(sum("", [1, 2, 3])) // "123" +``` + +> Test this Tengo code in the +> [Tengo Playground](https://tengolang.com/?s=0c8d5d0d88f2795a7093d7f35ae12c3afa17bea3) + +## Features + +- Simple and highly readable + [Syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md) + - Dynamic typing with type coercion + - Higher-order functions and closures + - Immutable values +- [Securely Embeddable](https://github.com/d5/tengo/blob/master/docs/interoperability.md) + and [Extensible](https://github.com/d5/tengo/blob/master/docs/objects.md) +- Compiler/runtime written in native Go _(no external deps or cgo)_ +- Executable as a + [standalone](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) + language / REPL +- Use cases: rules engine, [state machine](https://github.com/d5/go-fsm), + data pipeline, [transpiler](https://github.com/d5/tengo2lua) + +## Benchmark + +| | fib(35) | fibt(35) | Type | +| :--- | ---: | ---: | :---: | +| Go | `48ms` | `3ms` | Go (native) | +| [**Tengo**](https://github.com/d5/tengo) | `2,349ms` | `5ms` | VM on Go | +| Lua | `1,416ms` | `3ms` | Lua (native) | +| [go-lua](https://github.com/Shopify/go-lua) | `4,402ms` | `5ms` | Lua VM on Go | +| [GopherLua](https://github.com/yuin/gopher-lua) | `4,023ms` | `5ms` | Lua VM on Go | +| Python | `2,588ms` | `26ms` | Python (native) | +| [starlark-go](https://github.com/google/starlark-go) | `11,126ms` | `6ms` | Python-like Interpreter on Go | +| [gpython](https://github.com/go-python/gpython) | `15,035ms` | `4ms` | Python Interpreter on Go | +| [goja](https://github.com/dop251/goja) | `5,089ms` | `5ms` | JS VM on Go | +| [otto](https://github.com/robertkrimen/otto) | `68,377ms` | `11ms` | JS Interpreter on Go | +| [Anko](https://github.com/mattn/anko) | `92,579ms` | `18ms` | Interpreter on Go | + +_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): +Fibonacci(35)_ +_* [fibt(35)](https://github.com/d5/tengobench/blob/master/code/fibtc.tengo): +[tail-call](https://en.wikipedia.org/wiki/Tail_call) version of Fibonacci(35)_ +_* **Go** does not read the source code from file, while all other cases do_ +_* See [here](https://github.com/d5/tengobench) for commands/codes used_ + +## Quick Start + +A simple Go example code that compiles/runs Tengo script code with some input/output values: + +```golang +package main + +import ( + "context" + "fmt" + + "github.com/d5/tengo/v2" +) + +func main() { + // Tengo script code + src := ` +each := func(seq, fn) { + for x in seq { fn(x) } +} + +sum := 0 +mul := 1 +each([a, b, c, d], func(x) { + sum += x + mul *= x +})` + + // create a new Script instance + script := tengo.NewScript([]byte(src)) + + // set values + _ = script.Add("a", 1) + _ = script.Add("b", 9) + _ = script.Add("c", 8) + _ = script.Add("d", 4) + + // run the script + compiled, err := script.RunContext(context.Background()) + if err != nil { + panic(err) + } + + // retrieve values + sum := compiled.Get("sum") + mul := compiled.Get("mul") + fmt.Println(sum, mul) // "22 288" +} +``` + +## References + +- [Language Syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md) +- [Object Types](https://github.com/d5/tengo/blob/master/docs/objects.md) +- [Runtime Types](https://github.com/d5/tengo/blob/master/docs/runtime-types.md) + and [Operators](https://github.com/d5/tengo/blob/master/docs/operators.md) +- [Builtin Functions](https://github.com/d5/tengo/blob/master/docs/builtins.md) +- [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md) +- [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) +- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md) diff --git a/vendor/github.com/d5/tengo/v2/builtins.go b/vendor/github.com/d5/tengo/v2/builtins.go new file mode 100644 index 00000000..87f7fc3d --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/builtins.go @@ -0,0 +1,502 @@ +package tengo + +var builtinFuncs = []*BuiltinFunction{ + { + Name: "len", + Value: builtinLen, + }, + { + Name: "copy", + Value: builtinCopy, + }, + { + Name: "append", + Value: builtinAppend, + }, + { + 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(), + } + } +} diff --git a/vendor/github.com/d5/tengo/v2/bytecode.go b/vendor/github.com/d5/tengo/v2/bytecode.go new file mode 100644 index 00000000..cfd0d0b5 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/bytecode.go @@ -0,0 +1,292 @@ +package tengo + +import ( + "encoding/gob" + "fmt" + "io" + "reflect" + + "github.com/d5/tengo/v2/parser" +) + +// Bytecode is a compiled instructions and constants. +type Bytecode struct { + FileSet *parser.SourceFileSet + MainFunction *CompiledFunction + Constants []Object +} + +// Encode writes Bytecode data to the writer. +func (b *Bytecode) Encode(w io.Writer) error { + enc := gob.NewEncoder(w) + if err := enc.Encode(b.FileSet); err != nil { + return err + } + if err := enc.Encode(b.MainFunction); err != nil { + return err + } + return enc.Encode(b.Constants) +} + +// CountObjects returns the number of objects found in Constants. +func (b *Bytecode) CountObjects() int { + n := 0 + for _, c := range b.Constants { + n += CountObjects(c) + } + return n +} + +// FormatInstructions returns human readable string representations of +// compiled instructions. +func (b *Bytecode) FormatInstructions() []string { + return FormatInstructions(b.MainFunction.Instructions, 0) +} + +// FormatConstants returns human readable string representations of +// compiled constants. +func (b *Bytecode) FormatConstants() (output []string) { + for cidx, cn := range b.Constants { + switch cn := cn.(type) { + case *CompiledFunction: + output = append(output, fmt.Sprintf( + "[% 3d] (Compiled Function|%p)", cidx, &cn)) + for _, l := range FormatInstructions(cn.Instructions, 0) { + output = append(output, fmt.Sprintf(" %s", l)) + } + default: + output = append(output, fmt.Sprintf("[% 3d] %s (%s|%p)", + cidx, cn, reflect.TypeOf(cn).Elem().Name(), &cn)) + } + } + return +} + +// Decode reads Bytecode data from the reader. +func (b *Bytecode) Decode(r io.Reader, modules *ModuleMap) error { + if modules == nil { + modules = NewModuleMap() + } + + dec := gob.NewDecoder(r) + if err := dec.Decode(&b.FileSet); err != nil { + return err + } + // TODO: files in b.FileSet.File does not have their 'set' field properly + // set to b.FileSet as it's private field and not serialized by gob + // encoder/decoder. + if err := dec.Decode(&b.MainFunction); err != nil { + return err + } + if err := dec.Decode(&b.Constants); err != nil { + return err + } + for i, v := range b.Constants { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return err + } + b.Constants[i] = fv + } + return nil +} + +// RemoveDuplicates finds and remove the duplicate values in Constants. +// Note this function mutates Bytecode. +func (b *Bytecode) RemoveDuplicates() { + var deduped []Object + + indexMap := make(map[int]int) // mapping from old constant index to new index + ints := make(map[int64]int) + strings := make(map[string]int) + floats := make(map[float64]int) + chars := make(map[rune]int) + immutableMaps := make(map[string]int) // for modules + + for curIdx, c := range b.Constants { + switch c := c.(type) { + case *CompiledFunction: + // add to deduped list + indexMap[curIdx] = len(deduped) + deduped = append(deduped, c) + case *ImmutableMap: + modName := inferModuleName(c) + newIdx, ok := immutableMaps[modName] + if modName != "" && ok { + indexMap[curIdx] = newIdx + } else { + newIdx = len(deduped) + immutableMaps[modName] = newIdx + indexMap[curIdx] = newIdx + deduped = append(deduped, c) + } + case *Int: + if newIdx, ok := ints[c.Value]; ok { + indexMap[curIdx] = newIdx + } else { + newIdx = len(deduped) + ints[c.Value] = newIdx + indexMap[curIdx] = newIdx + deduped = append(deduped, c) + } + case *String: + if newIdx, ok := strings[c.Value]; ok { + indexMap[curIdx] = newIdx + } else { + newIdx = len(deduped) + strings[c.Value] = newIdx + indexMap[curIdx] = newIdx + deduped = append(deduped, c) + } + case *Float: + if newIdx, ok := floats[c.Value]; ok { + indexMap[curIdx] = newIdx + } else { + newIdx = len(deduped) + floats[c.Value] = newIdx + indexMap[curIdx] = newIdx + deduped = append(deduped, c) + } + case *Char: + if newIdx, ok := chars[c.Value]; ok { + indexMap[curIdx] = newIdx + } else { + newIdx = len(deduped) + chars[c.Value] = newIdx + indexMap[curIdx] = newIdx + deduped = append(deduped, c) + } + default: + panic(fmt.Errorf("unsupported top-level constant type: %s", + c.TypeName())) + } + } + + // replace with de-duplicated constants + b.Constants = deduped + + // update CONST instructions with new indexes + // main function + updateConstIndexes(b.MainFunction.Instructions, indexMap) + // other compiled functions in constants + for _, c := range b.Constants { + switch c := c.(type) { + case *CompiledFunction: + updateConstIndexes(c.Instructions, indexMap) + } + } +} + +func fixDecodedObject( + o Object, + modules *ModuleMap, +) (Object, error) { + switch o := o.(type) { + case *Bool: + if o.IsFalsy() { + return FalseValue, nil + } + return TrueValue, nil + case *Undefined: + return UndefinedValue, nil + case *Array: + for i, v := range o.Value { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[i] = fv + } + case *ImmutableArray: + for i, v := range o.Value { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[i] = fv + } + case *Map: + for k, v := range o.Value { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[k] = fv + } + case *ImmutableMap: + modName := inferModuleName(o) + if mod := modules.GetBuiltinModule(modName); mod != nil { + return mod.AsImmutableMap(modName), nil + } + + for k, v := range o.Value { + // encoding of user function not supported + if _, isUserFunction := v.(*UserFunction); isUserFunction { + return nil, fmt.Errorf("user function not decodable") + } + + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[k] = fv + } + } + return o, nil +} + +func updateConstIndexes(insts []byte, indexMap map[int]int) { + i := 0 + for i < len(insts) { + op := insts[i] + numOperands := parser.OpcodeOperands[op] + _, read := parser.ReadOperands(numOperands, insts[i+1:]) + + switch op { + case parser.OpConstant: + curIdx := int(insts[i+2]) | int(insts[i+1])<<8 + newIdx, ok := indexMap[curIdx] + if !ok { + panic(fmt.Errorf("constant index not found: %d", curIdx)) + } + copy(insts[i:], MakeInstruction(op, newIdx)) + case parser.OpClosure: + curIdx := int(insts[i+2]) | int(insts[i+1])<<8 + numFree := int(insts[i+3]) + newIdx, ok := indexMap[curIdx] + if !ok { + panic(fmt.Errorf("constant index not found: %d", curIdx)) + } + copy(insts[i:], MakeInstruction(op, newIdx, numFree)) + } + + i += 1 + read + } +} + +func inferModuleName(mod *ImmutableMap) string { + if modName, ok := mod.Value["__module_name__"].(*String); ok { + return modName.Value + } + return "" +} + +func init() { + gob.Register(&parser.SourceFileSet{}) + gob.Register(&parser.SourceFile{}) + gob.Register(&Array{}) + gob.Register(&Bool{}) + gob.Register(&Bytes{}) + gob.Register(&Char{}) + gob.Register(&CompiledFunction{}) + gob.Register(&Error{}) + gob.Register(&Float{}) + gob.Register(&ImmutableArray{}) + gob.Register(&ImmutableMap{}) + gob.Register(&Int{}) + gob.Register(&Map{}) + gob.Register(&String{}) + gob.Register(&Time{}) + gob.Register(&Undefined{}) + gob.Register(&UserFunction{}) +} diff --git a/vendor/github.com/d5/tengo/v2/compiler.go b/vendor/github.com/d5/tengo/v2/compiler.go new file mode 100644 index 00000000..eb686ed6 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/compiler.go @@ -0,0 +1,1312 @@ +package tengo + +import ( + "fmt" + "io" + "io/ioutil" + "path/filepath" + "reflect" + "strings" + + "github.com/d5/tengo/v2/parser" + "github.com/d5/tengo/v2/token" +) + +// compilationScope represents a compiled instructions and the last two +// instructions that were emitted. +type compilationScope struct { + Instructions []byte + SymbolInit map[string]bool + SourceMap map[int]parser.Pos +} + +// loop represents a loop construct that the compiler uses to track the current +// loop. +type loop struct { + Continues []int + Breaks []int +} + +// CompilerError represents a compiler error. +type CompilerError struct { + FileSet *parser.SourceFileSet + Node parser.Node + Err error +} + +func (e *CompilerError) Error() string { + filePos := e.FileSet.Position(e.Node.Pos()) + return fmt.Sprintf("Compile Error: %s\n\tat %s", e.Err.Error(), filePos) +} + +// Compiler compiles the AST into a bytecode. +type Compiler struct { + file *parser.SourceFile + parent *Compiler + modulePath string + constants []Object + symbolTable *SymbolTable + scopes []compilationScope + scopeIndex int + modules *ModuleMap + compiledModules map[string]*CompiledFunction + allowFileImport bool + loops []*loop + loopIndex int + trace io.Writer + indent int +} + +// NewCompiler creates a Compiler. +func NewCompiler( + file *parser.SourceFile, + symbolTable *SymbolTable, + constants []Object, + modules *ModuleMap, + trace io.Writer, +) *Compiler { + mainScope := compilationScope{ + SymbolInit: make(map[string]bool), + SourceMap: make(map[int]parser.Pos), + } + + // symbol table + if symbolTable == nil { + symbolTable = NewSymbolTable() + } + + // add builtin functions to the symbol table + for idx, fn := range builtinFuncs { + symbolTable.DefineBuiltin(idx, fn.Name) + } + + // builtin modules + if modules == nil { + modules = NewModuleMap() + } + + return &Compiler{ + file: file, + symbolTable: symbolTable, + constants: constants, + scopes: []compilationScope{mainScope}, + scopeIndex: 0, + loopIndex: -1, + trace: trace, + modules: modules, + compiledModules: make(map[string]*CompiledFunction), + } +} + +// Compile compiles the AST node. +func (c *Compiler) Compile(node parser.Node) error { + if c.trace != nil { + if node != nil { + defer untracec(tracec(c, fmt.Sprintf("%s (%s)", + node.String(), reflect.TypeOf(node).Elem().Name()))) + } else { + defer untracec(tracec(c, "")) + } + } + + switch node := node.(type) { + case *parser.File: + for _, stmt := range node.Stmts { + if err := c.Compile(stmt); err != nil { + return err + } + } + case *parser.ExprStmt: + if err := c.Compile(node.Expr); err != nil { + return err + } + c.emit(node, parser.OpPop) + case *parser.IncDecStmt: + op := token.AddAssign + if node.Token == token.Dec { + op = token.SubAssign + } + return c.compileAssign(node, []parser.Expr{node.Expr}, + []parser.Expr{&parser.IntLit{Value: 1}}, op) + case *parser.ParenExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + case *parser.BinaryExpr: + if node.Token == token.LAnd || node.Token == token.LOr { + return c.compileLogical(node) + } + if node.Token == token.Less { + if err := c.Compile(node.RHS); err != nil { + return err + } + if err := c.Compile(node.LHS); err != nil { + return err + } + c.emit(node, parser.OpBinaryOp, int(token.Greater)) + return nil + } else if node.Token == token.LessEq { + if err := c.Compile(node.RHS); err != nil { + return err + } + if err := c.Compile(node.LHS); err != nil { + return err + } + c.emit(node, parser.OpBinaryOp, int(token.GreaterEq)) + return nil + } + if err := c.Compile(node.LHS); err != nil { + return err + } + if err := c.Compile(node.RHS); err != nil { + return err + } + + switch node.Token { + case token.Add: + c.emit(node, parser.OpBinaryOp, int(token.Add)) + case token.Sub: + c.emit(node, parser.OpBinaryOp, int(token.Sub)) + case token.Mul: + c.emit(node, parser.OpBinaryOp, int(token.Mul)) + case token.Quo: + c.emit(node, parser.OpBinaryOp, int(token.Quo)) + case token.Rem: + c.emit(node, parser.OpBinaryOp, int(token.Rem)) + case token.Greater: + c.emit(node, parser.OpBinaryOp, int(token.Greater)) + case token.GreaterEq: + c.emit(node, parser.OpBinaryOp, int(token.GreaterEq)) + case token.Equal: + c.emit(node, parser.OpEqual) + case token.NotEqual: + c.emit(node, parser.OpNotEqual) + case token.And: + c.emit(node, parser.OpBinaryOp, int(token.And)) + case token.Or: + c.emit(node, parser.OpBinaryOp, int(token.Or)) + case token.Xor: + c.emit(node, parser.OpBinaryOp, int(token.Xor)) + case token.AndNot: + c.emit(node, parser.OpBinaryOp, int(token.AndNot)) + case token.Shl: + c.emit(node, parser.OpBinaryOp, int(token.Shl)) + case token.Shr: + c.emit(node, parser.OpBinaryOp, int(token.Shr)) + default: + return c.errorf(node, "invalid binary operator: %s", + node.Token.String()) + } + case *parser.IntLit: + c.emit(node, parser.OpConstant, + c.addConstant(&Int{Value: node.Value})) + case *parser.FloatLit: + c.emit(node, parser.OpConstant, + c.addConstant(&Float{Value: node.Value})) + case *parser.BoolLit: + if node.Value { + c.emit(node, parser.OpTrue) + } else { + c.emit(node, parser.OpFalse) + } + case *parser.StringLit: + if len(node.Value) > MaxStringLen { + return c.error(node, ErrStringLimit) + } + c.emit(node, parser.OpConstant, + c.addConstant(&String{Value: node.Value})) + case *parser.CharLit: + c.emit(node, parser.OpConstant, + c.addConstant(&Char{Value: node.Value})) + case *parser.UndefinedLit: + c.emit(node, parser.OpNull) + case *parser.UnaryExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + + switch node.Token { + case token.Not: + c.emit(node, parser.OpLNot) + case token.Sub: + c.emit(node, parser.OpMinus) + case token.Xor: + c.emit(node, parser.OpBComplement) + case token.Add: + // do nothing? + default: + return c.errorf(node, + "invalid unary operator: %s", node.Token.String()) + } + case *parser.IfStmt: + // open new symbol table for the statement + c.symbolTable = c.symbolTable.Fork(true) + defer func() { + c.symbolTable = c.symbolTable.Parent(false) + }() + + if node.Init != nil { + if err := c.Compile(node.Init); err != nil { + return err + } + } + if err := c.Compile(node.Cond); err != nil { + return err + } + + // first jump placeholder + jumpPos1 := c.emit(node, parser.OpJumpFalsy, 0) + if err := c.Compile(node.Body); err != nil { + return err + } + if node.Else != nil { + // second jump placeholder + jumpPos2 := c.emit(node, parser.OpJump, 0) + + // update first jump offset + curPos := len(c.currentInstructions()) + c.changeOperand(jumpPos1, curPos) + if err := c.Compile(node.Else); err != nil { + return err + } + + // update second jump offset + curPos = len(c.currentInstructions()) + c.changeOperand(jumpPos2, curPos) + } else { + // update first jump offset + curPos := len(c.currentInstructions()) + c.changeOperand(jumpPos1, curPos) + } + case *parser.ForStmt: + return c.compileForStmt(node) + case *parser.ForInStmt: + return c.compileForInStmt(node) + case *parser.BranchStmt: + if node.Token == token.Break { + curLoop := c.currentLoop() + if curLoop == nil { + return c.errorf(node, "break not allowed outside loop") + } + pos := c.emit(node, parser.OpJump, 0) + curLoop.Breaks = append(curLoop.Breaks, pos) + } else if node.Token == token.Continue { + curLoop := c.currentLoop() + if curLoop == nil { + return c.errorf(node, "continue not allowed outside loop") + } + pos := c.emit(node, parser.OpJump, 0) + curLoop.Continues = append(curLoop.Continues, pos) + } else { + panic(fmt.Errorf("invalid branch statement: %s", + node.Token.String())) + } + case *parser.BlockStmt: + if len(node.Stmts) == 0 { + return nil + } + + c.symbolTable = c.symbolTable.Fork(true) + defer func() { + c.symbolTable = c.symbolTable.Parent(false) + }() + + for _, stmt := range node.Stmts { + if err := c.Compile(stmt); err != nil { + return err + } + } + case *parser.AssignStmt: + err := c.compileAssign(node, node.LHS, node.RHS, node.Token) + if err != nil { + return err + } + case *parser.Ident: + symbol, _, ok := c.symbolTable.Resolve(node.Name) + if !ok { + return c.errorf(node, "unresolved reference '%s'", node.Name) + } + + switch symbol.Scope { + case ScopeGlobal: + c.emit(node, parser.OpGetGlobal, symbol.Index) + case ScopeLocal: + c.emit(node, parser.OpGetLocal, symbol.Index) + case ScopeBuiltin: + c.emit(node, parser.OpGetBuiltin, symbol.Index) + case ScopeFree: + c.emit(node, parser.OpGetFree, symbol.Index) + } + case *parser.ArrayLit: + for _, elem := range node.Elements { + if err := c.Compile(elem); err != nil { + return err + } + } + c.emit(node, parser.OpArray, len(node.Elements)) + case *parser.MapLit: + for _, elt := range node.Elements { + // key + if len(elt.Key) > MaxStringLen { + return c.error(node, ErrStringLimit) + } + c.emit(node, parser.OpConstant, + c.addConstant(&String{Value: elt.Key})) + + // value + if err := c.Compile(elt.Value); err != nil { + return err + } + } + c.emit(node, parser.OpMap, len(node.Elements)*2) + + case *parser.SelectorExpr: // selector on RHS side + if err := c.Compile(node.Expr); err != nil { + return err + } + if err := c.Compile(node.Sel); err != nil { + return err + } + c.emit(node, parser.OpIndex) + case *parser.IndexExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + if err := c.Compile(node.Index); err != nil { + return err + } + c.emit(node, parser.OpIndex) + case *parser.SliceExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + if node.Low != nil { + if err := c.Compile(node.Low); err != nil { + return err + } + } else { + c.emit(node, parser.OpNull) + } + if node.High != nil { + if err := c.Compile(node.High); err != nil { + return err + } + } else { + c.emit(node, parser.OpNull) + } + c.emit(node, parser.OpSliceIndex) + case *parser.FuncLit: + c.enterScope() + + for _, p := range node.Type.Params.List { + s := c.symbolTable.Define(p.Name) + + // function arguments is not assigned directly. + s.LocalAssigned = true + } + + if err := c.Compile(node.Body); err != nil { + return err + } + + // code optimization + c.optimizeFunc(node) + + freeSymbols := c.symbolTable.FreeSymbols() + numLocals := c.symbolTable.MaxSymbols() + instructions, sourceMap := c.leaveScope() + + for _, s := range freeSymbols { + switch s.Scope { + case ScopeLocal: + if !s.LocalAssigned { + // Here, the closure is capturing a local variable that's + // not yet assigned its value. One example is a local + // recursive function: + // + // func() { + // foo := func(x) { + // // .. + // return foo(x-1) + // } + // } + // + // which translate into + // + // 0000 GETL 0 + // 0002 CLOSURE ? 1 + // 0006 DEFL 0 + // + // . So the local variable (0) is being captured before + // it's assigned the value. + // + // Solution is to transform the code into something like + // this: + // + // func() { + // foo := undefined + // foo = func(x) { + // // .. + // return foo(x-1) + // } + // } + // + // that is equivalent to + // + // 0000 NULL + // 0001 DEFL 0 + // 0003 GETL 0 + // 0005 CLOSURE ? 1 + // 0009 SETL 0 + // + c.emit(node, parser.OpNull) + c.emit(node, parser.OpDefineLocal, s.Index) + s.LocalAssigned = true + } + c.emit(node, parser.OpGetLocalPtr, s.Index) + case ScopeFree: + c.emit(node, parser.OpGetFreePtr, s.Index) + } + } + + compiledFunction := &CompiledFunction{ + Instructions: instructions, + NumLocals: numLocals, + NumParameters: len(node.Type.Params.List), + VarArgs: node.Type.Params.VarArgs, + SourceMap: sourceMap, + } + if len(freeSymbols) > 0 { + c.emit(node, parser.OpClosure, + c.addConstant(compiledFunction), len(freeSymbols)) + } else { + c.emit(node, parser.OpConstant, c.addConstant(compiledFunction)) + } + case *parser.ReturnStmt: + if c.symbolTable.Parent(true) == nil { + // outside the function + return c.errorf(node, "return not allowed outside function") + } + + if node.Result == nil { + c.emit(node, parser.OpReturn, 0) + } else { + if err := c.Compile(node.Result); err != nil { + return err + } + c.emit(node, parser.OpReturn, 1) + } + case *parser.CallExpr: + if err := c.Compile(node.Func); err != nil { + return err + } + for _, arg := range node.Args { + if err := c.Compile(arg); err != nil { + return err + } + } + c.emit(node, parser.OpCall, len(node.Args)) + case *parser.ImportExpr: + if node.ModuleName == "" { + return c.errorf(node, "empty module name") + } + + if mod := c.modules.Get(node.ModuleName); mod != nil { + v, err := mod.Import(node.ModuleName) + if err != nil { + return err + } + + switch v := v.(type) { + case []byte: // module written in Tengo + compiled, err := c.compileModule(node, + node.ModuleName, node.ModuleName, v) + if err != nil { + return err + } + c.emit(node, parser.OpConstant, c.addConstant(compiled)) + c.emit(node, parser.OpCall, 0) + case Object: // builtin module + c.emit(node, parser.OpConstant, c.addConstant(v)) + default: + panic(fmt.Errorf("invalid import value type: %T", v)) + } + } else if c.allowFileImport { + moduleName := node.ModuleName + if !strings.HasSuffix(moduleName, ".tengo") { + moduleName += ".tengo" + } + + modulePath, err := filepath.Abs(moduleName) + if err != nil { + return c.errorf(node, "module file path error: %s", + err.Error()) + } + + if err := c.checkCyclicImports(node, modulePath); err != nil { + return err + } + + moduleSrc, err := ioutil.ReadFile(moduleName) + if err != nil { + return c.errorf(node, "module file read error: %s", + err.Error()) + } + + compiled, err := c.compileModule(node, + moduleName, modulePath, moduleSrc) + if err != nil { + return err + } + c.emit(node, parser.OpConstant, c.addConstant(compiled)) + c.emit(node, parser.OpCall, 0) + } else { + return c.errorf(node, "module '%s' not found", node.ModuleName) + } + case *parser.ExportStmt: + // export statement must be in top-level scope + if c.scopeIndex != 0 { + return c.errorf(node, "export not allowed inside function") + } + + // export statement is simply ignore when compiling non-module code + if c.parent == nil { + break + } + if err := c.Compile(node.Result); err != nil { + return err + } + c.emit(node, parser.OpImmutable) + c.emit(node, parser.OpReturn, 1) + case *parser.ErrorExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + c.emit(node, parser.OpError) + case *parser.ImmutableExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + c.emit(node, parser.OpImmutable) + case *parser.CondExpr: + if err := c.Compile(node.Cond); err != nil { + return err + } + + // first jump placeholder + jumpPos1 := c.emit(node, parser.OpJumpFalsy, 0) + if err := c.Compile(node.True); err != nil { + return err + } + + // second jump placeholder + jumpPos2 := c.emit(node, parser.OpJump, 0) + + // update first jump offset + curPos := len(c.currentInstructions()) + c.changeOperand(jumpPos1, curPos) + if err := c.Compile(node.False); err != nil { + return err + } + + // update second jump offset + curPos = len(c.currentInstructions()) + c.changeOperand(jumpPos2, curPos) + } + return nil +} + +// Bytecode returns a compiled bytecode. +func (c *Compiler) Bytecode() *Bytecode { + return &Bytecode{ + FileSet: c.file.Set(), + MainFunction: &CompiledFunction{ + Instructions: append(c.currentInstructions(), parser.OpSuspend), + SourceMap: c.currentSourceMap(), + }, + Constants: c.constants, + } +} + +// EnableFileImport enables or disables module loading from local files. +// Local file modules are disabled by default. +func (c *Compiler) EnableFileImport(enable bool) { + c.allowFileImport = enable +} + +func (c *Compiler) compileAssign( + node parser.Node, + lhs, rhs []parser.Expr, + op token.Token, +) error { + numLHS, numRHS := len(lhs), len(rhs) + if numLHS > 1 || numRHS > 1 { + return c.errorf(node, "tuple assignment not allowed") + } + + // resolve and compile left-hand side + ident, selectors := resolveAssignLHS(lhs[0]) + numSel := len(selectors) + + if op == token.Define && numSel > 0 { + // using selector on new variable does not make sense + return c.errorf(node, "operator ':=' not allowed with selector") + } + + symbol, depth, exists := c.symbolTable.Resolve(ident) + if op == token.Define { + if depth == 0 && exists { + return c.errorf(node, "'%s' redeclared in this block", ident) + } + symbol = c.symbolTable.Define(ident) + } else { + if !exists { + return c.errorf(node, "unresolved reference '%s'", ident) + } + } + + // +=, -=, *=, /= + if op != token.Assign && op != token.Define { + if err := c.Compile(lhs[0]); err != nil { + return err + } + } + + // compile RHSs + for _, expr := range rhs { + if err := c.Compile(expr); err != nil { + return err + } + } + + switch op { + case token.AddAssign: + c.emit(node, parser.OpBinaryOp, int(token.Add)) + case token.SubAssign: + c.emit(node, parser.OpBinaryOp, int(token.Sub)) + case token.MulAssign: + c.emit(node, parser.OpBinaryOp, int(token.Mul)) + case token.QuoAssign: + c.emit(node, parser.OpBinaryOp, int(token.Quo)) + case token.RemAssign: + c.emit(node, parser.OpBinaryOp, int(token.Rem)) + case token.AndAssign: + c.emit(node, parser.OpBinaryOp, int(token.And)) + case token.OrAssign: + c.emit(node, parser.OpBinaryOp, int(token.Or)) + case token.AndNotAssign: + c.emit(node, parser.OpBinaryOp, int(token.AndNot)) + case token.XorAssign: + c.emit(node, parser.OpBinaryOp, int(token.Xor)) + case token.ShlAssign: + c.emit(node, parser.OpBinaryOp, int(token.Shl)) + case token.ShrAssign: + c.emit(node, parser.OpBinaryOp, int(token.Shr)) + } + + // compile selector expressions (right to left) + for i := numSel - 1; i >= 0; i-- { + if err := c.Compile(selectors[i]); err != nil { + return err + } + } + + switch symbol.Scope { + case ScopeGlobal: + if numSel > 0 { + c.emit(node, parser.OpSetSelGlobal, symbol.Index, numSel) + } else { + c.emit(node, parser.OpSetGlobal, symbol.Index) + } + case ScopeLocal: + if numSel > 0 { + c.emit(node, parser.OpSetSelLocal, symbol.Index, numSel) + } else { + if op == token.Define && !symbol.LocalAssigned { + c.emit(node, parser.OpDefineLocal, symbol.Index) + } else { + c.emit(node, parser.OpSetLocal, symbol.Index) + } + } + + // mark the symbol as local-assigned + symbol.LocalAssigned = true + case ScopeFree: + if numSel > 0 { + c.emit(node, parser.OpSetSelFree, symbol.Index, numSel) + } else { + c.emit(node, parser.OpSetFree, symbol.Index) + } + default: + panic(fmt.Errorf("invalid assignment variable scope: %s", + symbol.Scope)) + } + return nil +} + +func (c *Compiler) compileLogical(node *parser.BinaryExpr) error { + // left side term + if err := c.Compile(node.LHS); err != nil { + return err + } + + // jump position + var jumpPos int + if node.Token == token.LAnd { + jumpPos = c.emit(node, parser.OpAndJump, 0) + } else { + jumpPos = c.emit(node, parser.OpOrJump, 0) + } + + // right side term + if err := c.Compile(node.RHS); err != nil { + return err + } + + c.changeOperand(jumpPos, len(c.currentInstructions())) + return nil +} + +func (c *Compiler) compileForStmt(stmt *parser.ForStmt) error { + c.symbolTable = c.symbolTable.Fork(true) + defer func() { + c.symbolTable = c.symbolTable.Parent(false) + }() + + // init statement + if stmt.Init != nil { + if err := c.Compile(stmt.Init); err != nil { + return err + } + } + + // pre-condition position + preCondPos := len(c.currentInstructions()) + + // condition expression + postCondPos := -1 + if stmt.Cond != nil { + if err := c.Compile(stmt.Cond); err != nil { + return err + } + // condition jump position + postCondPos = c.emit(stmt, parser.OpJumpFalsy, 0) + } + + // enter loop + loop := c.enterLoop() + + // body statement + if err := c.Compile(stmt.Body); err != nil { + c.leaveLoop() + return err + } + + c.leaveLoop() + + // post-body position + postBodyPos := len(c.currentInstructions()) + + // post statement + if stmt.Post != nil { + if err := c.Compile(stmt.Post); err != nil { + return err + } + } + + // back to condition + c.emit(stmt, parser.OpJump, preCondPos) + + // post-statement position + postStmtPos := len(c.currentInstructions()) + if postCondPos >= 0 { + c.changeOperand(postCondPos, postStmtPos) + } + + // update all break/continue jump positions + for _, pos := range loop.Breaks { + c.changeOperand(pos, postStmtPos) + } + for _, pos := range loop.Continues { + c.changeOperand(pos, postBodyPos) + } + return nil +} + +func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error { + c.symbolTable = c.symbolTable.Fork(true) + defer func() { + c.symbolTable = c.symbolTable.Parent(false) + }() + + // for-in statement is compiled like following: + // + // for :it := iterator(iterable); :it.next(); { + // k, v := :it.get() // DEFINE operator + // + // ... body ... + // } + // + // ":it" is a local variable but will be conflict with other user variables + // because character ":" is not allowed. + + // init + // :it = iterator(iterable) + itSymbol := c.symbolTable.Define(":it") + if err := c.Compile(stmt.Iterable); err != nil { + return err + } + c.emit(stmt, parser.OpIteratorInit) + if itSymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpSetGlobal, itSymbol.Index) + } else { + c.emit(stmt, parser.OpDefineLocal, itSymbol.Index) + } + + // pre-condition position + preCondPos := len(c.currentInstructions()) + + // condition + // :it.HasMore() + if itSymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpGetGlobal, itSymbol.Index) + } else { + c.emit(stmt, parser.OpGetLocal, itSymbol.Index) + } + c.emit(stmt, parser.OpIteratorNext) + + // condition jump position + postCondPos := c.emit(stmt, parser.OpJumpFalsy, 0) + + // enter loop + loop := c.enterLoop() + + // assign key variable + if stmt.Key.Name != "_" { + keySymbol := c.symbolTable.Define(stmt.Key.Name) + if itSymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpGetGlobal, itSymbol.Index) + } else { + c.emit(stmt, parser.OpGetLocal, itSymbol.Index) + } + c.emit(stmt, parser.OpIteratorKey) + if keySymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpSetGlobal, keySymbol.Index) + } else { + c.emit(stmt, parser.OpDefineLocal, keySymbol.Index) + } + } + + // assign value variable + if stmt.Value.Name != "_" { + valueSymbol := c.symbolTable.Define(stmt.Value.Name) + if itSymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpGetGlobal, itSymbol.Index) + } else { + c.emit(stmt, parser.OpGetLocal, itSymbol.Index) + } + c.emit(stmt, parser.OpIteratorValue) + if valueSymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpSetGlobal, valueSymbol.Index) + } else { + c.emit(stmt, parser.OpDefineLocal, valueSymbol.Index) + } + } + + // body statement + if err := c.Compile(stmt.Body); err != nil { + c.leaveLoop() + return err + } + + c.leaveLoop() + + // post-body position + postBodyPos := len(c.currentInstructions()) + + // back to condition + c.emit(stmt, parser.OpJump, preCondPos) + + // post-statement position + postStmtPos := len(c.currentInstructions()) + c.changeOperand(postCondPos, postStmtPos) + + // update all break/continue jump positions + for _, pos := range loop.Breaks { + c.changeOperand(pos, postStmtPos) + } + for _, pos := range loop.Continues { + c.changeOperand(pos, postBodyPos) + } + return nil +} + +func (c *Compiler) checkCyclicImports( + node parser.Node, + modulePath string, +) error { + if c.modulePath == modulePath { + return c.errorf(node, "cyclic module import: %s", modulePath) + } else if c.parent != nil { + return c.parent.checkCyclicImports(node, modulePath) + } + return nil +} + +func (c *Compiler) compileModule( + node parser.Node, + moduleName, modulePath string, + src []byte, +) (*CompiledFunction, error) { + if err := c.checkCyclicImports(node, modulePath); err != nil { + return nil, err + } + + compiledModule, exists := c.loadCompiledModule(modulePath) + if exists { + return compiledModule, nil + } + + modFile := c.file.Set().AddFile(moduleName, -1, len(src)) + p := parser.NewParser(modFile, src, nil) + file, err := p.ParseFile() + if err != nil { + return nil, err + } + + // inherit builtin functions + symbolTable := NewSymbolTable() + for _, sym := range c.symbolTable.BuiltinSymbols() { + symbolTable.DefineBuiltin(sym.Index, sym.Name) + } + + // no global scope for the module + symbolTable = symbolTable.Fork(false) + + // compile module + moduleCompiler := c.fork(modFile, modulePath, symbolTable) + if err := moduleCompiler.Compile(file); err != nil { + return nil, err + } + + // code optimization + moduleCompiler.optimizeFunc(node) + compiledFunc := moduleCompiler.Bytecode().MainFunction + compiledFunc.NumLocals = symbolTable.MaxSymbols() + c.storeCompiledModule(modulePath, compiledFunc) + return compiledFunc, nil +} + +func (c *Compiler) loadCompiledModule( + modulePath string, +) (mod *CompiledFunction, ok bool) { + if c.parent != nil { + return c.parent.loadCompiledModule(modulePath) + } + mod, ok = c.compiledModules[modulePath] + return +} + +func (c *Compiler) storeCompiledModule( + modulePath string, + module *CompiledFunction, +) { + if c.parent != nil { + c.parent.storeCompiledModule(modulePath, module) + } + c.compiledModules[modulePath] = module +} + +func (c *Compiler) enterLoop() *loop { + loop := &loop{} + c.loops = append(c.loops, loop) + c.loopIndex++ + if c.trace != nil { + c.printTrace("LOOPE", c.loopIndex) + } + return loop +} + +func (c *Compiler) leaveLoop() { + if c.trace != nil { + c.printTrace("LOOPL", c.loopIndex) + } + c.loops = c.loops[:len(c.loops)-1] + c.loopIndex-- +} + +func (c *Compiler) currentLoop() *loop { + if c.loopIndex >= 0 { + return c.loops[c.loopIndex] + } + return nil +} + +func (c *Compiler) currentInstructions() []byte { + return c.scopes[c.scopeIndex].Instructions +} + +func (c *Compiler) currentSourceMap() map[int]parser.Pos { + return c.scopes[c.scopeIndex].SourceMap +} + +func (c *Compiler) enterScope() { + scope := compilationScope{ + SymbolInit: make(map[string]bool), + SourceMap: make(map[int]parser.Pos), + } + c.scopes = append(c.scopes, scope) + c.scopeIndex++ + c.symbolTable = c.symbolTable.Fork(false) + if c.trace != nil { + c.printTrace("SCOPE", c.scopeIndex) + } +} + +func (c *Compiler) leaveScope() ( + instructions []byte, + sourceMap map[int]parser.Pos, +) { + instructions = c.currentInstructions() + sourceMap = c.currentSourceMap() + c.scopes = c.scopes[:len(c.scopes)-1] + c.scopeIndex-- + c.symbolTable = c.symbolTable.Parent(true) + if c.trace != nil { + c.printTrace("SCOPL", c.scopeIndex) + } + return +} + +func (c *Compiler) fork( + file *parser.SourceFile, + modulePath string, + symbolTable *SymbolTable, +) *Compiler { + child := NewCompiler(file, symbolTable, nil, c.modules, c.trace) + child.modulePath = modulePath // module file path + child.parent = c // parent to set to current compiler + return child +} + +func (c *Compiler) error(node parser.Node, err error) error { + return &CompilerError{ + FileSet: c.file.Set(), + Node: node, + Err: err, + } +} + +func (c *Compiler) errorf( + node parser.Node, + format string, + args ...interface{}, +) error { + return &CompilerError{ + FileSet: c.file.Set(), + Node: node, + Err: fmt.Errorf(format, args...), + } +} + +func (c *Compiler) addConstant(o Object) int { + if c.parent != nil { + // module compilers will use their parent's constants array + return c.parent.addConstant(o) + } + c.constants = append(c.constants, o) + if c.trace != nil { + c.printTrace(fmt.Sprintf("CONST %04d %s", len(c.constants)-1, o)) + } + return len(c.constants) - 1 +} + +func (c *Compiler) addInstruction(b []byte) int { + posNewIns := len(c.currentInstructions()) + c.scopes[c.scopeIndex].Instructions = append( + c.currentInstructions(), b...) + return posNewIns +} + +func (c *Compiler) replaceInstruction(pos int, inst []byte) { + copy(c.currentInstructions()[pos:], inst) + if c.trace != nil { + c.printTrace(fmt.Sprintf("REPLC %s", + FormatInstructions( + c.scopes[c.scopeIndex].Instructions[pos:], pos)[0])) + } +} + +func (c *Compiler) changeOperand(opPos int, operand ...int) { + op := c.currentInstructions()[opPos] + inst := MakeInstruction(op, operand...) + c.replaceInstruction(opPos, inst) +} + +// optimizeFunc performs some code-level optimization for the current function +// instructions. It also removes unreachable (dead code) instructions and adds +// "returns" instruction if needed. +func (c *Compiler) optimizeFunc(node parser.Node) { + // any instructions between RETURN and the function end + // or instructions between RETURN and jump target position + // are considered as unreachable. + + // pass 1. identify all jump destinations + dsts := make(map[int]bool) + iterateInstructions(c.scopes[c.scopeIndex].Instructions, + func(pos int, opcode parser.Opcode, operands []int) bool { + switch opcode { + case parser.OpJump, parser.OpJumpFalsy, + parser.OpAndJump, parser.OpOrJump: + dsts[operands[0]] = true + } + return true + }) + + // pass 2. eliminate dead code + var newInsts []byte + posMap := make(map[int]int) // old position to new position + var dstIdx int + var deadCode bool + iterateInstructions(c.scopes[c.scopeIndex].Instructions, + func(pos int, opcode parser.Opcode, operands []int) bool { + switch { + case opcode == parser.OpReturn: + if deadCode { + return true + } + deadCode = true + case dsts[pos]: + dstIdx++ + deadCode = false + case deadCode: + return true + } + posMap[pos] = len(newInsts) + newInsts = append(newInsts, + MakeInstruction(opcode, operands...)...) + return true + }) + + // pass 3. update jump positions + var lastOp parser.Opcode + var appendReturn bool + endPos := len(c.scopes[c.scopeIndex].Instructions) + iterateInstructions(newInsts, + func(pos int, opcode parser.Opcode, operands []int) bool { + switch opcode { + case parser.OpJump, parser.OpJumpFalsy, parser.OpAndJump, + parser.OpOrJump: + newDst, ok := posMap[operands[0]] + if ok { + copy(newInsts[pos:], + MakeInstruction(opcode, newDst)) + } else if endPos == operands[0] { + // there's a jump instruction that jumps to the end of + // function compiler should append "return". + appendReturn = true + } else { + panic(fmt.Errorf("invalid jump position: %d", newDst)) + } + } + lastOp = opcode + return true + }) + if lastOp != parser.OpReturn { + appendReturn = true + } + + // pass 4. update source map + newSourceMap := make(map[int]parser.Pos) + for pos, srcPos := range c.scopes[c.scopeIndex].SourceMap { + newPos, ok := posMap[pos] + if ok { + newSourceMap[newPos] = srcPos + } + } + c.scopes[c.scopeIndex].Instructions = newInsts + c.scopes[c.scopeIndex].SourceMap = newSourceMap + + // append "return" + if appendReturn { + c.emit(node, parser.OpReturn, 0) + } +} + +func (c *Compiler) emit( + node parser.Node, + opcode parser.Opcode, + operands ...int, +) int { + filePos := parser.NoPos + if node != nil { + filePos = node.Pos() + } + + inst := MakeInstruction(opcode, operands...) + pos := c.addInstruction(inst) + c.scopes[c.scopeIndex].SourceMap[pos] = filePos + if c.trace != nil { + c.printTrace(fmt.Sprintf("EMIT %s", + FormatInstructions( + c.scopes[c.scopeIndex].Instructions[pos:], pos)[0])) + } + return pos +} + +func (c *Compiler) printTrace(a ...interface{}) { + const ( + dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + n = len(dots) + ) + + i := 2 * c.indent + for i > n { + _, _ = fmt.Fprint(c.trace, dots) + i -= n + } + _, _ = fmt.Fprint(c.trace, dots[0:i]) + _, _ = fmt.Fprintln(c.trace, a...) +} + +func resolveAssignLHS( + expr parser.Expr, +) (name string, selectors []parser.Expr) { + switch term := expr.(type) { + case *parser.SelectorExpr: + name, selectors = resolveAssignLHS(term.Expr) + selectors = append(selectors, term.Sel) + return + case *parser.IndexExpr: + name, selectors = resolveAssignLHS(term.Expr) + selectors = append(selectors, term.Index) + case *parser.Ident: + name = term.Name + } + return +} + +func iterateInstructions( + b []byte, + fn func(pos int, opcode parser.Opcode, operands []int) bool, +) { + for i := 0; i < len(b); i++ { + numOperands := parser.OpcodeOperands[b[i]] + operands, read := parser.ReadOperands(numOperands, b[i+1:]) + if !fn(i, b[i], operands) { + break + } + i += read + } +} + +func tracec(c *Compiler, msg string) *Compiler { + c.printTrace(msg, "{") + c.indent++ + return c +} + +func untracec(c *Compiler) { + c.indent-- + c.printTrace("}") +} diff --git a/vendor/github.com/d5/tengo/v2/doc.go b/vendor/github.com/d5/tengo/v2/doc.go new file mode 100644 index 00000000..05b47de1 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/doc.go @@ -0,0 +1,3 @@ +// tengo is a small, dynamic, fast, secure script language for Go. + +package tengo diff --git a/vendor/github.com/d5/tengo/v2/errors.go b/vendor/github.com/d5/tengo/v2/errors.go new file mode 100644 index 00000000..a3fd1f3b --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/errors.go @@ -0,0 +1,64 @@ +package tengo + +import ( + "errors" + "fmt" +) + +var ( + // ErrStackOverflow is a stack overflow error. + ErrStackOverflow = errors.New("stack overflow") + + // ErrObjectAllocLimit is an objects allocation limit error. + ErrObjectAllocLimit = errors.New("object allocation limit exceeded") + + // ErrIndexOutOfBounds is an error where a given index is out of the + // bounds. + ErrIndexOutOfBounds = errors.New("index out of bounds") + + // ErrInvalidIndexType represents an invalid index type. + ErrInvalidIndexType = errors.New("invalid index type") + + // ErrInvalidIndexValueType represents an invalid index value type. + ErrInvalidIndexValueType = errors.New("invalid index value type") + + // ErrInvalidIndexOnError represents an invalid index on error. + ErrInvalidIndexOnError = errors.New("invalid index on error") + + // ErrInvalidOperator represents an error for invalid operator usage. + ErrInvalidOperator = errors.New("invalid operator") + + // ErrWrongNumArguments represents a wrong number of arguments error. + ErrWrongNumArguments = errors.New("wrong number of arguments") + + // ErrBytesLimit represents an error where the size of bytes value exceeds + // the limit. + ErrBytesLimit = errors.New("exceeding bytes size limit") + + // ErrStringLimit represents an error where the size of string value + // exceeds the limit. + ErrStringLimit = errors.New("exceeding string size limit") + + // ErrNotIndexable is an error where an Object is not indexable. + ErrNotIndexable = errors.New("not indexable") + + // ErrNotIndexAssignable is an error where an Object is not index + // assignable. + ErrNotIndexAssignable = errors.New("not index-assignable") + + // ErrNotImplemented is an error where an Object has not implemented a + // required method. + ErrNotImplemented = errors.New("not implemented") +) + +// ErrInvalidArgumentType represents an invalid argument value type error. +type ErrInvalidArgumentType struct { + Name string + Expected string + Found string +} + +func (e ErrInvalidArgumentType) Error() string { + return fmt.Sprintf("invalid type for argument '%s': expected %s, found %s", + e.Name, e.Expected, e.Found) +} diff --git a/vendor/github.com/d5/tengo/v2/formatter.go b/vendor/github.com/d5/tengo/v2/formatter.go new file mode 100644 index 00000000..0dbf71c6 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/formatter.go @@ -0,0 +1,1245 @@ +package tengo + +import ( + "strconv" + "sync" + "unicode/utf8" +) + +// Strings for use with fmtbuf.WriteString. This is less overhead than using +// fmtbuf.Write with byte arrays. +const ( + commaSpaceString = ", " + nilParenString = "(nil)" + percentBangString = "%!" + missingString = "(MISSING)" + badIndexString = "(BADINDEX)" + extraString = "%!(EXTRA " + badWidthString = "%!(BADWIDTH)" + badPrecString = "%!(BADPREC)" + noVerbString = "%!(NOVERB)" +) + +const ( + ldigits = "0123456789abcdefx" + udigits = "0123456789ABCDEFX" +) + +const ( + signed = true + unsigned = false +) + +// flags placed in a separate struct for easy clearing. +type fmtFlags struct { + widPresent bool + precPresent bool + minus bool + plus bool + sharp bool + space bool + zero bool + + // For the formats %+v %#v, we set the plusV/sharpV flags + // and clear the plus/sharp flags since %+v and %#v are in effect + // different, flagless formats set at the top level. + plusV bool + sharpV bool + + // error-related flags. + inDetail bool + needNewline bool + needColon bool +} + +// A formatter is the raw formatter used by Printf etc. +// It prints into a fmtbuf that must be set up separately. +type formatter struct { + buf *fmtbuf + + fmtFlags + + wid int // width + prec int // precision + + // intbuf is large enough to store %b of an int64 with a sign and + // avoids padding at the end of the struct on 32 bit architectures. + intbuf [68]byte +} + +func (f *formatter) clearFlags() { + f.fmtFlags = fmtFlags{} +} + +func (f *formatter) init(buf *fmtbuf) { + f.buf = buf + f.clearFlags() +} + +// writePadding generates n bytes of padding. +func (f *formatter) writePadding(n int) { + if n <= 0 { // No padding bytes needed. + return + } + buf := *f.buf + oldLen := len(buf) + newLen := oldLen + n + + if newLen > MaxStringLen { + panic(ErrStringLimit) + } + + // Make enough room for padding. + if newLen > cap(buf) { + buf = make(fmtbuf, cap(buf)*2+n) + copy(buf, *f.buf) + } + // Decide which byte the padding should be filled with. + padByte := byte(' ') + if f.zero { + padByte = byte('0') + } + // Fill padding with padByte. + padding := buf[oldLen:newLen] + for i := range padding { + padding[i] = padByte + } + *f.buf = buf[:newLen] +} + +// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus). +func (f *formatter) pad(b []byte) { + if !f.widPresent || f.wid == 0 { + f.buf.Write(b) + return + } + width := f.wid - utf8.RuneCount(b) + if !f.minus { + // left padding + f.writePadding(width) + f.buf.Write(b) + } else { + // right padding + f.buf.Write(b) + f.writePadding(width) + } +} + +// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus). +func (f *formatter) padString(s string) { + if !f.widPresent || f.wid == 0 { + f.buf.WriteString(s) + return + } + width := f.wid - utf8.RuneCountInString(s) + if !f.minus { + // left padding + f.writePadding(width) + f.buf.WriteString(s) + } else { + // right padding + f.buf.WriteString(s) + f.writePadding(width) + } +} + +// fmtBoolean formats a boolean. +func (f *formatter) fmtBoolean(v bool) { + if v { + f.padString("true") + } else { + f.padString("false") + } +} + +// fmtUnicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'". +func (f *formatter) fmtUnicode(u uint64) { + buf := f.intbuf[0:] + + // With default precision set the maximum needed buf length is 18 + // for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF") which fits + // into the already allocated intbuf with a capacity of 68 bytes. + prec := 4 + if f.precPresent && f.prec > 4 { + prec = f.prec + // Compute space needed for "U+" , number, " '", character, "'". + width := 2 + prec + 2 + utf8.UTFMax + 1 + if width > len(buf) { + buf = make([]byte, width) + } + } + + // Format into buf, ending at buf[i]. Formatting numbers is easier + // right-to-left. + i := len(buf) + + // For %#U we want to add a space and a quoted character at the end of + // the fmtbuf. + if f.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) { + i-- + buf[i] = '\'' + i -= utf8.RuneLen(rune(u)) + utf8.EncodeRune(buf[i:], rune(u)) + i-- + buf[i] = '\'' + i-- + buf[i] = ' ' + } + // Format the Unicode code point u as a hexadecimal number. + for u >= 16 { + i-- + buf[i] = udigits[u&0xF] + prec-- + u >>= 4 + } + i-- + buf[i] = udigits[u] + prec-- + // Add zeros in front of the number until requested precision is reached. + for prec > 0 { + i-- + buf[i] = '0' + prec-- + } + // Add a leading "U+". + i-- + buf[i] = '+' + i-- + buf[i] = 'U' + + oldZero := f.zero + f.zero = false + f.pad(buf[i:]) + f.zero = oldZero +} + +// fmtInteger formats signed and unsigned integers. +func (f *formatter) fmtInteger( + u uint64, + base int, + isSigned bool, + verb rune, + digits string, +) { + negative := isSigned && int64(u) < 0 + if negative { + u = -u + } + + buf := f.intbuf[0:] + // The already allocated f.intbuf with a capacity of 68 bytes + // is large enough for integer formatting when no precision or width is set. + if f.widPresent || f.precPresent { + // Account 3 extra bytes for possible addition of a sign and "0x". + width := 3 + f.wid + f.prec // wid and prec are always positive. + if width > len(buf) { + // We're going to need a bigger boat. + buf = make([]byte, width) + } + } + + // Two ways to ask for extra leading zero digits: %.3d or %03d. + // If both are specified the f.zero flag is ignored and + // padding with spaces is used instead. + prec := 0 + if f.precPresent { + prec = f.prec + // Precision of 0 and value of 0 means "print nothing" but padding. + if prec == 0 && u == 0 { + oldZero := f.zero + f.zero = false + f.writePadding(f.wid) + f.zero = oldZero + return + } + } else if f.zero && f.widPresent { + prec = f.wid + if negative || f.plus || f.space { + prec-- // leave room for sign + } + } + + // Because printing is easier right-to-left: format u into buf, ending at + // buf[i]. We could make things marginally faster by splitting the 32-bit + // case out into a separate block but it's not worth the duplication, so + // u has 64 bits. + i := len(buf) + // Use constants for the division and modulo for more efficient code. + // Switch cases ordered by popularity. + switch base { + case 10: + for u >= 10 { + i-- + next := u / 10 + buf[i] = byte('0' + u - next*10) + u = next + } + case 16: + for u >= 16 { + i-- + buf[i] = digits[u&0xF] + u >>= 4 + } + case 8: + for u >= 8 { + i-- + buf[i] = byte('0' + u&7) + u >>= 3 + } + case 2: + for u >= 2 { + i-- + buf[i] = byte('0' + u&1) + u >>= 1 + } + default: + panic("fmt: unknown base; can't happen") + } + i-- + buf[i] = digits[u] + for i > 0 && prec > len(buf)-i { + i-- + buf[i] = '0' + } + + // Various prefixes: 0x, -, etc. + if f.sharp { + switch base { + case 2: + // Add a leading 0b. + i-- + buf[i] = 'b' + i-- + buf[i] = '0' + case 8: + if buf[i] != '0' { + i-- + buf[i] = '0' + } + case 16: + // Add a leading 0x or 0X. + i-- + buf[i] = digits[16] + i-- + buf[i] = '0' + } + } + if verb == 'O' { + i-- + buf[i] = 'o' + i-- + buf[i] = '0' + } + + if negative { + i-- + buf[i] = '-' + } else if f.plus { + i-- + buf[i] = '+' + } else if f.space { + i-- + buf[i] = ' ' + } + + // Left padding with zeros has already been handled like precision earlier + // or the f.zero flag is ignored due to an explicitly set precision. + oldZero := f.zero + f.zero = false + f.pad(buf[i:]) + f.zero = oldZero +} + +// truncate truncates the string s to the specified precision, if present. +func (f *formatter) truncateString(s string) string { + if f.precPresent { + n := f.prec + for i := range s { + n-- + if n < 0 { + return s[:i] + } + } + } + return s +} + +// truncate truncates the byte slice b as a string of the specified precision, +// if present. +func (f *formatter) truncate(b []byte) []byte { + if f.precPresent { + n := f.prec + for i := 0; i < len(b); { + n-- + if n < 0 { + return b[:i] + } + wid := 1 + if b[i] >= utf8.RuneSelf { + _, wid = utf8.DecodeRune(b[i:]) + } + i += wid + } + } + return b +} + +// fmtS formats a string. +func (f *formatter) fmtS(s string) { + s = f.truncateString(s) + f.padString(s) +} + +// fmtBs formats the byte slice b as if it was formatted as string with fmtS. +func (f *formatter) fmtBs(b []byte) { + b = f.truncate(b) + f.pad(b) +} + +// fmtSbx formats a string or byte slice as a hexadecimal encoding of its bytes. +func (f *formatter) fmtSbx(s string, b []byte, digits string) { + length := len(b) + if b == nil { + // No byte slice present. Assume string s should be encoded. + length = len(s) + } + // Set length to not process more bytes than the precision demands. + if f.precPresent && f.prec < length { + length = f.prec + } + // Compute width of the encoding taking into account the f.sharp and + // f.space flag. + width := 2 * length + if width > 0 { + if f.space { + // Each element encoded by two hexadecimals will get a leading + // 0x or 0X. + if f.sharp { + width *= 2 + } + // Elements will be separated by a space. + width += length - 1 + } else if f.sharp { + // Only a leading 0x or 0X will be added for the whole string. + width += 2 + } + } else { // The byte slice or string that should be encoded is empty. + if f.widPresent { + f.writePadding(f.wid) + } + return + } + // Handle padding to the left. + if f.widPresent && f.wid > width && !f.minus { + f.writePadding(f.wid - width) + } + // Write the encoding directly into the output fmtbuf. + buf := *f.buf + if f.sharp { + // Add leading 0x or 0X. + buf = append(buf, '0', digits[16]) + } + var c byte + for i := 0; i < length; i++ { + if f.space && i > 0 { + // Separate elements with a space. + buf = append(buf, ' ') + if f.sharp { + // Add leading 0x or 0X for each element. + buf = append(buf, '0', digits[16]) + } + } + if b != nil { + c = b[i] // Take a byte from the input byte slice. + } else { + c = s[i] // Take a byte from the input string. + } + // Encode each byte as two hexadecimal digits. + buf = append(buf, digits[c>>4], digits[c&0xF]) + } + *f.buf = buf + // Handle padding to the right. + if f.widPresent && f.wid > width && f.minus { + f.writePadding(f.wid - width) + } +} + +// fmtSx formats a string as a hexadecimal encoding of its bytes. +func (f *formatter) fmtSx(s, digits string) { + f.fmtSbx(s, nil, digits) +} + +// fmtBx formats a byte slice as a hexadecimal encoding of its bytes. +func (f *formatter) fmtBx(b []byte, digits string) { + f.fmtSbx("", b, digits) +} + +// fmtQ formats a string as a double-quoted, escaped Go string constant. +// If f.sharp is set a raw (backquoted) string may be returned instead +// if the string does not contain any control characters other than tab. +func (f *formatter) fmtQ(s string) { + s = f.truncateString(s) + if f.sharp && strconv.CanBackquote(s) { + f.padString("`" + s + "`") + return + } + buf := f.intbuf[:0] + if f.plus { + f.pad(strconv.AppendQuoteToASCII(buf, s)) + } else { + f.pad(strconv.AppendQuote(buf, s)) + } +} + +// fmtC formats an integer as a Unicode character. +// If the character is not valid Unicode, it will print '\ufffd'. +func (f *formatter) fmtC(c uint64) { + r := rune(c) + if c > utf8.MaxRune { + r = utf8.RuneError + } + buf := f.intbuf[:0] + w := utf8.EncodeRune(buf[:utf8.UTFMax], r) + f.pad(buf[:w]) +} + +// fmtQc formats an integer as a single-quoted, escaped Go character constant. +// If the character is not valid Unicode, it will print '\ufffd'. +func (f *formatter) fmtQc(c uint64) { + r := rune(c) + if c > utf8.MaxRune { + r = utf8.RuneError + } + buf := f.intbuf[:0] + if f.plus { + f.pad(strconv.AppendQuoteRuneToASCII(buf, r)) + } else { + f.pad(strconv.AppendQuoteRune(buf, r)) + } +} + +// fmtFloat formats a float64. It assumes that verb is a valid format specifier +// for strconv.AppendFloat and therefore fits into a byte. +func (f *formatter) fmtFloat(v float64, size int, verb rune, prec int) { + // Explicit precision in format specifier overrules default precision. + if f.precPresent { + prec = f.prec + } + // Format number, reserving space for leading + sign if needed. + num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size) + if num[1] == '-' || num[1] == '+' { + num = num[1:] + } else { + num[0] = '+' + } + // f.space means to add a leading space instead of a "+" sign unless + // the sign is explicitly asked for by f.plus. + if f.space && num[0] == '+' && !f.plus { + num[0] = ' ' + } + // Special handling for infinities and NaN, + // which don't look like a number so shouldn't be padded with zeros. + if num[1] == 'I' || num[1] == 'N' { + oldZero := f.zero + f.zero = false + // Remove sign before NaN if not asked for. + if num[1] == 'N' && !f.space && !f.plus { + num = num[1:] + } + f.pad(num) + f.zero = oldZero + return + } + // The sharp flag forces printing a decimal point for non-binary formats + // and retains trailing zeros, which we may need to restore. + if f.sharp && verb != 'b' { + digits := 0 + switch verb { + case 'v', 'g', 'G', 'x': + digits = prec + // If no precision is set explicitly use a precision of 6. + if digits == -1 { + digits = 6 + } + } + + // Buffer pre-allocated with enough room for + // exponent notations of the form "e+123" or "p-1023". + var tailBuf [6]byte + tail := tailBuf[:0] + + hasDecimalPoint := false + // Starting from i = 1 to skip sign at num[0]. + for i := 1; i < len(num); i++ { + switch num[i] { + case '.': + hasDecimalPoint = true + case 'p', 'P': + tail = append(tail, num[i:]...) + num = num[:i] + case 'e', 'E': + if verb != 'x' && verb != 'X' { + tail = append(tail, num[i:]...) + num = num[:i] + break + } + fallthrough + default: + digits-- + } + } + if !hasDecimalPoint { + num = append(num, '.') + } + for digits > 0 { + num = append(num, '0') + digits-- + } + num = append(num, tail...) + } + // We want a sign if asked for and if the sign is not positive. + if f.plus || num[0] != '+' { + // If we're zero padding to the left we want the sign before the + // leading zeros. Achieve this by writing the sign out and then padding + // the unsigned number. + if f.zero && f.widPresent && f.wid > len(num) { + f.buf.WriteSingleByte(num[0]) + f.writePadding(f.wid - len(num)) + f.buf.Write(num[1:]) + return + } + f.pad(num) + return + } + // No sign to show and the number is positive; just print the unsigned + // number. + f.pad(num[1:]) +} + +// Use simple []byte instead of bytes.Buffer to avoid large dependency. +type fmtbuf []byte + +func (b *fmtbuf) Write(p []byte) { + if len(*b)+len(p) > MaxStringLen { + panic(ErrStringLimit) + } + + *b = append(*b, p...) +} + +func (b *fmtbuf) WriteString(s string) { + if len(*b)+len(s) > MaxStringLen { + panic(ErrStringLimit) + } + + *b = append(*b, s...) +} + +func (b *fmtbuf) WriteSingleByte(c byte) { + if len(*b) >= MaxStringLen { + panic(ErrStringLimit) + } + + *b = append(*b, c) +} + +func (b *fmtbuf) WriteRune(r rune) { + if len(*b)+utf8.RuneLen(r) > MaxStringLen { + panic(ErrStringLimit) + } + + if r < utf8.RuneSelf { + *b = append(*b, byte(r)) + return + } + + b2 := *b + n := len(b2) + for n+utf8.UTFMax > cap(b2) { + b2 = append(b2, 0) + } + w := utf8.EncodeRune(b2[n:n+utf8.UTFMax], r) + *b = b2[:n+w] +} + +// pp is used to store a printer's state and is reused with sync.Pool to avoid +// allocations. +type pp struct { + buf fmtbuf + + // arg holds the current item. + arg Object + + // fmt is used to format basic items such as integers or strings. + fmt formatter + + // reordered records whether the format string used argument reordering. + reordered bool + + // goodArgNum records whether the most recent reordering directive was + // valid. + goodArgNum bool + + // erroring is set when printing an error string to guard against calling + // handleMethods. + erroring bool +} + +var ppFree = sync.Pool{ + New: func() interface{} { return new(pp) }, +} + +// newPrinter allocates a new pp struct or grabs a cached one. +func newPrinter() *pp { + p := ppFree.Get().(*pp) + p.erroring = false + p.fmt.init(&p.buf) + return p +} + +// free saves used pp structs in ppFree; avoids an allocation per invocation. +func (p *pp) free() { + // Proper usage of a sync.Pool requires each entry to have approximately + // the same memory cost. To obtain this property when the stored type + // contains a variably-sized fmtbuf, we add a hard limit on the maximum + // fmtbuf to place back in the pool. + // + // See https://golang.org/issue/23199 + if cap(p.buf) > 64<<10 { + return + } + + p.buf = p.buf[:0] + p.arg = nil + ppFree.Put(p) +} + +func (p *pp) Width() (wid int, ok bool) { + return p.fmt.wid, p.fmt.widPresent +} + +func (p *pp) Precision() (prec int, ok bool) { + return p.fmt.prec, p.fmt.precPresent +} + +func (p *pp) Flag(b int) bool { + switch b { + case '-': + return p.fmt.minus + case '+': + return p.fmt.plus || p.fmt.plusV + case '#': + return p.fmt.sharp || p.fmt.sharpV + case ' ': + return p.fmt.space + case '0': + return p.fmt.zero + } + return false +} + +// Implement Write so we can call Fprintf on a pp (through State), for +// recursive use in custom verbs. +func (p *pp) Write(b []byte) (ret int, err error) { + p.buf.Write(b) + return len(b), nil +} + +// Implement WriteString so that we can call io.WriteString +// on a pp (through state), for efficiency. +func (p *pp) WriteString(s string) (ret int, err error) { + p.buf.WriteString(s) + return len(s), nil +} + +func (p *pp) WriteRune(r rune) (ret int, err error) { + p.buf.WriteRune(r) + return utf8.RuneLen(r), nil +} + +func (p *pp) WriteSingleByte(c byte) (ret int, err error) { + p.buf.WriteSingleByte(c) + return 1, nil +} + +// tooLarge reports whether the magnitude of the integer is +// too large to be used as a formatting width or precision. +func tooLarge(x int) bool { + const max int = 1e6 + return x > max || x < -max +} + +// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no +// number present. +func parsenum(s string, start, end int) (num int, isnum bool, newi int) { + if start >= end { + return 0, false, end + } + for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ { + if tooLarge(num) { + return 0, false, end // Overflow; crazy long number most likely. + } + num = num*10 + int(s[newi]-'0') + isnum = true + } + return +} + +func (p *pp) badVerb(verb rune) { + p.erroring = true + _, _ = p.WriteString(percentBangString) + _, _ = p.WriteRune(verb) + _, _ = p.WriteSingleByte('(') + switch { + case p.arg != nil: + _, _ = p.WriteString(p.arg.String()) + _, _ = p.WriteSingleByte('=') + p.printArg(p.arg, 'v') + default: + _, _ = p.WriteString(UndefinedValue.String()) + } + _, _ = p.WriteSingleByte(')') + p.erroring = false +} + +func (p *pp) fmtBool(v bool, verb rune) { + switch verb { + case 't', 'v': + p.fmt.fmtBoolean(v) + default: + p.badVerb(verb) + } +} + +// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or +// not, as requested, by temporarily setting the sharp flag. +func (p *pp) fmt0x64(v uint64, leading0x bool) { + sharp := p.fmt.sharp + p.fmt.sharp = leading0x + p.fmt.fmtInteger(v, 16, unsigned, 'v', ldigits) + p.fmt.sharp = sharp +} + +// fmtInteger formats a signed or unsigned integer. +func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) { + switch verb { + case 'v': + if p.fmt.sharpV && !isSigned { + p.fmt0x64(v, true) + } else { + p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits) + } + case 'd': + p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits) + case 'b': + p.fmt.fmtInteger(v, 2, isSigned, verb, ldigits) + case 'o', 'O': + p.fmt.fmtInteger(v, 8, isSigned, verb, ldigits) + case 'x': + p.fmt.fmtInteger(v, 16, isSigned, verb, ldigits) + case 'X': + p.fmt.fmtInteger(v, 16, isSigned, verb, udigits) + case 'c': + p.fmt.fmtC(v) + case 'q': + if v <= utf8.MaxRune { + p.fmt.fmtQc(v) + } else { + p.badVerb(verb) + } + case 'U': + p.fmt.fmtUnicode(v) + default: + p.badVerb(verb) + } +} + +// fmtFloat formats a float. The default precision for each verb +// is specified as last argument in the call to fmt_float. +func (p *pp) fmtFloat(v float64, size int, verb rune) { + switch verb { + case 'v': + p.fmt.fmtFloat(v, size, 'g', -1) + case 'b', 'g', 'G', 'x', 'X': + p.fmt.fmtFloat(v, size, verb, -1) + case 'f', 'e', 'E': + p.fmt.fmtFloat(v, size, verb, 6) + case 'F': + p.fmt.fmtFloat(v, size, 'f', 6) + default: + p.badVerb(verb) + } +} + +func (p *pp) fmtString(v string, verb rune) { + switch verb { + case 'v': + if p.fmt.sharpV { + p.fmt.fmtQ(v) + } else { + p.fmt.fmtS(v) + } + case 's': + p.fmt.fmtS(v) + case 'x': + p.fmt.fmtSx(v, ldigits) + case 'X': + p.fmt.fmtSx(v, udigits) + case 'q': + p.fmt.fmtQ(v) + default: + p.badVerb(verb) + } +} + +func (p *pp) fmtBytes(v []byte, verb rune, typeString string) { + switch verb { + case 'v', 'd': + if p.fmt.sharpV { + _, _ = p.WriteString(typeString) + if v == nil { + _, _ = p.WriteString(nilParenString) + return + } + _, _ = p.WriteSingleByte('{') + for i, c := range v { + if i > 0 { + _, _ = p.WriteString(commaSpaceString) + } + p.fmt0x64(uint64(c), true) + } + _, _ = p.WriteSingleByte('}') + } else { + _, _ = p.WriteSingleByte('[') + for i, c := range v { + if i > 0 { + _, _ = p.WriteSingleByte(' ') + } + p.fmt.fmtInteger(uint64(c), 10, unsigned, verb, ldigits) + } + _, _ = p.WriteSingleByte(']') + } + case 's': + p.fmt.fmtBs(v) + case 'x': + p.fmt.fmtBx(v, ldigits) + case 'X': + p.fmt.fmtBx(v, udigits) + case 'q': + p.fmt.fmtQ(string(v)) + } +} + +func (p *pp) printArg(arg Object, verb rune) { + p.arg = arg + + if arg == nil { + arg = UndefinedValue + } + + // Special processing considerations. + // %T (the value's type) and %p (its address) are special; we always do + // them first. + switch verb { + case 'T': + p.fmt.fmtS(arg.TypeName()) + return + case 'v': + p.fmt.fmtS(arg.String()) + return + } + + // Some types can be done without reflection. + switch f := arg.(type) { + case *Bool: + p.fmtBool(!f.IsFalsy(), verb) + case *Float: + p.fmtFloat(f.Value, 64, verb) + case *Int: + p.fmtInteger(uint64(f.Value), signed, verb) + case *String: + p.fmtString(f.Value, verb) + case *Bytes: + p.fmtBytes(f.Value, verb, "[]byte") + default: + p.fmtString(f.String(), verb) + } +} + +// intFromArg gets the argNumth element of a. On return, isInt reports whether +// the argument has integer type. +func intFromArg(a []Object, argNum int) (num int, isInt bool, newArgNum int) { + newArgNum = argNum + if argNum < len(a) { + var num64 int64 + num64, isInt = ToInt64(a[argNum]) + num = int(num64) + newArgNum = argNum + 1 + if tooLarge(num) { + num = 0 + isInt = false + } + } + return +} + +// parseArgNumber returns the value of the bracketed number, minus 1 +// (explicit argument numbers are one-indexed but we want zero-indexed). +// The opening bracket is known to be present at format[0]. +// The returned values are the index, the number of bytes to consume +// up to the closing paren, if present, and whether the number parsed +// ok. The bytes to consume will be 1 if no closing paren is present. +func parseArgNumber(format string) (index int, wid int, ok bool) { + // There must be at least 3 bytes: [n]. + if len(format) < 3 { + return 0, 1, false + } + + // Find closing bracket. + for i := 1; i < len(format); i++ { + if format[i] == ']' { + width, ok, newi := parsenum(format, 1, i) + if !ok || newi != i { + return 0, i + 1, false + } + // arg numbers are one-indexed andskip paren. + return width - 1, i + 1, true + } + } + return 0, 1, false +} + +// argNumber returns the next argument to evaluate, which is either the value +// of the passed-in argNum or the value of the bracketed integer that begins +// format[i:]. It also returns the new value of i, that is, the index of the +// next byte of the format to process. +func (p *pp) argNumber( + argNum int, + format string, + i int, + numArgs int, +) (newArgNum, newi int, found bool) { + if len(format) <= i || format[i] != '[' { + return argNum, i, false + } + p.reordered = true + index, wid, ok := parseArgNumber(format[i:]) + if ok && 0 <= index && index < numArgs { + return index, i + wid, true + } + p.goodArgNum = false + return argNum, i + wid, ok +} + +func (p *pp) badArgNum(verb rune) { + _, _ = p.WriteString(percentBangString) + _, _ = p.WriteRune(verb) + _, _ = p.WriteString(badIndexString) +} + +func (p *pp) missingArg(verb rune) { + _, _ = p.WriteString(percentBangString) + _, _ = p.WriteRune(verb) + _, _ = p.WriteString(missingString) +} + +func (p *pp) doFormat(format string, a []Object) (err error) { + defer func() { + if r := recover(); r != nil { + if e, ok := r.(error); ok && e == ErrStringLimit { + err = e + return + } + panic(r) + } + }() + + end := len(format) + argNum := 0 // we process one argument per non-trivial format + afterIndex := false // previous item in format was an index like [3]. + p.reordered = false +formatLoop: + for i := 0; i < end; { + p.goodArgNum = true + lasti := i + for i < end && format[i] != '%' { + i++ + } + if i > lasti { + _, _ = p.WriteString(format[lasti:i]) + } + if i >= end { + // done processing format string + break + } + + // Process one verb + i++ + + // Do we have flags? + p.fmt.clearFlags() + simpleFormat: + for ; i < end; i++ { + c := format[i] + switch c { + case '#': + p.fmt.sharp = true + case '0': + // Only allow zero padding to the left. + p.fmt.zero = !p.fmt.minus + case '+': + p.fmt.plus = true + case '-': + p.fmt.minus = true + p.fmt.zero = false // Do not pad with zeros to the right. + case ' ': + p.fmt.space = true + default: + // Fast path for common case of ascii lower case simple verbs + // without precision or width or argument indices. + if 'a' <= c && c <= 'z' && argNum < len(a) { + if c == 'v' { + // Go syntax + p.fmt.sharpV = p.fmt.sharp + p.fmt.sharp = false + // Struct-field syntax + p.fmt.plusV = p.fmt.plus + p.fmt.plus = false + } + p.printArg(a[argNum], rune(c)) + argNum++ + i++ + continue formatLoop + } + // Format is more complex than simple flags and a verb or is + // malformed. + break simpleFormat + } + } + + // Do we have an explicit argument index? + argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) + + // Do we have width? + if i < end && format[i] == '*' { + i++ + p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum) + + if !p.fmt.widPresent { + _, _ = p.WriteString(badWidthString) + } + + // We have a negative width, so take its value and ensure + // that the minus flag is set + if p.fmt.wid < 0 { + p.fmt.wid = -p.fmt.wid + p.fmt.minus = true + p.fmt.zero = false // Do not pad with zeros to the right. + } + afterIndex = false + } else { + p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) + if afterIndex && p.fmt.widPresent { // "%[3]2d" + p.goodArgNum = false + } + } + + // Do we have precision? + if i+1 < end && format[i] == '.' { + i++ + if afterIndex { // "%[3].2d" + p.goodArgNum = false + } + argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) + if i < end && format[i] == '*' { + i++ + p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum) + // Negative precision arguments don't make sense + if p.fmt.prec < 0 { + p.fmt.prec = 0 + p.fmt.precPresent = false + } + if !p.fmt.precPresent { + _, _ = p.WriteString(badPrecString) + } + afterIndex = false + } else { + p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i, end) + if !p.fmt.precPresent { + p.fmt.prec = 0 + p.fmt.precPresent = true + } + } + } + + if !afterIndex { + argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) + } + + if i >= end { + _, _ = p.WriteString(noVerbString) + break + } + + verb, size := rune(format[i]), 1 + if verb >= utf8.RuneSelf { + verb, size = utf8.DecodeRuneInString(format[i:]) + } + i += size + + switch { + case verb == '%': + // Percent does not absorb operands and ignores f.wid and f.prec. + _, _ = p.WriteSingleByte('%') + case !p.goodArgNum: + p.badArgNum(verb) + case argNum >= len(a): + // No argument left over to print for the current verb. + p.missingArg(verb) + case verb == 'v': + // Go syntax + p.fmt.sharpV = p.fmt.sharp + p.fmt.sharp = false + // Struct-field syntax + p.fmt.plusV = p.fmt.plus + p.fmt.plus = false + fallthrough + default: + p.printArg(a[argNum], verb) + argNum++ + } + } + + // Check for extra arguments unless the call accessed the arguments + // out of order, in which case it's too expensive to detect if they've all + // been used and arguably OK if they're not. + if !p.reordered && argNum < len(a) { + p.fmt.clearFlags() + _, _ = p.WriteString(extraString) + for i, arg := range a[argNum:] { + if i > 0 { + _, _ = p.WriteString(commaSpaceString) + } + if arg == nil { + _, _ = p.WriteString(UndefinedValue.String()) + } else { + _, _ = p.WriteString(arg.TypeName()) + _, _ = p.WriteSingleByte('=') + p.printArg(arg, 'v') + } + } + _, _ = p.WriteSingleByte(')') + } + + return nil +} + +// Format is like fmt.Sprintf but using Objects. +func Format(format string, a ...Object) (string, error) { + p := newPrinter() + err := p.doFormat(format, a) + s := string(p.buf) + p.free() + + return s, err +} diff --git a/vendor/github.com/d5/tengo/v2/go.mod b/vendor/github.com/d5/tengo/v2/go.mod new file mode 100644 index 00000000..732ff142 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/go.mod @@ -0,0 +1,3 @@ +module github.com/d5/tengo/v2 + +go 1.13 diff --git a/vendor/github.com/d5/tengo/v2/go.sum b/vendor/github.com/d5/tengo/v2/go.sum new file mode 100644 index 00000000..e69de29b diff --git a/vendor/github.com/d5/tengo/v2/instructions.go b/vendor/github.com/d5/tengo/v2/instructions.go new file mode 100644 index 00000000..eb1fbf27 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/instructions.go @@ -0,0 +1,61 @@ +package tengo + +import ( + "fmt" + + "github.com/d5/tengo/v2/parser" +) + +// MakeInstruction returns a bytecode for an opcode and the operands. +func MakeInstruction(opcode parser.Opcode, operands ...int) []byte { + numOperands := parser.OpcodeOperands[opcode] + + totalLen := 1 + for _, w := range numOperands { + totalLen += w + } + + instruction := make([]byte, totalLen) + instruction[0] = opcode + + offset := 1 + for i, o := range operands { + width := numOperands[i] + switch width { + case 1: + instruction[offset] = byte(o) + case 2: + n := uint16(o) + instruction[offset] = byte(n >> 8) + instruction[offset+1] = byte(n) + } + offset += width + } + return instruction +} + +// FormatInstructions returns string representation of bytecode instructions. +func FormatInstructions(b []byte, posOffset int) []string { + var out []string + + i := 0 + for i < len(b) { + numOperands := parser.OpcodeOperands[b[i]] + operands, read := parser.ReadOperands(numOperands, b[i+1:]) + + switch len(numOperands) { + case 0: + out = append(out, fmt.Sprintf("%04d %-7s", + posOffset+i, parser.OpcodeNames[b[i]])) + case 1: + out = append(out, fmt.Sprintf("%04d %-7s %-5d", + posOffset+i, parser.OpcodeNames[b[i]], operands[0])) + case 2: + out = append(out, fmt.Sprintf("%04d %-7s %-5d %-5d", + posOffset+i, parser.OpcodeNames[b[i]], + operands[0], operands[1])) + } + i += 1 + read + } + return out +} diff --git a/vendor/github.com/d5/tengo/v2/iterator.go b/vendor/github.com/d5/tengo/v2/iterator.go new file mode 100644 index 00000000..13adbbab --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/iterator.go @@ -0,0 +1,209 @@ +package tengo + +// Iterator represents an iterator for underlying data type. +type Iterator interface { + Object + + // Next returns true if there are more elements to iterate. + Next() bool + + // Key returns the key or index value of the current element. + Key() Object + + // Value returns the value of the current element. + Value() Object +} + +// ArrayIterator is an iterator for an array. +type ArrayIterator struct { + ObjectImpl + v []Object + i int + l int +} + +// TypeName returns the name of the type. +func (i *ArrayIterator) TypeName() string { + return "array-iterator" +} + +func (i *ArrayIterator) String() string { + return "" +} + +// IsFalsy returns true if the value of the type is falsy. +func (i *ArrayIterator) IsFalsy() bool { + return true +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (i *ArrayIterator) Equals(Object) bool { + return false +} + +// Copy returns a copy of the type. +func (i *ArrayIterator) Copy() Object { + return &ArrayIterator{v: i.v, i: i.i, l: i.l} +} + +// Next returns true if there are more elements to iterate. +func (i *ArrayIterator) Next() bool { + i.i++ + return i.i <= i.l +} + +// Key returns the key or index value of the current element. +func (i *ArrayIterator) Key() Object { + return &Int{Value: int64(i.i - 1)} +} + +// Value returns the value of the current element. +func (i *ArrayIterator) Value() Object { + return i.v[i.i-1] +} + +// BytesIterator represents an iterator for a string. +type BytesIterator struct { + ObjectImpl + v []byte + i int + l int +} + +// TypeName returns the name of the type. +func (i *BytesIterator) TypeName() string { + return "bytes-iterator" +} + +func (i *BytesIterator) String() string { + return "" +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (i *BytesIterator) Equals(Object) bool { + return false +} + +// Copy returns a copy of the type. +func (i *BytesIterator) Copy() Object { + return &BytesIterator{v: i.v, i: i.i, l: i.l} +} + +// Next returns true if there are more elements to iterate. +func (i *BytesIterator) Next() bool { + i.i++ + return i.i <= i.l +} + +// Key returns the key or index value of the current element. +func (i *BytesIterator) Key() Object { + return &Int{Value: int64(i.i - 1)} +} + +// Value returns the value of the current element. +func (i *BytesIterator) Value() Object { + return &Int{Value: int64(i.v[i.i-1])} +} + +// MapIterator represents an iterator for the map. +type MapIterator struct { + ObjectImpl + v map[string]Object + k []string + i int + l int +} + +// TypeName returns the name of the type. +func (i *MapIterator) TypeName() string { + return "map-iterator" +} + +func (i *MapIterator) String() string { + return "" +} + +// IsFalsy returns true if the value of the type is falsy. +func (i *MapIterator) IsFalsy() bool { + return true +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (i *MapIterator) Equals(Object) bool { + return false +} + +// Copy returns a copy of the type. +func (i *MapIterator) Copy() Object { + return &MapIterator{v: i.v, k: i.k, i: i.i, l: i.l} +} + +// Next returns true if there are more elements to iterate. +func (i *MapIterator) Next() bool { + i.i++ + return i.i <= i.l +} + +// Key returns the key or index value of the current element. +func (i *MapIterator) Key() Object { + k := i.k[i.i-1] + return &String{Value: k} +} + +// Value returns the value of the current element. +func (i *MapIterator) Value() Object { + k := i.k[i.i-1] + return i.v[k] +} + +// StringIterator represents an iterator for a string. +type StringIterator struct { + ObjectImpl + v []rune + i int + l int +} + +// TypeName returns the name of the type. +func (i *StringIterator) TypeName() string { + return "string-iterator" +} + +func (i *StringIterator) String() string { + return "" +} + +// IsFalsy returns true if the value of the type is falsy. +func (i *StringIterator) IsFalsy() bool { + return true +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (i *StringIterator) Equals(Object) bool { + return false +} + +// Copy returns a copy of the type. +func (i *StringIterator) Copy() Object { + return &StringIterator{v: i.v, i: i.i, l: i.l} +} + +// Next returns true if there are more elements to iterate. +func (i *StringIterator) Next() bool { + i.i++ + return i.i <= i.l +} + +// Key returns the key or index value of the current element. +func (i *StringIterator) Key() Object { + return &Int{Value: int64(i.i - 1)} +} + +// Value returns the value of the current element. +func (i *StringIterator) Value() Object { + return &Char{Value: i.v[i.i-1]} +} diff --git a/vendor/github.com/d5/tengo/v2/modules.go b/vendor/github.com/d5/tengo/v2/modules.go new file mode 100644 index 00000000..c8fcde7f --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/modules.go @@ -0,0 +1,93 @@ +package tengo + +// Importable interface represents importable module instance. +type Importable interface { + // Import should return either an Object or module source code ([]byte). + Import(moduleName string) (interface{}, error) +} + +// ModuleMap represents a set of named modules. Use NewModuleMap to create a +// new module map. +type ModuleMap struct { + m map[string]Importable +} + +// NewModuleMap creates a new module map. +func NewModuleMap() *ModuleMap { + return &ModuleMap{ + m: make(map[string]Importable), + } +} + +// Add adds an import module. +func (m *ModuleMap) Add(name string, module Importable) { + m.m[name] = module +} + +// AddBuiltinModule adds a builtin module. +func (m *ModuleMap) AddBuiltinModule(name string, attrs map[string]Object) { + m.m[name] = &BuiltinModule{Attrs: attrs} +} + +// AddSourceModule adds a source module. +func (m *ModuleMap) AddSourceModule(name string, src []byte) { + m.m[name] = &SourceModule{Src: src} +} + +// Remove removes a named module. +func (m *ModuleMap) Remove(name string) { + delete(m.m, name) +} + +// Get returns an import module identified by name. It returns if the name is +// not found. +func (m *ModuleMap) Get(name string) Importable { + return m.m[name] +} + +// GetBuiltinModule returns a builtin module identified by name. It returns +// if the name is not found or the module is not a builtin module. +func (m *ModuleMap) GetBuiltinModule(name string) *BuiltinModule { + mod, _ := m.m[name].(*BuiltinModule) + return mod +} + +// GetSourceModule returns a source module identified by name. It returns if +// the name is not found or the module is not a source module. +func (m *ModuleMap) GetSourceModule(name string) *SourceModule { + mod, _ := m.m[name].(*SourceModule) + return mod +} + +// Copy creates a copy of the module map. +func (m *ModuleMap) Copy() *ModuleMap { + c := &ModuleMap{ + m: make(map[string]Importable), + } + for name, mod := range m.m { + c.m[name] = mod + } + return c +} + +// Len returns the number of named modules. +func (m *ModuleMap) Len() int { + return len(m.m) +} + +// AddMap adds named modules from another module map. +func (m *ModuleMap) AddMap(o *ModuleMap) { + for name, mod := range o.m { + m.m[name] = mod + } +} + +// SourceModule is an importable module that's written in Tengo. +type SourceModule struct { + Src []byte +} + +// Import returns a module source code. +func (m *SourceModule) Import(_ string) (interface{}, error) { + return m.Src, nil +} diff --git a/vendor/github.com/d5/tengo/v2/objects.go b/vendor/github.com/d5/tengo/v2/objects.go new file mode 100644 index 00000000..27c1d493 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/objects.go @@ -0,0 +1,1581 @@ +package tengo + +import ( + "bytes" + "fmt" + "math" + "strconv" + "strings" + "time" + + "github.com/d5/tengo/v2/parser" + "github.com/d5/tengo/v2/token" +) + +var ( + // TrueValue represents a true value. + TrueValue Object = &Bool{value: true} + + // FalseValue represents a false value. + FalseValue Object = &Bool{value: false} + + // UndefinedValue represents an undefined value. + UndefinedValue Object = &Undefined{} +) + +// Object represents an object in the VM. +type Object interface { + // TypeName should return the name of the type. + TypeName() string + + // String should return a string representation of the type's value. + String() string + + // BinaryOp should return another object that is the result of a given + // binary operator and a right-hand side object. If BinaryOp returns an + // error, the VM will treat it as a run-time error. + BinaryOp(op token.Token, rhs Object) (Object, error) + + // IsFalsy should return true if the value of the type should be considered + // as falsy. + IsFalsy() bool + + // Equals should return true if the value of the type should be considered + // as equal to the value of another object. + Equals(another Object) bool + + // Copy should return a copy of the type (and its value). Copy function + // will be used for copy() builtin function which is expected to deep-copy + // the values generally. + Copy() Object + + // IndexGet should take an index Object and return a result Object or an + // error for indexable objects. Indexable is an object that can take an + // index and return an object. If error is returned, the runtime will treat + // it as a run-time error and ignore returned value. If Object is not + // indexable, ErrNotIndexable should be returned as error. If nil is + // returned as value, it will be converted to UndefinedToken value by the + // runtime. + IndexGet(index Object) (value Object, err error) + + // IndexSet should take an index Object and a value Object for index + // assignable objects. Index assignable is an object that can take an index + // and a value on the left-hand side of the assignment statement. If Object + // is not index assignable, ErrNotIndexAssignable should be returned as + // error. If an error is returned, it will be treated as a run-time error. + IndexSet(index, value Object) error + + // Iterate should return an Iterator for the type. + Iterate() Iterator + + // CanIterate should return whether the Object can be Iterated. + CanIterate() bool + + // Call should take an arbitrary number of arguments and returns a return + // value and/or an error, which the VM will consider as a run-time error. + Call(args ...Object) (ret Object, err error) + + // CanCall should return whether the Object can be Called. + CanCall() bool +} + +// ObjectImpl represents a default Object Implementation. To defined a new +// value type, one can embed ObjectImpl in their type declarations to avoid +// implementing all non-significant methods. TypeName() and String() methods +// still need to be implemented. +type ObjectImpl struct { +} + +// TypeName returns the name of the type. +func (o *ObjectImpl) TypeName() string { + panic(ErrNotImplemented) +} + +func (o *ObjectImpl) String() string { + panic(ErrNotImplemented) +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *ObjectImpl) BinaryOp(_ token.Token, _ Object) (Object, error) { + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *ObjectImpl) Copy() Object { + return nil +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *ObjectImpl) IsFalsy() bool { + return false +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *ObjectImpl) Equals(x Object) bool { + return o == x +} + +// IndexGet returns an element at a given index. +func (o *ObjectImpl) IndexGet(_ Object) (res Object, err error) { + return nil, ErrNotIndexable +} + +// IndexSet sets an element at a given index. +func (o *ObjectImpl) IndexSet(_, _ Object) (err error) { + return ErrNotIndexAssignable +} + +// Iterate returns an iterator. +func (o *ObjectImpl) Iterate() Iterator { + return nil +} + +// CanIterate returns whether the Object can be Iterated. +func (o *ObjectImpl) CanIterate() bool { + return false +} + +// Call takes an arbitrary number of arguments and returns a return value +// and/or an error. +func (o *ObjectImpl) Call(_ ...Object) (ret Object, err error) { + return nil, nil +} + +// CanCall returns whether the Object can be Called. +func (o *ObjectImpl) CanCall() bool { + return false +} + +// Array represents an array of objects. +type Array struct { + ObjectImpl + Value []Object +} + +// TypeName returns the name of the type. +func (o *Array) TypeName() string { + return "array" +} + +func (o *Array) String() string { + var elements []string + for _, e := range o.Value { + elements = append(elements, e.String()) + } + return fmt.Sprintf("[%s]", strings.Join(elements, ", ")) +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) { + if rhs, ok := rhs.(*Array); ok { + switch op { + case token.Add: + if len(rhs.Value) == 0 { + return o, nil + } + return &Array{Value: append(o.Value, rhs.Value...)}, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Array) Copy() Object { + var c []Object + for _, elem := range o.Value { + c = append(c, elem.Copy()) + } + return &Array{Value: c} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Array) IsFalsy() bool { + return len(o.Value) == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Array) Equals(x Object) bool { + var xVal []Object + switch x := x.(type) { + case *Array: + xVal = x.Value + case *ImmutableArray: + xVal = x.Value + default: + return false + } + if len(o.Value) != len(xVal) { + return false + } + for i, e := range o.Value { + if !e.Equals(xVal[i]) { + return false + } + } + return true +} + +// IndexGet returns an element at a given index. +func (o *Array) IndexGet(index Object) (res Object, err error) { + intIdx, ok := index.(*Int) + if !ok { + err = ErrInvalidIndexType + return + } + idxVal := int(intIdx.Value) + if idxVal < 0 || idxVal >= len(o.Value) { + res = UndefinedValue + return + } + res = o.Value[idxVal] + return +} + +// IndexSet sets an element at a given index. +func (o *Array) IndexSet(index, value Object) (err error) { + intIdx, ok := ToInt(index) + if !ok { + err = ErrInvalidIndexType + return + } + if intIdx < 0 || intIdx >= len(o.Value) { + err = ErrIndexOutOfBounds + return + } + o.Value[intIdx] = value + return nil +} + +// Iterate creates an array iterator. +func (o *Array) Iterate() Iterator { + return &ArrayIterator{ + v: o.Value, + l: len(o.Value), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *Array) CanIterate() bool { + return true +} + +// Bool represents a boolean value. +type Bool struct { + ObjectImpl + + // this is intentionally non-public to force using objects.TrueValue and + // FalseValue always + value bool +} + +func (o *Bool) String() string { + if o.value { + return "true" + } + + return "false" +} + +// TypeName returns the name of the type. +func (o *Bool) TypeName() string { + return "bool" +} + +// Copy returns a copy of the type. +func (o *Bool) Copy() Object { + return o +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Bool) IsFalsy() bool { + return !o.value +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Bool) Equals(x Object) bool { + return o == x +} + +// GobDecode decodes bool value from input bytes. +func (o *Bool) GobDecode(b []byte) (err error) { + o.value = b[0] == 1 + return +} + +// GobEncode encodes bool values into bytes. +func (o *Bool) GobEncode() (b []byte, err error) { + if o.value { + b = []byte{1} + } else { + b = []byte{0} + } + return +} + +// BuiltinFunction represents a builtin function. +type BuiltinFunction struct { + ObjectImpl + Name string + Value CallableFunc +} + +// TypeName returns the name of the type. +func (o *BuiltinFunction) TypeName() string { + return "builtin-function:" + o.Name +} + +func (o *BuiltinFunction) String() string { + return "" +} + +// Copy returns a copy of the type. +func (o *BuiltinFunction) Copy() Object { + return &BuiltinFunction{Value: o.Value} +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *BuiltinFunction) Equals(_ Object) bool { + return false +} + +// Call executes a builtin function. +func (o *BuiltinFunction) Call(args ...Object) (Object, error) { + return o.Value(args...) +} + +// CanCall returns whether the Object can be Called. +func (o *BuiltinFunction) CanCall() bool { + return true +} + +// BuiltinModule is an importable module that's written in Go. +type BuiltinModule struct { + Attrs map[string]Object +} + +// Import returns an immutable map for the module. +func (m *BuiltinModule) Import(moduleName string) (interface{}, error) { + return m.AsImmutableMap(moduleName), nil +} + +// AsImmutableMap converts builtin module into an immutable map. +func (m *BuiltinModule) AsImmutableMap(moduleName string) *ImmutableMap { + attrs := make(map[string]Object, len(m.Attrs)) + for k, v := range m.Attrs { + attrs[k] = v.Copy() + } + attrs["__module_name__"] = &String{Value: moduleName} + return &ImmutableMap{Value: attrs} +} + +// Bytes represents a byte array. +type Bytes struct { + ObjectImpl + Value []byte +} + +func (o *Bytes) String() string { + return string(o.Value) +} + +// TypeName returns the name of the type. +func (o *Bytes) TypeName() string { + return "bytes" +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Bytes) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch op { + case token.Add: + switch rhs := rhs.(type) { + case *Bytes: + if len(o.Value)+len(rhs.Value) > MaxBytesLen { + return nil, ErrBytesLimit + } + return &Bytes{Value: append(o.Value, rhs.Value...)}, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Bytes) Copy() Object { + return &Bytes{Value: append([]byte{}, o.Value...)} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Bytes) IsFalsy() bool { + return len(o.Value) == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Bytes) Equals(x Object) bool { + t, ok := x.(*Bytes) + if !ok { + return false + } + return bytes.Equal(o.Value, t.Value) +} + +// IndexGet returns an element (as Int) at a given index. +func (o *Bytes) IndexGet(index Object) (res Object, err error) { + intIdx, ok := index.(*Int) + if !ok { + err = ErrInvalidIndexType + return + } + idxVal := int(intIdx.Value) + if idxVal < 0 || idxVal >= len(o.Value) { + res = UndefinedValue + return + } + res = &Int{Value: int64(o.Value[idxVal])} + return +} + +// Iterate creates a bytes iterator. +func (o *Bytes) Iterate() Iterator { + return &BytesIterator{ + v: o.Value, + l: len(o.Value), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *Bytes) CanIterate() bool { + return true +} + +// Char represents a character value. +type Char struct { + ObjectImpl + Value rune +} + +func (o *Char) String() string { + return string(o.Value) +} + +// TypeName returns the name of the type. +func (o *Char) TypeName() string { + return "char" +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Char) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch rhs := rhs.(type) { + case *Char: + switch op { + case token.Add: + r := o.Value + rhs.Value + if r == o.Value { + return o, nil + } + return &Char{Value: r}, nil + case token.Sub: + r := o.Value - rhs.Value + if r == o.Value { + return o, nil + } + return &Char{Value: r}, nil + case token.Less: + if o.Value < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + } + case *Int: + switch op { + case token.Add: + r := o.Value + rune(rhs.Value) + if r == o.Value { + return o, nil + } + return &Char{Value: r}, nil + case token.Sub: + r := o.Value - rune(rhs.Value) + if r == o.Value { + return o, nil + } + return &Char{Value: r}, nil + case token.Less: + if int64(o.Value) < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if int64(o.Value) > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if int64(o.Value) <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if int64(o.Value) >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Char) Copy() Object { + return &Char{Value: o.Value} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Char) IsFalsy() bool { + return o.Value == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Char) Equals(x Object) bool { + t, ok := x.(*Char) + if !ok { + return false + } + return o.Value == t.Value +} + +// CompiledFunction represents a compiled function. +type CompiledFunction struct { + ObjectImpl + Instructions []byte + NumLocals int // number of local variables (including function parameters) + NumParameters int + VarArgs bool + SourceMap map[int]parser.Pos + Free []*ObjectPtr +} + +// TypeName returns the name of the type. +func (o *CompiledFunction) TypeName() string { + return "compiled-function" +} + +func (o *CompiledFunction) String() string { + return "" +} + +// Copy returns a copy of the type. +func (o *CompiledFunction) Copy() Object { + return &CompiledFunction{ + Instructions: append([]byte{}, o.Instructions...), + NumLocals: o.NumLocals, + NumParameters: o.NumParameters, + VarArgs: o.VarArgs, + Free: append([]*ObjectPtr{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers + } +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *CompiledFunction) Equals(_ Object) bool { + return false +} + +// SourcePos returns the source position of the instruction at ip. +func (o *CompiledFunction) SourcePos(ip int) parser.Pos { + for ip >= 0 { + if p, ok := o.SourceMap[ip]; ok { + return p + } + ip-- + } + return parser.NoPos +} + +// CanCall returns whether the Object can be Called. +func (o *CompiledFunction) CanCall() bool { + return true +} + +// Error represents an error value. +type Error struct { + ObjectImpl + Value Object +} + +// TypeName returns the name of the type. +func (o *Error) TypeName() string { + return "error" +} + +func (o *Error) String() string { + if o.Value != nil { + return fmt.Sprintf("error: %s", o.Value.String()) + } + return "error" +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Error) IsFalsy() bool { + return true // error is always false. +} + +// Copy returns a copy of the type. +func (o *Error) Copy() Object { + return &Error{Value: o.Value.Copy()} +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Error) Equals(x Object) bool { + return o == x // pointer equality +} + +// IndexGet returns an element at a given index. +func (o *Error) IndexGet(index Object) (res Object, err error) { + if strIdx, _ := ToString(index); strIdx != "value" { + err = ErrInvalidIndexOnError + return + } + res = o.Value + return +} + +// Float represents a floating point number value. +type Float struct { + ObjectImpl + Value float64 +} + +func (o *Float) String() string { + return strconv.FormatFloat(o.Value, 'f', -1, 64) +} + +// TypeName returns the name of the type. +func (o *Float) TypeName() string { + return "float" +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Float) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch rhs := rhs.(type) { + case *Float: + switch op { + case token.Add: + r := o.Value + rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Sub: + r := o.Value - rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Mul: + r := o.Value * rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Quo: + r := o.Value / rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Less: + if o.Value < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + } + case *Int: + switch op { + case token.Add: + r := o.Value + float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Sub: + r := o.Value - float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Mul: + r := o.Value * float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Quo: + r := o.Value / float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Less: + if o.Value < float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value > float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value <= float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value >= float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Float) Copy() Object { + return &Float{Value: o.Value} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Float) IsFalsy() bool { + return math.IsNaN(o.Value) +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Float) Equals(x Object) bool { + t, ok := x.(*Float) + if !ok { + return false + } + return o.Value == t.Value +} + +// ImmutableArray represents an immutable array of objects. +type ImmutableArray struct { + ObjectImpl + Value []Object +} + +// TypeName returns the name of the type. +func (o *ImmutableArray) TypeName() string { + return "immutable-array" +} + +func (o *ImmutableArray) String() string { + var elements []string + for _, e := range o.Value { + elements = append(elements, e.String()) + } + return fmt.Sprintf("[%s]", strings.Join(elements, ", ")) +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *ImmutableArray) BinaryOp(op token.Token, rhs Object) (Object, error) { + if rhs, ok := rhs.(*ImmutableArray); ok { + switch op { + case token.Add: + return &Array{Value: append(o.Value, rhs.Value...)}, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *ImmutableArray) Copy() Object { + var c []Object + for _, elem := range o.Value { + c = append(c, elem.Copy()) + } + return &Array{Value: c} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *ImmutableArray) IsFalsy() bool { + return len(o.Value) == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *ImmutableArray) Equals(x Object) bool { + var xVal []Object + switch x := x.(type) { + case *Array: + xVal = x.Value + case *ImmutableArray: + xVal = x.Value + default: + return false + } + if len(o.Value) != len(xVal) { + return false + } + for i, e := range o.Value { + if !e.Equals(xVal[i]) { + return false + } + } + return true +} + +// IndexGet returns an element at a given index. +func (o *ImmutableArray) IndexGet(index Object) (res Object, err error) { + intIdx, ok := index.(*Int) + if !ok { + err = ErrInvalidIndexType + return + } + idxVal := int(intIdx.Value) + if idxVal < 0 || idxVal >= len(o.Value) { + res = UndefinedValue + return + } + res = o.Value[idxVal] + return +} + +// Iterate creates an array iterator. +func (o *ImmutableArray) Iterate() Iterator { + return &ArrayIterator{ + v: o.Value, + l: len(o.Value), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *ImmutableArray) CanIterate() bool { + return true +} + +// ImmutableMap represents an immutable map object. +type ImmutableMap struct { + ObjectImpl + Value map[string]Object +} + +// TypeName returns the name of the type. +func (o *ImmutableMap) TypeName() string { + return "immutable-map" +} + +func (o *ImmutableMap) String() string { + var pairs []string + for k, v := range o.Value { + pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String())) + } + return fmt.Sprintf("{%s}", strings.Join(pairs, ", ")) +} + +// Copy returns a copy of the type. +func (o *ImmutableMap) Copy() Object { + c := make(map[string]Object) + for k, v := range o.Value { + c[k] = v.Copy() + } + return &Map{Value: c} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *ImmutableMap) IsFalsy() bool { + return len(o.Value) == 0 +} + +// IndexGet returns the value for the given key. +func (o *ImmutableMap) IndexGet(index Object) (res Object, err error) { + strIdx, ok := ToString(index) + if !ok { + err = ErrInvalidIndexType + return + } + res, ok = o.Value[strIdx] + if !ok { + res = UndefinedValue + } + return +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *ImmutableMap) Equals(x Object) bool { + var xVal map[string]Object + switch x := x.(type) { + case *Map: + xVal = x.Value + case *ImmutableMap: + xVal = x.Value + default: + return false + } + if len(o.Value) != len(xVal) { + return false + } + for k, v := range o.Value { + tv := xVal[k] + if !v.Equals(tv) { + return false + } + } + return true +} + +// Iterate creates an immutable map iterator. +func (o *ImmutableMap) Iterate() Iterator { + var keys []string + for k := range o.Value { + keys = append(keys, k) + } + return &MapIterator{ + v: o.Value, + k: keys, + l: len(keys), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *ImmutableMap) CanIterate() bool { + return true +} + +// Int represents an integer value. +type Int struct { + ObjectImpl + Value int64 +} + +func (o *Int) String() string { + return strconv.FormatInt(o.Value, 10) +} + +// TypeName returns the name of the type. +func (o *Int) TypeName() string { + return "int" +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch rhs := rhs.(type) { + case *Int: + switch op { + case token.Add: + r := o.Value + rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Sub: + r := o.Value - rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Mul: + r := o.Value * rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Quo: + r := o.Value / rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Rem: + r := o.Value % rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.And: + r := o.Value & rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Or: + r := o.Value | rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Xor: + r := o.Value ^ rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.AndNot: + r := o.Value &^ rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Shl: + r := o.Value << uint64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Shr: + r := o.Value >> uint64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Less: + if o.Value < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + } + case *Float: + switch op { + case token.Add: + return &Float{Value: float64(o.Value) + rhs.Value}, nil + case token.Sub: + return &Float{Value: float64(o.Value) - rhs.Value}, nil + case token.Mul: + return &Float{Value: float64(o.Value) * rhs.Value}, nil + case token.Quo: + return &Float{Value: float64(o.Value) / rhs.Value}, nil + case token.Less: + if float64(o.Value) < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if float64(o.Value) > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if float64(o.Value) <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if float64(o.Value) >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + } + case *Char: + switch op { + case token.Add: + return &Char{Value: rune(o.Value) + rhs.Value}, nil + case token.Sub: + return &Char{Value: rune(o.Value) - rhs.Value}, nil + case token.Less: + if o.Value < int64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value > int64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value <= int64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value >= int64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Int) Copy() Object { + return &Int{Value: o.Value} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Int) IsFalsy() bool { + return o.Value == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Int) Equals(x Object) bool { + t, ok := x.(*Int) + if !ok { + return false + } + return o.Value == t.Value +} + +// Map represents a map of objects. +type Map struct { + ObjectImpl + Value map[string]Object +} + +// TypeName returns the name of the type. +func (o *Map) TypeName() string { + return "map" +} + +func (o *Map) String() string { + var pairs []string + for k, v := range o.Value { + pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String())) + } + return fmt.Sprintf("{%s}", strings.Join(pairs, ", ")) +} + +// Copy returns a copy of the type. +func (o *Map) Copy() Object { + c := make(map[string]Object) + for k, v := range o.Value { + c[k] = v.Copy() + } + return &Map{Value: c} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Map) IsFalsy() bool { + return len(o.Value) == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Map) Equals(x Object) bool { + var xVal map[string]Object + switch x := x.(type) { + case *Map: + xVal = x.Value + case *ImmutableMap: + xVal = x.Value + default: + return false + } + if len(o.Value) != len(xVal) { + return false + } + for k, v := range o.Value { + tv := xVal[k] + if !v.Equals(tv) { + return false + } + } + return true +} + +// IndexGet returns the value for the given key. +func (o *Map) IndexGet(index Object) (res Object, err error) { + strIdx, ok := ToString(index) + if !ok { + err = ErrInvalidIndexType + return + } + res, ok = o.Value[strIdx] + if !ok { + res = UndefinedValue + } + return +} + +// IndexSet sets the value for the given key. +func (o *Map) IndexSet(index, value Object) (err error) { + strIdx, ok := ToString(index) + if !ok { + err = ErrInvalidIndexType + return + } + o.Value[strIdx] = value + return nil +} + +// Iterate creates a map iterator. +func (o *Map) Iterate() Iterator { + var keys []string + for k := range o.Value { + keys = append(keys, k) + } + return &MapIterator{ + v: o.Value, + k: keys, + l: len(keys), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *Map) CanIterate() bool { + return true +} + +// ObjectPtr represents a free variable. +type ObjectPtr struct { + ObjectImpl + Value *Object +} + +func (o *ObjectPtr) String() string { + return "free-var" +} + +// TypeName returns the name of the type. +func (o *ObjectPtr) TypeName() string { + return "" +} + +// Copy returns a copy of the type. +func (o *ObjectPtr) Copy() Object { + return o +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *ObjectPtr) IsFalsy() bool { + return o.Value == nil +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *ObjectPtr) Equals(x Object) bool { + return o == x +} + +// String represents a string value. +type String struct { + ObjectImpl + Value string + runeStr []rune +} + +// TypeName returns the name of the type. +func (o *String) TypeName() string { + return "string" +} + +func (o *String) String() string { + return strconv.Quote(o.Value) +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch op { + case token.Add: + switch rhs := rhs.(type) { + case *String: + if len(o.Value)+len(rhs.Value) > MaxStringLen { + return nil, ErrStringLimit + } + return &String{Value: o.Value + rhs.Value}, nil + default: + rhsStr := rhs.String() + if len(o.Value)+len(rhsStr) > MaxStringLen { + return nil, ErrStringLimit + } + return &String{Value: o.Value + rhsStr}, nil + } + } + return nil, ErrInvalidOperator +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *String) IsFalsy() bool { + return len(o.Value) == 0 +} + +// Copy returns a copy of the type. +func (o *String) Copy() Object { + return &String{Value: o.Value} +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *String) Equals(x Object) bool { + t, ok := x.(*String) + if !ok { + return false + } + return o.Value == t.Value +} + +// IndexGet returns a character at a given index. +func (o *String) IndexGet(index Object) (res Object, err error) { + intIdx, ok := index.(*Int) + if !ok { + err = ErrInvalidIndexType + return + } + idxVal := int(intIdx.Value) + if o.runeStr == nil { + o.runeStr = []rune(o.Value) + } + if idxVal < 0 || idxVal >= len(o.runeStr) { + res = UndefinedValue + return + } + res = &Char{Value: o.runeStr[idxVal]} + return +} + +// Iterate creates a string iterator. +func (o *String) Iterate() Iterator { + if o.runeStr == nil { + o.runeStr = []rune(o.Value) + } + return &StringIterator{ + v: o.runeStr, + l: len(o.runeStr), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *String) CanIterate() bool { + return true +} + +// Time represents a time value. +type Time struct { + ObjectImpl + Value time.Time +} + +func (o *Time) String() string { + return o.Value.String() +} + +// TypeName returns the name of the type. +func (o *Time) TypeName() string { + return "time" +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Time) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch rhs := rhs.(type) { + case *Int: + switch op { + case token.Add: // time + int => time + if rhs.Value == 0 { + return o, nil + } + return &Time{Value: o.Value.Add(time.Duration(rhs.Value))}, nil + case token.Sub: // time - int => time + if rhs.Value == 0 { + return o, nil + } + return &Time{Value: o.Value.Add(time.Duration(-rhs.Value))}, nil + } + case *Time: + switch op { + case token.Sub: // time - time => int (duration) + return &Int{Value: int64(o.Value.Sub(rhs.Value))}, nil + case token.Less: // time < time => bool + if o.Value.Before(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value.After(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value.Equal(rhs.Value) || o.Value.Before(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value.Equal(rhs.Value) || o.Value.After(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Time) Copy() Object { + return &Time{Value: o.Value} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Time) IsFalsy() bool { + return o.Value.IsZero() +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Time) Equals(x Object) bool { + t, ok := x.(*Time) + if !ok { + return false + } + return o.Value.Equal(t.Value) +} + +// Undefined represents an undefined value. +type Undefined struct { + ObjectImpl +} + +// TypeName returns the name of the type. +func (o *Undefined) TypeName() string { + return "undefined" +} + +func (o *Undefined) String() string { + return "" +} + +// Copy returns a copy of the type. +func (o *Undefined) Copy() Object { + return o +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Undefined) IsFalsy() bool { + return true +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Undefined) Equals(x Object) bool { + return o == x +} + +// IndexGet returns an element at a given index. +func (o *Undefined) IndexGet(_ Object) (Object, error) { + return UndefinedValue, nil +} + +// Iterate creates a map iterator. +func (o *Undefined) Iterate() Iterator { + return o +} + +// CanIterate returns whether the Object can be Iterated. +func (o *Undefined) CanIterate() bool { + return true +} + +// Next returns true if there are more elements to iterate. +func (o *Undefined) Next() bool { + return false +} + +// Key returns the key or index value of the current element. +func (o *Undefined) Key() Object { + return o +} + +// Value returns the value of the current element. +func (o *Undefined) Value() Object { + return o +} + +// UserFunction represents a user function. +type UserFunction struct { + ObjectImpl + Name string + Value CallableFunc + EncodingID string +} + +// TypeName returns the name of the type. +func (o *UserFunction) TypeName() string { + return "user-function:" + o.Name +} + +func (o *UserFunction) String() string { + return "" +} + +// Copy returns a copy of the type. +func (o *UserFunction) Copy() Object { + return &UserFunction{Value: o.Value} +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *UserFunction) Equals(_ Object) bool { + return false +} + +// Call invokes a user function. +func (o *UserFunction) Call(args ...Object) (Object, error) { + return o.Value(args...) +} + +// CanCall returns whether the Object can be Called. +func (o *UserFunction) CanCall() bool { + return true +} diff --git a/vendor/github.com/d5/tengo/v2/parser/ast.go b/vendor/github.com/d5/tengo/v2/parser/ast.go new file mode 100644 index 00000000..8c2f7c07 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/ast.go @@ -0,0 +1,69 @@ +package parser + +import ( + "strings" +) + +const ( + nullRep = "" +) + +// Node represents a node in the AST. +type Node interface { + // Pos returns the position of first character belonging to the node. + Pos() Pos + // End returns the position of first character immediately after the node. + End() Pos + // String returns a string representation of the node. + String() string +} + +// IdentList represents a list of identifiers. +type IdentList struct { + LParen Pos + VarArgs bool + List []*Ident + RParen Pos +} + +// Pos returns the position of first character belonging to the node. +func (n *IdentList) Pos() Pos { + if n.LParen.IsValid() { + return n.LParen + } + if len(n.List) > 0 { + return n.List[0].Pos() + } + return NoPos +} + +// End returns the position of first character immediately after the node. +func (n *IdentList) End() Pos { + if n.RParen.IsValid() { + return n.RParen + 1 + } + if l := len(n.List); l > 0 { + return n.List[l-1].End() + } + return NoPos +} + +// NumFields returns the number of fields. +func (n *IdentList) NumFields() int { + if n == nil { + return 0 + } + return len(n.List) +} + +func (n *IdentList) String() string { + var list []string + for i, e := range n.List { + if n.VarArgs && i == len(n.List)-1 { + list = append(list, "..."+e.String()) + } else { + list = append(list, e.String()) + } + } + return "(" + strings.Join(list, ", ") + ")" +} diff --git a/vendor/github.com/d5/tengo/v2/parser/expr.go b/vendor/github.com/d5/tengo/v2/parser/expr.go new file mode 100644 index 00000000..71e5155b --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/expr.go @@ -0,0 +1,597 @@ +package parser + +import ( + "strings" + + "github.com/d5/tengo/v2/token" +) + +// Expr represents an expression node in the AST. +type Expr interface { + Node + exprNode() +} + +// ArrayLit represents an array literal. +type ArrayLit struct { + Elements []Expr + LBrack Pos + RBrack Pos +} + +func (e *ArrayLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *ArrayLit) Pos() Pos { + return e.LBrack +} + +// End returns the position of first character immediately after the node. +func (e *ArrayLit) End() Pos { + return e.RBrack + 1 +} + +func (e *ArrayLit) String() string { + var elements []string + for _, m := range e.Elements { + elements = append(elements, m.String()) + } + return "[" + strings.Join(elements, ", ") + "]" +} + +// BadExpr represents a bad expression. +type BadExpr struct { + From Pos + To Pos +} + +func (e *BadExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *BadExpr) Pos() Pos { + return e.From +} + +// End returns the position of first character immediately after the node. +func (e *BadExpr) End() Pos { + return e.To +} + +func (e *BadExpr) String() string { + return "" +} + +// BinaryExpr represents a binary operator expression. +type BinaryExpr struct { + LHS Expr + RHS Expr + Token token.Token + TokenPos Pos +} + +func (e *BinaryExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *BinaryExpr) Pos() Pos { + return e.LHS.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *BinaryExpr) End() Pos { + return e.RHS.End() +} + +func (e *BinaryExpr) String() string { + return "(" + e.LHS.String() + " " + e.Token.String() + + " " + e.RHS.String() + ")" +} + +// BoolLit represents a boolean literal. +type BoolLit struct { + Value bool + ValuePos Pos + Literal string +} + +func (e *BoolLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *BoolLit) Pos() Pos { + return e.ValuePos +} + +// End returns the position of first character immediately after the node. +func (e *BoolLit) End() Pos { + return Pos(int(e.ValuePos) + len(e.Literal)) +} + +func (e *BoolLit) String() string { + return e.Literal +} + +// CallExpr represents a function call expression. +type CallExpr struct { + Func Expr + LParen Pos + Args []Expr + RParen Pos +} + +func (e *CallExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *CallExpr) Pos() Pos { + return e.Func.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *CallExpr) End() Pos { + return e.RParen + 1 +} + +func (e *CallExpr) String() string { + var args []string + for _, e := range e.Args { + args = append(args, e.String()) + } + return e.Func.String() + "(" + strings.Join(args, ", ") + ")" +} + +// CharLit represents a character literal. +type CharLit struct { + Value rune + ValuePos Pos + Literal string +} + +func (e *CharLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *CharLit) Pos() Pos { + return e.ValuePos +} + +// End returns the position of first character immediately after the node. +func (e *CharLit) End() Pos { + return Pos(int(e.ValuePos) + len(e.Literal)) +} + +func (e *CharLit) String() string { + return e.Literal +} + +// CondExpr represents a ternary conditional expression. +type CondExpr struct { + Cond Expr + True Expr + False Expr + QuestionPos Pos + ColonPos Pos +} + +func (e *CondExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *CondExpr) Pos() Pos { + return e.Cond.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *CondExpr) End() Pos { + return e.False.End() +} + +func (e *CondExpr) String() string { + return "(" + e.Cond.String() + " ? " + e.True.String() + + " : " + e.False.String() + ")" +} + +// ErrorExpr represents an error expression +type ErrorExpr struct { + Expr Expr + ErrorPos Pos + LParen Pos + RParen Pos +} + +func (e *ErrorExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *ErrorExpr) Pos() Pos { + return e.ErrorPos +} + +// End returns the position of first character immediately after the node. +func (e *ErrorExpr) End() Pos { + return e.RParen +} + +func (e *ErrorExpr) String() string { + return "error(" + e.Expr.String() + ")" +} + +// FloatLit represents a floating point literal. +type FloatLit struct { + Value float64 + ValuePos Pos + Literal string +} + +func (e *FloatLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *FloatLit) Pos() Pos { + return e.ValuePos +} + +// End returns the position of first character immediately after the node. +func (e *FloatLit) End() Pos { + return Pos(int(e.ValuePos) + len(e.Literal)) +} + +func (e *FloatLit) String() string { + return e.Literal +} + +// FuncLit represents a function literal. +type FuncLit struct { + Type *FuncType + Body *BlockStmt +} + +func (e *FuncLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *FuncLit) Pos() Pos { + return e.Type.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *FuncLit) End() Pos { + return e.Body.End() +} + +func (e *FuncLit) String() string { + return "func" + e.Type.Params.String() + " " + e.Body.String() +} + +// FuncType represents a function type definition. +type FuncType struct { + FuncPos Pos + Params *IdentList +} + +func (e *FuncType) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *FuncType) Pos() Pos { + return e.FuncPos +} + +// End returns the position of first character immediately after the node. +func (e *FuncType) End() Pos { + return e.Params.End() +} + +func (e *FuncType) String() string { + return "func" + e.Params.String() +} + +// Ident represents an identifier. +type Ident struct { + Name string + NamePos Pos +} + +func (e *Ident) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *Ident) Pos() Pos { + return e.NamePos +} + +// End returns the position of first character immediately after the node. +func (e *Ident) End() Pos { + return Pos(int(e.NamePos) + len(e.Name)) +} + +func (e *Ident) String() string { + if e != nil { + return e.Name + } + return nullRep +} + +// ImmutableExpr represents an immutable expression +type ImmutableExpr struct { + Expr Expr + ErrorPos Pos + LParen Pos + RParen Pos +} + +func (e *ImmutableExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *ImmutableExpr) Pos() Pos { + return e.ErrorPos +} + +// End returns the position of first character immediately after the node. +func (e *ImmutableExpr) End() Pos { + return e.RParen +} + +func (e *ImmutableExpr) String() string { + return "immutable(" + e.Expr.String() + ")" +} + +// ImportExpr represents an import expression +type ImportExpr struct { + ModuleName string + Token token.Token + TokenPos Pos +} + +func (e *ImportExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *ImportExpr) Pos() Pos { + return e.TokenPos +} + +// End returns the position of first character immediately after the node. +func (e *ImportExpr) End() Pos { + // import("moduleName") + return Pos(int(e.TokenPos) + 10 + len(e.ModuleName)) +} + +func (e *ImportExpr) String() string { + return `import("` + e.ModuleName + `")"` +} + +// IndexExpr represents an index expression. +type IndexExpr struct { + Expr Expr + LBrack Pos + Index Expr + RBrack Pos +} + +func (e *IndexExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *IndexExpr) Pos() Pos { + return e.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *IndexExpr) End() Pos { + return e.RBrack + 1 +} + +func (e *IndexExpr) String() string { + var index string + if e.Index != nil { + index = e.Index.String() + } + return e.Expr.String() + "[" + index + "]" +} + +// IntLit represents an integer literal. +type IntLit struct { + Value int64 + ValuePos Pos + Literal string +} + +func (e *IntLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *IntLit) Pos() Pos { + return e.ValuePos +} + +// End returns the position of first character immediately after the node. +func (e *IntLit) End() Pos { + return Pos(int(e.ValuePos) + len(e.Literal)) +} + +func (e *IntLit) String() string { + return e.Literal +} + +// MapElementLit represents a map element. +type MapElementLit struct { + Key string + KeyPos Pos + ColonPos Pos + Value Expr +} + +func (e *MapElementLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *MapElementLit) Pos() Pos { + return e.KeyPos +} + +// End returns the position of first character immediately after the node. +func (e *MapElementLit) End() Pos { + return e.Value.End() +} + +func (e *MapElementLit) String() string { + return e.Key + ": " + e.Value.String() +} + +// MapLit represents a map literal. +type MapLit struct { + LBrace Pos + Elements []*MapElementLit + RBrace Pos +} + +func (e *MapLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *MapLit) Pos() Pos { + return e.LBrace +} + +// End returns the position of first character immediately after the node. +func (e *MapLit) End() Pos { + return e.RBrace + 1 +} + +func (e *MapLit) String() string { + var elements []string + for _, m := range e.Elements { + elements = append(elements, m.String()) + } + return "{" + strings.Join(elements, ", ") + "}" +} + +// ParenExpr represents a parenthesis wrapped expression. +type ParenExpr struct { + Expr Expr + LParen Pos + RParen Pos +} + +func (e *ParenExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *ParenExpr) Pos() Pos { + return e.LParen +} + +// End returns the position of first character immediately after the node. +func (e *ParenExpr) End() Pos { + return e.RParen + 1 +} + +func (e *ParenExpr) String() string { + return "(" + e.Expr.String() + ")" +} + +// SelectorExpr represents a selector expression. +type SelectorExpr struct { + Expr Expr + Sel Expr +} + +func (e *SelectorExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *SelectorExpr) Pos() Pos { + return e.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *SelectorExpr) End() Pos { + return e.Sel.End() +} + +func (e *SelectorExpr) String() string { + return e.Expr.String() + "." + e.Sel.String() +} + +// SliceExpr represents a slice expression. +type SliceExpr struct { + Expr Expr + LBrack Pos + Low Expr + High Expr + RBrack Pos +} + +func (e *SliceExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *SliceExpr) Pos() Pos { + return e.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *SliceExpr) End() Pos { + return e.RBrack + 1 +} + +func (e *SliceExpr) String() string { + var low, high string + if e.Low != nil { + low = e.Low.String() + } + if e.High != nil { + high = e.High.String() + } + return e.Expr.String() + "[" + low + ":" + high + "]" +} + +// StringLit represents a string literal. +type StringLit struct { + Value string + ValuePos Pos + Literal string +} + +func (e *StringLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *StringLit) Pos() Pos { + return e.ValuePos +} + +// End returns the position of first character immediately after the node. +func (e *StringLit) End() Pos { + return Pos(int(e.ValuePos) + len(e.Literal)) +} + +func (e *StringLit) String() string { + return e.Literal +} + +// UnaryExpr represents an unary operator expression. +type UnaryExpr struct { + Expr Expr + Token token.Token + TokenPos Pos +} + +func (e *UnaryExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *UnaryExpr) Pos() Pos { + return e.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *UnaryExpr) End() Pos { + return e.Expr.End() +} + +func (e *UnaryExpr) String() string { + return "(" + e.Token.String() + e.Expr.String() + ")" +} + +// UndefinedLit represents an undefined literal. +type UndefinedLit struct { + TokenPos Pos +} + +func (e *UndefinedLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *UndefinedLit) Pos() Pos { + return e.TokenPos +} + +// End returns the position of first character immediately after the node. +func (e *UndefinedLit) End() Pos { + return e.TokenPos + 9 // len(undefined) == 9 +} + +func (e *UndefinedLit) String() string { + return "undefined" +} diff --git a/vendor/github.com/d5/tengo/v2/parser/file.go b/vendor/github.com/d5/tengo/v2/parser/file.go new file mode 100644 index 00000000..7cf50fea --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/file.go @@ -0,0 +1,29 @@ +package parser + +import ( + "strings" +) + +// File represents a file unit. +type File struct { + InputFile *SourceFile + Stmts []Stmt +} + +// Pos returns the position of first character belonging to the node. +func (n *File) Pos() Pos { + return Pos(n.InputFile.Base) +} + +// End returns the position of first character immediately after the node. +func (n *File) End() Pos { + return Pos(n.InputFile.Base + n.InputFile.Size) +} + +func (n *File) String() string { + var stmts []string + for _, e := range n.Stmts { + stmts = append(stmts, e.String()) + } + return strings.Join(stmts, "; ") +} diff --git a/vendor/github.com/d5/tengo/v2/parser/opcodes.go b/vendor/github.com/d5/tengo/v2/parser/opcodes.go new file mode 100644 index 00000000..a4fbfbaf --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/opcodes.go @@ -0,0 +1,156 @@ +package parser + +// Opcode represents a single byte operation code. +type Opcode = byte + +// List of opcodes +const ( + OpConstant Opcode = iota // Load constant + OpBComplement // bitwise complement + OpPop // Pop + OpTrue // Push true + OpFalse // Push false + OpEqual // Equal == + OpNotEqual // Not equal != + OpMinus // Minus - + OpLNot // Logical not ! + OpJumpFalsy // Jump if falsy + OpAndJump // Logical AND jump + OpOrJump // Logical OR jump + OpJump // Jump + OpNull // Push null + OpArray // Array object + OpMap // Map object + OpError // Error object + OpImmutable // Immutable object + OpIndex // Index operation + OpSliceIndex // Slice operation + OpCall // Call function + OpReturn // Return + OpGetGlobal // Get global variable + OpSetGlobal // Set global variable + OpSetSelGlobal // Set global variable using selectors + OpGetLocal // Get local variable + OpSetLocal // Set local variable + OpDefineLocal // Define local variable + OpSetSelLocal // Set local variable using selectors + OpGetFreePtr // Get free variable pointer object + OpGetFree // Get free variables + OpSetFree // Set free variables + OpGetLocalPtr // Get local variable as a pointer + OpSetSelFree // Set free variables using selectors + OpGetBuiltin // Get builtin function + OpClosure // Push closure + OpIteratorInit // Iterator init + OpIteratorNext // Iterator next + OpIteratorKey // Iterator key + OpIteratorValue // Iterator value + OpBinaryOp // Binary operation + OpSuspend // Suspend VM +) + +// OpcodeNames are string representation of opcodes. +var OpcodeNames = [...]string{ + OpConstant: "CONST", + OpPop: "POP", + OpTrue: "TRUE", + OpFalse: "FALSE", + OpBComplement: "NEG", + OpEqual: "EQL", + OpNotEqual: "NEQ", + OpMinus: "NEG", + OpLNot: "NOT", + OpJumpFalsy: "JMPF", + OpAndJump: "ANDJMP", + OpOrJump: "ORJMP", + OpJump: "JMP", + OpNull: "NULL", + OpGetGlobal: "GETG", + OpSetGlobal: "SETG", + OpSetSelGlobal: "SETSG", + OpArray: "ARR", + OpMap: "MAP", + OpError: "ERROR", + OpImmutable: "IMMUT", + OpIndex: "INDEX", + OpSliceIndex: "SLICE", + OpCall: "CALL", + OpReturn: "RET", + OpGetLocal: "GETL", + OpSetLocal: "SETL", + OpDefineLocal: "DEFL", + OpSetSelLocal: "SETSL", + OpGetBuiltin: "BUILTIN", + OpClosure: "CLOSURE", + OpGetFreePtr: "GETFP", + OpGetFree: "GETF", + OpSetFree: "SETF", + OpGetLocalPtr: "GETLP", + OpSetSelFree: "SETSF", + OpIteratorInit: "ITER", + OpIteratorNext: "ITNXT", + OpIteratorKey: "ITKEY", + OpIteratorValue: "ITVAL", + OpBinaryOp: "BINARYOP", + OpSuspend: "SUSPEND", +} + +// OpcodeOperands is the number of operands. +var OpcodeOperands = [...][]int{ + OpConstant: {2}, + OpPop: {}, + OpTrue: {}, + OpFalse: {}, + OpBComplement: {}, + OpEqual: {}, + OpNotEqual: {}, + OpMinus: {}, + OpLNot: {}, + OpJumpFalsy: {2}, + OpAndJump: {2}, + OpOrJump: {2}, + OpJump: {2}, + OpNull: {}, + OpGetGlobal: {2}, + OpSetGlobal: {2}, + OpSetSelGlobal: {2, 1}, + OpArray: {2}, + OpMap: {2}, + OpError: {}, + OpImmutable: {}, + OpIndex: {}, + OpSliceIndex: {}, + OpCall: {1}, + OpReturn: {1}, + OpGetLocal: {1}, + OpSetLocal: {1}, + OpDefineLocal: {1}, + OpSetSelLocal: {1, 1}, + OpGetBuiltin: {1}, + OpClosure: {2, 1}, + OpGetFreePtr: {1}, + OpGetFree: {1}, + OpSetFree: {1}, + OpGetLocalPtr: {1}, + OpSetSelFree: {1, 1}, + OpIteratorInit: {}, + OpIteratorNext: {}, + OpIteratorKey: {}, + OpIteratorValue: {}, + OpBinaryOp: {1}, + OpSuspend: {}, +} + +// ReadOperands reads operands from the bytecode. +func ReadOperands(numOperands []int, ins []byte) (operands []int, offset int) { + for _, width := range numOperands { + switch width { + case 1: + operands = append(operands, int(ins[offset])) + case 2: + operands = append(operands, int(ins[offset+1])|int(ins[offset])<<8) + } + offset += width + } + return +} diff --git a/vendor/github.com/d5/tengo/v2/parser/parser.go b/vendor/github.com/d5/tengo/v2/parser/parser.go new file mode 100644 index 00000000..501a9106 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/parser.go @@ -0,0 +1,1196 @@ +package parser + +import ( + "fmt" + "io" + "sort" + "strconv" + + "github.com/d5/tengo/v2/token" +) + +type bailout struct{} + +var stmtStart = map[token.Token]bool{ + token.Break: true, + token.Continue: true, + token.For: true, + token.If: true, + token.Return: true, + token.Export: true, +} + +// Error represents a parser error. +type Error struct { + Pos SourceFilePos + Msg string +} + +func (e Error) Error() string { + if e.Pos.Filename != "" || e.Pos.IsValid() { + return fmt.Sprintf("Parse Error: %s\n\tat %s", e.Msg, e.Pos) + } + return fmt.Sprintf("Parse Error: %s", e.Msg) +} + +// ErrorList is a collection of parser errors. +type ErrorList []*Error + +// Add adds a new parser error to the collection. +func (p *ErrorList) Add(pos SourceFilePos, msg string) { + *p = append(*p, &Error{pos, msg}) +} + +// Len returns the number of elements in the collection. +func (p ErrorList) Len() int { + return len(p) +} + +func (p ErrorList) Swap(i, j int) { + p[i], p[j] = p[j], p[i] +} + +func (p ErrorList) Less(i, j int) bool { + e := &p[i].Pos + f := &p[j].Pos + + if e.Filename != f.Filename { + return e.Filename < f.Filename + } + if e.Line != f.Line { + return e.Line < f.Line + } + if e.Column != f.Column { + return e.Column < f.Column + } + return p[i].Msg < p[j].Msg +} + +// Sort sorts the collection. +func (p ErrorList) Sort() { + sort.Sort(p) +} + +func (p ErrorList) Error() string { + switch len(p) { + case 0: + return "no errors" + case 1: + return p[0].Error() + } + return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1) +} + +// Err returns an error. +func (p ErrorList) Err() error { + if len(p) == 0 { + return nil + } + return p +} + +// Parser parses the Tengo source files. It's based on Go's parser +// implementation. +type Parser struct { + file *SourceFile + errors ErrorList + scanner *Scanner + pos Pos + token token.Token + tokenLit string + exprLevel int // < 0: in control clause, >= 0: in expression + syncPos Pos // last sync position + syncCount int // number of advance calls without progress + trace bool + indent int + traceOut io.Writer +} + +// NewParser creates a Parser. +func NewParser(file *SourceFile, src []byte, trace io.Writer) *Parser { + p := &Parser{ + file: file, + trace: trace != nil, + traceOut: trace, + } + p.scanner = NewScanner(p.file, src, + func(pos SourceFilePos, msg string) { + p.errors.Add(pos, msg) + }, 0) + p.next() + return p +} + +// ParseFile parses the source and returns an AST file unit. +func (p *Parser) ParseFile() (file *File, err error) { + defer func() { + if e := recover(); e != nil { + if _, ok := e.(bailout); !ok { + panic(e) + } + } + + p.errors.Sort() + err = p.errors.Err() + }() + + if p.trace { + defer untracep(tracep(p, "File")) + } + + if p.errors.Len() > 0 { + return nil, p.errors.Err() + } + + stmts := p.parseStmtList() + if p.errors.Len() > 0 { + return nil, p.errors.Err() + } + + file = &File{ + InputFile: p.file, + Stmts: stmts, + } + return +} + +func (p *Parser) parseExpr() Expr { + if p.trace { + defer untracep(tracep(p, "Expression")) + } + + expr := p.parseBinaryExpr(token.LowestPrec + 1) + + // ternary conditional expression + if p.token == token.Question { + return p.parseCondExpr(expr) + } + return expr +} + +func (p *Parser) parseBinaryExpr(prec1 int) Expr { + if p.trace { + defer untracep(tracep(p, "BinaryExpression")) + } + + x := p.parseUnaryExpr() + + for { + op, prec := p.token, p.token.Precedence() + if prec < prec1 { + return x + } + + pos := p.expect(op) + + y := p.parseBinaryExpr(prec + 1) + + x = &BinaryExpr{ + LHS: x, + RHS: y, + Token: op, + TokenPos: pos, + } + } +} + +func (p *Parser) parseCondExpr(cond Expr) Expr { + questionPos := p.expect(token.Question) + trueExpr := p.parseExpr() + colonPos := p.expect(token.Colon) + falseExpr := p.parseExpr() + + return &CondExpr{ + Cond: cond, + True: trueExpr, + False: falseExpr, + QuestionPos: questionPos, + ColonPos: colonPos, + } +} + +func (p *Parser) parseUnaryExpr() Expr { + if p.trace { + defer untracep(tracep(p, "UnaryExpression")) + } + + switch p.token { + case token.Add, token.Sub, token.Not, token.Xor: + pos, op := p.pos, p.token + p.next() + x := p.parseUnaryExpr() + return &UnaryExpr{ + Token: op, + TokenPos: pos, + Expr: x, + } + } + return p.parsePrimaryExpr() +} + +func (p *Parser) parsePrimaryExpr() Expr { + if p.trace { + defer untracep(tracep(p, "PrimaryExpression")) + } + + x := p.parseOperand() + +L: + for { + switch p.token { + case token.Period: + p.next() + + switch p.token { + case token.Ident: + x = p.parseSelector(x) + default: + pos := p.pos + p.errorExpected(pos, "selector") + p.advance(stmtStart) + return &BadExpr{From: pos, To: p.pos} + } + case token.LBrack: + x = p.parseIndexOrSlice(x) + case token.LParen: + x = p.parseCall(x) + default: + break L + } + } + return x +} + +func (p *Parser) parseCall(x Expr) *CallExpr { + if p.trace { + defer untracep(tracep(p, "Call")) + } + + lparen := p.expect(token.LParen) + p.exprLevel++ + + var list []Expr + for p.token != token.RParen && p.token != token.EOF { + list = append(list, p.parseExpr()) + + if !p.expectComma(token.RParen, "call argument") { + break + } + } + + p.exprLevel-- + rparen := p.expect(token.RParen) + return &CallExpr{ + Func: x, + LParen: lparen, + RParen: rparen, + Args: list, + } +} + +func (p *Parser) expectComma(closing token.Token, want string) bool { + if p.token == token.Comma { + p.next() + + if p.token == closing { + p.errorExpected(p.pos, want) + return false + } + return true + } + + if p.token == token.Semicolon && p.tokenLit == "\n" { + p.next() + } + return false +} + +func (p *Parser) parseIndexOrSlice(x Expr) Expr { + if p.trace { + defer untracep(tracep(p, "IndexOrSlice")) + } + + lbrack := p.expect(token.LBrack) + p.exprLevel++ + + var index [2]Expr + if p.token != token.Colon { + index[0] = p.parseExpr() + } + numColons := 0 + if p.token == token.Colon { + numColons++ + p.next() + + if p.token != token.RBrack && p.token != token.EOF { + index[1] = p.parseExpr() + } + } + + p.exprLevel-- + rbrack := p.expect(token.RBrack) + + if numColons > 0 { + // slice expression + return &SliceExpr{ + Expr: x, + LBrack: lbrack, + RBrack: rbrack, + Low: index[0], + High: index[1], + } + } + return &IndexExpr{ + Expr: x, + LBrack: lbrack, + RBrack: rbrack, + Index: index[0], + } +} + +func (p *Parser) parseSelector(x Expr) Expr { + if p.trace { + defer untracep(tracep(p, "Selector")) + } + + sel := p.parseIdent() + return &SelectorExpr{Expr: x, Sel: &StringLit{ + Value: sel.Name, + ValuePos: sel.NamePos, + Literal: sel.Name, + }} +} + +func (p *Parser) parseOperand() Expr { + if p.trace { + defer untracep(tracep(p, "Operand")) + } + + switch p.token { + case token.Ident: + return p.parseIdent() + case token.Int: + v, _ := strconv.ParseInt(p.tokenLit, 10, 64) + x := &IntLit{ + Value: v, + ValuePos: p.pos, + Literal: p.tokenLit, + } + p.next() + return x + case token.Float: + v, _ := strconv.ParseFloat(p.tokenLit, 64) + x := &FloatLit{ + Value: v, + ValuePos: p.pos, + Literal: p.tokenLit, + } + p.next() + return x + case token.Char: + return p.parseCharLit() + case token.String: + v, _ := strconv.Unquote(p.tokenLit) + x := &StringLit{ + Value: v, + ValuePos: p.pos, + Literal: p.tokenLit, + } + p.next() + return x + case token.True: + x := &BoolLit{ + Value: true, + ValuePos: p.pos, + Literal: p.tokenLit, + } + p.next() + return x + case token.False: + x := &BoolLit{ + Value: false, + ValuePos: p.pos, + Literal: p.tokenLit, + } + p.next() + return x + case token.Undefined: + x := &UndefinedLit{TokenPos: p.pos} + p.next() + return x + case token.Import: + return p.parseImportExpr() + case token.LParen: + lparen := p.pos + p.next() + p.exprLevel++ + x := p.parseExpr() + p.exprLevel-- + rparen := p.expect(token.RParen) + return &ParenExpr{ + LParen: lparen, + Expr: x, + RParen: rparen, + } + case token.LBrack: // array literal + return p.parseArrayLit() + case token.LBrace: // map literal + return p.parseMapLit() + case token.Func: // function literal + return p.parseFuncLit() + case token.Error: // error expression + return p.parseErrorExpr() + case token.Immutable: // immutable expression + return p.parseImmutableExpr() + } + + pos := p.pos + p.errorExpected(pos, "operand") + p.advance(stmtStart) + return &BadExpr{From: pos, To: p.pos} +} + +func (p *Parser) parseImportExpr() Expr { + pos := p.pos + p.next() + p.expect(token.LParen) + if p.token != token.String { + p.errorExpected(p.pos, "module name") + p.advance(stmtStart) + return &BadExpr{From: pos, To: p.pos} + } + + // module name + moduleName, _ := strconv.Unquote(p.tokenLit) + expr := &ImportExpr{ + ModuleName: moduleName, + Token: token.Import, + TokenPos: pos, + } + + p.next() + p.expect(token.RParen) + return expr +} + +func (p *Parser) parseCharLit() Expr { + if n := len(p.tokenLit); n >= 3 { + code, _, _, err := strconv.UnquoteChar(p.tokenLit[1:n-1], '\'') + if err == nil { + x := &CharLit{ + Value: code, + ValuePos: p.pos, + Literal: p.tokenLit, + } + p.next() + return x + } + } + + pos := p.pos + p.error(pos, "illegal char literal") + p.next() + return &BadExpr{ + From: pos, + To: p.pos, + } +} + +func (p *Parser) parseFuncLit() Expr { + if p.trace { + defer untracep(tracep(p, "FuncLit")) + } + + typ := p.parseFuncType() + p.exprLevel++ + body := p.parseBody() + p.exprLevel-- + return &FuncLit{ + Type: typ, + Body: body, + } +} + +func (p *Parser) parseArrayLit() Expr { + if p.trace { + defer untracep(tracep(p, "ArrayLit")) + } + + lbrack := p.expect(token.LBrack) + p.exprLevel++ + + var elements []Expr + for p.token != token.RBrack && p.token != token.EOF { + elements = append(elements, p.parseExpr()) + + if !p.expectComma(token.RBrack, "array element") { + break + } + } + + p.exprLevel-- + rbrack := p.expect(token.RBrack) + return &ArrayLit{ + Elements: elements, + LBrack: lbrack, + RBrack: rbrack, + } +} + +func (p *Parser) parseErrorExpr() Expr { + pos := p.pos + + p.next() + lparen := p.expect(token.LParen) + value := p.parseExpr() + rparen := p.expect(token.RParen) + return &ErrorExpr{ + ErrorPos: pos, + Expr: value, + LParen: lparen, + RParen: rparen, + } +} + +func (p *Parser) parseImmutableExpr() Expr { + pos := p.pos + + p.next() + lparen := p.expect(token.LParen) + value := p.parseExpr() + rparen := p.expect(token.RParen) + return &ImmutableExpr{ + ErrorPos: pos, + Expr: value, + LParen: lparen, + RParen: rparen, + } +} + +func (p *Parser) parseFuncType() *FuncType { + if p.trace { + defer untracep(tracep(p, "FuncType")) + } + + pos := p.expect(token.Func) + params := p.parseIdentList() + return &FuncType{ + FuncPos: pos, + Params: params, + } +} + +func (p *Parser) parseBody() *BlockStmt { + if p.trace { + defer untracep(tracep(p, "Body")) + } + + lbrace := p.expect(token.LBrace) + list := p.parseStmtList() + rbrace := p.expect(token.RBrace) + return &BlockStmt{ + LBrace: lbrace, + RBrace: rbrace, + Stmts: list, + } +} + +func (p *Parser) parseStmtList() (list []Stmt) { + if p.trace { + defer untracep(tracep(p, "StatementList")) + } + + for p.token != token.RBrace && p.token != token.EOF { + list = append(list, p.parseStmt()) + } + return +} + +func (p *Parser) parseIdent() *Ident { + pos := p.pos + name := "_" + + if p.token == token.Ident { + name = p.tokenLit + p.next() + } else { + p.expect(token.Ident) + } + return &Ident{ + NamePos: pos, + Name: name, + } +} + +func (p *Parser) parseIdentList() *IdentList { + if p.trace { + defer untracep(tracep(p, "IdentList")) + } + + var params []*Ident + lparen := p.expect(token.LParen) + isVarArgs := false + if p.token != token.RParen { + if p.token == token.Ellipsis { + isVarArgs = true + p.next() + } + + params = append(params, p.parseIdent()) + for !isVarArgs && p.token == token.Comma { + p.next() + if p.token == token.Ellipsis { + isVarArgs = true + p.next() + } + params = append(params, p.parseIdent()) + } + } + + rparen := p.expect(token.RParen) + return &IdentList{ + LParen: lparen, + RParen: rparen, + VarArgs: isVarArgs, + List: params, + } +} + +func (p *Parser) parseStmt() (stmt Stmt) { + if p.trace { + defer untracep(tracep(p, "Statement")) + } + + switch p.token { + case // simple statements + token.Func, token.Error, token.Immutable, token.Ident, token.Int, + token.Float, token.Char, token.String, token.True, token.False, + token.Undefined, token.Import, token.LParen, token.LBrace, + token.LBrack, token.Add, token.Sub, token.Mul, token.And, token.Xor, + token.Not: + s := p.parseSimpleStmt(false) + p.expectSemi() + return s + case token.Return: + return p.parseReturnStmt() + case token.Export: + return p.parseExportStmt() + case token.If: + return p.parseIfStmt() + case token.For: + return p.parseForStmt() + case token.Break, token.Continue: + return p.parseBranchStmt(p.token) + case token.Semicolon: + s := &EmptyStmt{Semicolon: p.pos, Implicit: p.tokenLit == "\n"} + p.next() + return s + case token.RBrace: + // semicolon may be omitted before a closing "}" + return &EmptyStmt{Semicolon: p.pos, Implicit: true} + default: + pos := p.pos + p.errorExpected(pos, "statement") + p.advance(stmtStart) + return &BadStmt{From: pos, To: p.pos} + } +} + +func (p *Parser) parseForStmt() Stmt { + if p.trace { + defer untracep(tracep(p, "ForStmt")) + } + + pos := p.expect(token.For) + + // for {} + if p.token == token.LBrace { + body := p.parseBlockStmt() + p.expectSemi() + + return &ForStmt{ + ForPos: pos, + Body: body, + } + } + + prevLevel := p.exprLevel + p.exprLevel = -1 + + var s1 Stmt + if p.token != token.Semicolon { // skipping init + s1 = p.parseSimpleStmt(true) + } + + // for _ in seq {} or + // for value in seq {} or + // for key, value in seq {} + if forInStmt, isForIn := s1.(*ForInStmt); isForIn { + forInStmt.ForPos = pos + p.exprLevel = prevLevel + forInStmt.Body = p.parseBlockStmt() + p.expectSemi() + return forInStmt + } + + // for init; cond; post {} + var s2, s3 Stmt + if p.token == token.Semicolon { + p.next() + if p.token != token.Semicolon { + s2 = p.parseSimpleStmt(false) // cond + } + p.expect(token.Semicolon) + if p.token != token.LBrace { + s3 = p.parseSimpleStmt(false) // post + } + } else { + // for cond {} + s2 = s1 + s1 = nil + } + + // body + p.exprLevel = prevLevel + body := p.parseBlockStmt() + p.expectSemi() + cond := p.makeExpr(s2, "condition expression") + return &ForStmt{ + ForPos: pos, + Init: s1, + Cond: cond, + Post: s3, + Body: body, + } +} + +func (p *Parser) parseBranchStmt(tok token.Token) Stmt { + if p.trace { + defer untracep(tracep(p, "BranchStmt")) + } + + pos := p.expect(tok) + + var label *Ident + if p.token == token.Ident { + label = p.parseIdent() + } + p.expectSemi() + return &BranchStmt{ + Token: tok, + TokenPos: pos, + Label: label, + } +} + +func (p *Parser) parseIfStmt() Stmt { + if p.trace { + defer untracep(tracep(p, "IfStmt")) + } + + pos := p.expect(token.If) + init, cond := p.parseIfHeader() + body := p.parseBlockStmt() + + var elseStmt Stmt + if p.token == token.Else { + p.next() + + switch p.token { + case token.If: + elseStmt = p.parseIfStmt() + case token.LBrace: + elseStmt = p.parseBlockStmt() + p.expectSemi() + default: + p.errorExpected(p.pos, "if or {") + elseStmt = &BadStmt{From: p.pos, To: p.pos} + } + } else { + p.expectSemi() + } + return &IfStmt{ + IfPos: pos, + Init: init, + Cond: cond, + Body: body, + Else: elseStmt, + } +} + +func (p *Parser) parseBlockStmt() *BlockStmt { + if p.trace { + defer untracep(tracep(p, "BlockStmt")) + } + + lbrace := p.expect(token.LBrace) + list := p.parseStmtList() + rbrace := p.expect(token.RBrace) + return &BlockStmt{ + LBrace: lbrace, + RBrace: rbrace, + Stmts: list, + } +} + +func (p *Parser) parseIfHeader() (init Stmt, cond Expr) { + if p.token == token.LBrace { + p.error(p.pos, "missing condition in if statement") + cond = &BadExpr{From: p.pos, To: p.pos} + return + } + + outer := p.exprLevel + p.exprLevel = -1 + if p.token == token.Semicolon { + p.error(p.pos, "missing init in if statement") + return + } + init = p.parseSimpleStmt(false) + + var condStmt Stmt + if p.token == token.LBrace { + condStmt = init + init = nil + } else if p.token == token.Semicolon { + p.next() + + condStmt = p.parseSimpleStmt(false) + } else { + p.error(p.pos, "missing condition in if statement") + } + + if condStmt != nil { + cond = p.makeExpr(condStmt, "boolean expression") + } + if cond == nil { + cond = &BadExpr{From: p.pos, To: p.pos} + } + p.exprLevel = outer + return +} + +func (p *Parser) makeExpr(s Stmt, want string) Expr { + if s == nil { + return nil + } + + if es, isExpr := s.(*ExprStmt); isExpr { + return es.Expr + } + + found := "simple statement" + if _, isAss := s.(*AssignStmt); isAss { + found = "assignment" + } + p.error(s.Pos(), fmt.Sprintf("expected %s, found %s", want, found)) + return &BadExpr{From: s.Pos(), To: p.safePos(s.End())} +} + +func (p *Parser) parseReturnStmt() Stmt { + if p.trace { + defer untracep(tracep(p, "ReturnStmt")) + } + + pos := p.pos + p.expect(token.Return) + + var x Expr + if p.token != token.Semicolon && p.token != token.RBrace { + x = p.parseExpr() + } + p.expectSemi() + return &ReturnStmt{ + ReturnPos: pos, + Result: x, + } +} + +func (p *Parser) parseExportStmt() Stmt { + if p.trace { + defer untracep(tracep(p, "ExportStmt")) + } + + pos := p.pos + p.expect(token.Export) + x := p.parseExpr() + p.expectSemi() + return &ExportStmt{ + ExportPos: pos, + Result: x, + } +} + +func (p *Parser) parseSimpleStmt(forIn bool) Stmt { + if p.trace { + defer untracep(tracep(p, "SimpleStmt")) + } + + x := p.parseExprList() + + switch p.token { + case token.Assign, token.Define: // assignment statement + pos, tok := p.pos, p.token + p.next() + y := p.parseExprList() + return &AssignStmt{ + LHS: x, + RHS: y, + Token: tok, + TokenPos: pos, + } + case token.In: + if forIn { + p.next() + y := p.parseExpr() + + var key, value *Ident + var ok bool + switch len(x) { + case 1: + key = &Ident{Name: "_", NamePos: x[0].Pos()} + + value, ok = x[0].(*Ident) + if !ok { + p.errorExpected(x[0].Pos(), "identifier") + value = &Ident{Name: "_", NamePos: x[0].Pos()} + } + case 2: + key, ok = x[0].(*Ident) + if !ok { + p.errorExpected(x[0].Pos(), "identifier") + key = &Ident{Name: "_", NamePos: x[0].Pos()} + } + value, ok = x[1].(*Ident) + if !ok { + p.errorExpected(x[1].Pos(), "identifier") + value = &Ident{Name: "_", NamePos: x[1].Pos()} + } + } + return &ForInStmt{ + Key: key, + Value: value, + Iterable: y, + } + } + } + + if len(x) > 1 { + p.errorExpected(x[0].Pos(), "1 expression") + // continue with first expression + } + + switch p.token { + case token.Define, + token.AddAssign, token.SubAssign, token.MulAssign, token.QuoAssign, + token.RemAssign, token.AndAssign, token.OrAssign, token.XorAssign, + token.ShlAssign, token.ShrAssign, token.AndNotAssign: + pos, tok := p.pos, p.token + p.next() + y := p.parseExpr() + return &AssignStmt{ + LHS: []Expr{x[0]}, + RHS: []Expr{y}, + Token: tok, + TokenPos: pos, + } + case token.Inc, token.Dec: + // increment or decrement statement + s := &IncDecStmt{Expr: x[0], Token: p.token, TokenPos: p.pos} + p.next() + return s + } + return &ExprStmt{Expr: x[0]} +} + +func (p *Parser) parseExprList() (list []Expr) { + if p.trace { + defer untracep(tracep(p, "ExpressionList")) + } + + list = append(list, p.parseExpr()) + for p.token == token.Comma { + p.next() + list = append(list, p.parseExpr()) + } + return +} + +func (p *Parser) parseMapElementLit() *MapElementLit { + if p.trace { + defer untracep(tracep(p, "MapElementLit")) + } + + pos := p.pos + name := "_" + if p.token == token.Ident { + name = p.tokenLit + } else if p.token == token.String { + v, _ := strconv.Unquote(p.tokenLit) + name = v + } else { + p.errorExpected(pos, "map key") + } + p.next() + colonPos := p.expect(token.Colon) + valueExpr := p.parseExpr() + return &MapElementLit{ + Key: name, + KeyPos: pos, + ColonPos: colonPos, + Value: valueExpr, + } +} + +func (p *Parser) parseMapLit() *MapLit { + if p.trace { + defer untracep(tracep(p, "MapLit")) + } + + lbrace := p.expect(token.LBrace) + p.exprLevel++ + + var elements []*MapElementLit + for p.token != token.RBrace && p.token != token.EOF { + elements = append(elements, p.parseMapElementLit()) + + if !p.expectComma(token.RBrace, "map element") { + break + } + } + + p.exprLevel-- + rbrace := p.expect(token.RBrace) + return &MapLit{ + LBrace: lbrace, + RBrace: rbrace, + Elements: elements, + } +} + +func (p *Parser) expect(token token.Token) Pos { + pos := p.pos + + if p.token != token { + p.errorExpected(pos, "'"+token.String()+"'") + } + p.next() + return pos +} + +func (p *Parser) expectSemi() { + switch p.token { + case token.RParen, token.RBrace: + // semicolon is optional before a closing ')' or '}' + case token.Comma: + // permit a ',' instead of a ';' but complain + p.errorExpected(p.pos, "';'") + fallthrough + case token.Semicolon: + p.next() + default: + p.errorExpected(p.pos, "';'") + p.advance(stmtStart) + } +} + +func (p *Parser) advance(to map[token.Token]bool) { + for ; p.token != token.EOF; p.next() { + if to[p.token] { + if p.pos == p.syncPos && p.syncCount < 10 { + p.syncCount++ + return + } + if p.pos > p.syncPos { + p.syncPos = p.pos + p.syncCount = 0 + return + } + } + } +} + +func (p *Parser) error(pos Pos, msg string) { + filePos := p.file.Position(pos) + + n := len(p.errors) + if n > 0 && p.errors[n-1].Pos.Line == filePos.Line { + // discard errors reported on the same line + return + } + if n > 10 { + // too many errors; terminate early + panic(bailout{}) + } + p.errors.Add(filePos, msg) +} + +func (p *Parser) errorExpected(pos Pos, msg string) { + msg = "expected " + msg + if pos == p.pos { + // error happened at the current position: provide more specific + switch { + case p.token == token.Semicolon && p.tokenLit == "\n": + msg += ", found newline" + case p.token.IsLiteral(): + msg += ", found " + p.tokenLit + default: + msg += ", found '" + p.token.String() + "'" + } + } + p.error(pos, msg) +} + +func (p *Parser) next() { + if p.trace && p.pos.IsValid() { + s := p.token.String() + switch { + case p.token.IsLiteral(): + p.printTrace(s, p.tokenLit) + case p.token.IsOperator(), p.token.IsKeyword(): + p.printTrace(`"` + s + `"`) + default: + p.printTrace(s) + } + } + p.token, p.tokenLit, p.pos = p.scanner.Scan() +} + +func (p *Parser) printTrace(a ...interface{}) { + const ( + dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + n = len(dots) + ) + + filePos := p.file.Position(p.pos) + _, _ = fmt.Fprintf(p.traceOut, "%5d: %5d:%3d: ", p.pos, filePos.Line, + filePos.Column) + i := 2 * p.indent + for i > n { + _, _ = fmt.Fprint(p.traceOut, dots) + i -= n + } + _, _ = fmt.Fprint(p.traceOut, dots[0:i]) + _, _ = fmt.Fprintln(p.traceOut, a...) +} + +func (p *Parser) safePos(pos Pos) Pos { + fileBase := p.file.Base + fileSize := p.file.Size + + if int(pos) < fileBase || int(pos) > fileBase+fileSize { + return Pos(fileBase + fileSize) + } + return pos +} + +func tracep(p *Parser, msg string) *Parser { + p.printTrace(msg, "(") + p.indent++ + return p +} + +func untracep(p *Parser) { + p.indent-- + p.printTrace(")") +} diff --git a/vendor/github.com/d5/tengo/v2/parser/pos.go b/vendor/github.com/d5/tengo/v2/parser/pos.go new file mode 100644 index 00000000..f8d3898c --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/pos.go @@ -0,0 +1,12 @@ +package parser + +// Pos represents a position in the file set. +type Pos int + +// NoPos represents an invalid position. +const NoPos Pos = 0 + +// IsValid returns true if the position is valid. +func (p Pos) IsValid() bool { + return p != NoPos +} diff --git a/vendor/github.com/d5/tengo/v2/parser/scanner.go b/vendor/github.com/d5/tengo/v2/parser/scanner.go new file mode 100644 index 00000000..f1d820a4 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/scanner.go @@ -0,0 +1,689 @@ +package parser + +import ( + "fmt" + "unicode" + "unicode/utf8" + + "github.com/d5/tengo/v2/token" +) + +// byte order mark +const bom = 0xFEFF + +// ScanMode represents a scanner mode. +type ScanMode int + +// List of scanner modes. +const ( + ScanComments ScanMode = 1 << iota + DontInsertSemis +) + +// ScannerErrorHandler is an error handler for the scanner. +type ScannerErrorHandler func(pos SourceFilePos, msg string) + +// Scanner reads the Tengo source text. It's based on Go's scanner +// implementation. +type Scanner struct { + file *SourceFile // source file handle + src []byte // source + ch rune // current character + offset int // character offset + readOffset int // reading offset (position after current character) + lineOffset int // current line offset + insertSemi bool // insert a semicolon before next newline + errorHandler ScannerErrorHandler // error reporting; or nil + errorCount int // number of errors encountered + mode ScanMode +} + +// NewScanner creates a Scanner. +func NewScanner( + file *SourceFile, + src []byte, + errorHandler ScannerErrorHandler, + mode ScanMode, +) *Scanner { + if file.Size != len(src) { + panic(fmt.Sprintf("file size (%d) does not match src len (%d)", + file.Size, len(src))) + } + + s := &Scanner{ + file: file, + src: src, + errorHandler: errorHandler, + ch: ' ', + mode: mode, + } + + s.next() + if s.ch == bom { + s.next() // ignore BOM at file beginning + } + + return s +} + +// ErrorCount returns the number of errors. +func (s *Scanner) ErrorCount() int { + return s.errorCount +} + +// Scan returns a token, token literal and its position. +func (s *Scanner) Scan() ( + tok token.Token, + literal string, + pos Pos, +) { + s.skipWhitespace() + + pos = s.file.FileSetPos(s.offset) + + insertSemi := false + + // determine token value + switch ch := s.ch; { + case isLetter(ch): + literal = s.scanIdentifier() + tok = token.Lookup(literal) + switch tok { + case token.Ident, token.Break, token.Continue, token.Return, + token.Export, token.True, token.False, token.Undefined: + insertSemi = true + } + case '0' <= ch && ch <= '9': + insertSemi = true + tok, literal = s.scanNumber(false) + default: + s.next() // always make progress + + switch ch { + case -1: // EOF + if s.insertSemi { + s.insertSemi = false // EOF consumed + return token.Semicolon, "\n", pos + } + tok = token.EOF + case '\n': + // we only reach here if s.insertSemi was set in the first place + s.insertSemi = false // newline consumed + return token.Semicolon, "\n", pos + case '"': + insertSemi = true + tok = token.String + literal = s.scanString() + case '\'': + insertSemi = true + tok = token.Char + literal = s.scanRune() + case '`': + insertSemi = true + tok = token.String + literal = s.scanRawString() + case ':': + tok = s.switch2(token.Colon, token.Define) + case '.': + if '0' <= s.ch && s.ch <= '9' { + insertSemi = true + tok, literal = s.scanNumber(true) + } else { + tok = token.Period + if s.ch == '.' && s.peek() == '.' { + s.next() + s.next() // consume last '.' + tok = token.Ellipsis + } + } + case ',': + tok = token.Comma + case '?': + tok = token.Question + case ';': + tok = token.Semicolon + literal = ";" + case '(': + tok = token.LParen + case ')': + insertSemi = true + tok = token.RParen + case '[': + tok = token.LBrack + case ']': + insertSemi = true + tok = token.RBrack + case '{': + tok = token.LBrace + case '}': + insertSemi = true + tok = token.RBrace + case '+': + tok = s.switch3(token.Add, token.AddAssign, '+', token.Inc) + if tok == token.Inc { + insertSemi = true + } + case '-': + tok = s.switch3(token.Sub, token.SubAssign, '-', token.Dec) + if tok == token.Dec { + insertSemi = true + } + case '*': + tok = s.switch2(token.Mul, token.MulAssign) + case '/': + if s.ch == '/' || s.ch == '*' { + // comment + if s.insertSemi && s.findLineEnd() { + // reset position to the beginning of the comment + s.ch = '/' + s.offset = s.file.Offset(pos) + s.readOffset = s.offset + 1 + s.insertSemi = false // newline consumed + return token.Semicolon, "\n", pos + } + comment := s.scanComment() + if s.mode&ScanComments == 0 { + // skip comment + s.insertSemi = false // newline consumed + return s.Scan() + } + tok = token.Comment + literal = comment + } else { + tok = s.switch2(token.Quo, token.QuoAssign) + } + case '%': + tok = s.switch2(token.Rem, token.RemAssign) + case '^': + tok = s.switch2(token.Xor, token.XorAssign) + case '<': + tok = s.switch4(token.Less, token.LessEq, '<', + token.Shl, token.ShlAssign) + case '>': + tok = s.switch4(token.Greater, token.GreaterEq, '>', + token.Shr, token.ShrAssign) + case '=': + tok = s.switch2(token.Assign, token.Equal) + case '!': + tok = s.switch2(token.Not, token.NotEqual) + case '&': + if s.ch == '^' { + s.next() + tok = s.switch2(token.AndNot, token.AndNotAssign) + } else { + tok = s.switch3(token.And, token.AndAssign, '&', token.LAnd) + } + case '|': + tok = s.switch3(token.Or, token.OrAssign, '|', token.LOr) + default: + // next reports unexpected BOMs - don't repeat + if ch != bom { + s.error(s.file.Offset(pos), + fmt.Sprintf("illegal character %#U", ch)) + } + insertSemi = s.insertSemi // preserve insertSemi info + tok = token.Illegal + literal = string(ch) + } + } + if s.mode&DontInsertSemis == 0 { + s.insertSemi = insertSemi + } + return +} + +func (s *Scanner) next() { + if s.readOffset < len(s.src) { + s.offset = s.readOffset + if s.ch == '\n' { + s.lineOffset = s.offset + s.file.AddLine(s.offset) + } + r, w := rune(s.src[s.readOffset]), 1 + switch { + case r == 0: + s.error(s.offset, "illegal character NUL") + case r >= utf8.RuneSelf: + // not ASCII + r, w = utf8.DecodeRune(s.src[s.readOffset:]) + if r == utf8.RuneError && w == 1 { + s.error(s.offset, "illegal UTF-8 encoding") + } else if r == bom && s.offset > 0 { + s.error(s.offset, "illegal byte order mark") + } + } + s.readOffset += w + s.ch = r + } else { + s.offset = len(s.src) + if s.ch == '\n' { + s.lineOffset = s.offset + s.file.AddLine(s.offset) + } + s.ch = -1 // eof + } +} + +func (s *Scanner) peek() byte { + if s.readOffset < len(s.src) { + return s.src[s.readOffset] + } + return 0 +} + +func (s *Scanner) error(offset int, msg string) { + if s.errorHandler != nil { + s.errorHandler(s.file.Position(s.file.FileSetPos(offset)), msg) + } + s.errorCount++ +} + +func (s *Scanner) scanComment() string { + // initial '/' already consumed; s.ch == '/' || s.ch == '*' + offs := s.offset - 1 // position of initial '/' + var numCR int + + if s.ch == '/' { + //-style comment + // (the final '\n' is not considered part of the comment) + s.next() + for s.ch != '\n' && s.ch >= 0 { + if s.ch == '\r' { + numCR++ + } + s.next() + } + goto exit + } + + /*-style comment */ + s.next() + for s.ch >= 0 { + ch := s.ch + if ch == '\r' { + numCR++ + } + s.next() + if ch == '*' && s.ch == '/' { + s.next() + goto exit + } + } + + s.error(offs, "comment not terminated") + +exit: + lit := s.src[offs:s.offset] + + // On Windows, a (//-comment) line may end in "\r\n". + // Remove the final '\r' before analyzing the text for line directives (matching the compiler). + // Remove any other '\r' afterwards (matching the pre-existing behavior of the scanner). + if numCR > 0 && len(lit) >= 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' { + lit = lit[:len(lit)-1] + numCR-- + } + if numCR > 0 { + lit = StripCR(lit, lit[1] == '*') + } + return string(lit) +} + +func (s *Scanner) findLineEnd() bool { + // initial '/' already consumed + + defer func(offs int) { + // reset scanner state to where it was upon calling findLineEnd + s.ch = '/' + s.offset = offs + s.readOffset = offs + 1 + s.next() // consume initial '/' again + }(s.offset - 1) + + // read ahead until a newline, EOF, or non-comment tok is found + for s.ch == '/' || s.ch == '*' { + if s.ch == '/' { + //-style comment always contains a newline + return true + } + /*-style comment: look for newline */ + s.next() + for s.ch >= 0 { + ch := s.ch + if ch == '\n' { + return true + } + s.next() + if ch == '*' && s.ch == '/' { + s.next() + break + } + } + s.skipWhitespace() // s.insertSemi is set + if s.ch < 0 || s.ch == '\n' { + return true + } + if s.ch != '/' { + // non-comment tok + return false + } + s.next() // consume '/' + } + return false +} + +func (s *Scanner) scanIdentifier() string { + offs := s.offset + for isLetter(s.ch) || isDigit(s.ch) { + s.next() + } + return string(s.src[offs:s.offset]) +} + +func (s *Scanner) scanMantissa(base int) { + for digitVal(s.ch) < base { + s.next() + } +} + +func (s *Scanner) scanNumber( + seenDecimalPoint bool, +) (tok token.Token, lit string) { + // digitVal(s.ch) < 10 + offs := s.offset + tok = token.Int + + defer func() { + lit = string(s.src[offs:s.offset]) + }() + + if seenDecimalPoint { + offs-- + tok = token.Float + s.scanMantissa(10) + goto exponent + } + + if s.ch == '0' { + // int or float + offs := s.offset + s.next() + if s.ch == 'x' || s.ch == 'X' { + // hexadecimal int + s.next() + s.scanMantissa(16) + if s.offset-offs <= 2 { + // only scanned "0x" or "0X" + s.error(offs, "illegal hexadecimal number") + } + } else { + // octal int or float + seenDecimalDigit := false + s.scanMantissa(8) + if s.ch == '8' || s.ch == '9' { + // illegal octal int or float + seenDecimalDigit = true + s.scanMantissa(10) + } + if s.ch == '.' || s.ch == 'e' || s.ch == 'E' || s.ch == 'i' { + goto fraction + } + // octal int + if seenDecimalDigit { + s.error(offs, "illegal octal number") + } + } + return + } + + // decimal int or float + s.scanMantissa(10) + +fraction: + if s.ch == '.' { + tok = token.Float + s.next() + s.scanMantissa(10) + } + +exponent: + if s.ch == 'e' || s.ch == 'E' { + tok = token.Float + s.next() + if s.ch == '-' || s.ch == '+' { + s.next() + } + if digitVal(s.ch) < 10 { + s.scanMantissa(10) + } else { + s.error(offs, "illegal floating-point exponent") + } + } + return +} + +func (s *Scanner) scanEscape(quote rune) bool { + offs := s.offset + + var n int + var base, max uint32 + switch s.ch { + case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote: + s.next() + return true + case '0', '1', '2', '3', '4', '5', '6', '7': + n, base, max = 3, 8, 255 + case 'x': + s.next() + n, base, max = 2, 16, 255 + case 'u': + s.next() + n, base, max = 4, 16, unicode.MaxRune + case 'U': + s.next() + n, base, max = 8, 16, unicode.MaxRune + default: + msg := "unknown escape sequence" + if s.ch < 0 { + msg = "escape sequence not terminated" + } + s.error(offs, msg) + return false + } + + var x uint32 + for n > 0 { + d := uint32(digitVal(s.ch)) + if d >= base { + msg := fmt.Sprintf( + "illegal character %#U in escape sequence", s.ch) + if s.ch < 0 { + msg = "escape sequence not terminated" + } + s.error(s.offset, msg) + return false + } + x = x*base + d + s.next() + n-- + } + + if x > max || 0xD800 <= x && x < 0xE000 { + s.error(offs, "escape sequence is invalid Unicode code point") + return false + } + return true +} + +func (s *Scanner) scanRune() string { + offs := s.offset - 1 // '\'' opening already consumed + + valid := true + n := 0 + for { + ch := s.ch + if ch == '\n' || ch < 0 { + // only report error if we don't have one already + if valid { + s.error(offs, "rune literal not terminated") + valid = false + } + break + } + s.next() + if ch == '\'' { + break + } + n++ + if ch == '\\' { + if !s.scanEscape('\'') { + valid = false + } + // continue to read to closing quote + } + } + + if valid && n != 1 { + s.error(offs, "illegal rune literal") + } + return string(s.src[offs:s.offset]) +} + +func (s *Scanner) scanString() string { + offs := s.offset - 1 // '"' opening already consumed + + for { + ch := s.ch + if ch == '\n' || ch < 0 { + s.error(offs, "string literal not terminated") + break + } + s.next() + if ch == '"' { + break + } + if ch == '\\' { + s.scanEscape('"') + } + } + return string(s.src[offs:s.offset]) +} + +func (s *Scanner) scanRawString() string { + offs := s.offset - 1 // '`' opening already consumed + + hasCR := false + for { + ch := s.ch + if ch < 0 { + s.error(offs, "raw string literal not terminated") + break + } + + s.next() + + if ch == '`' { + break + } + + if ch == '\r' { + hasCR = true + } + } + + lit := s.src[offs:s.offset] + if hasCR { + lit = StripCR(lit, false) + } + return string(lit) +} + +// StripCR removes carriage return characters. +func StripCR(b []byte, comment bool) []byte { + c := make([]byte, len(b)) + i := 0 + for j, ch := range b { + // In a /*-style comment, don't strip \r from *\r/ (incl. sequences of + // \r from *\r\r...\r/) since the resulting */ would terminate the + // comment too early unless the \r is immediately following the opening + // /* in which case it's ok because /*/ is not closed yet. + if ch != '\r' || comment && i > len("/*") && c[i-1] == '*' && + j+1 < len(b) && b[j+1] == '/' { + c[i] = ch + i++ + } + } + return c[:i] +} + +func (s *Scanner) skipWhitespace() { + for s.ch == ' ' || s.ch == '\t' || s.ch == '\n' && !s.insertSemi || + s.ch == '\r' { + s.next() + } +} + +func (s *Scanner) switch2(tok0, tok1 token.Token) token.Token { + if s.ch == '=' { + s.next() + return tok1 + } + return tok0 +} + +func (s *Scanner) switch3( + tok0, tok1 token.Token, + ch2 rune, + tok2 token.Token, +) token.Token { + if s.ch == '=' { + s.next() + return tok1 + } + if s.ch == ch2 { + s.next() + return tok2 + } + return tok0 +} + +func (s *Scanner) switch4( + tok0, tok1 token.Token, + ch2 rune, + tok2, tok3 token.Token, +) token.Token { + if s.ch == '=' { + s.next() + return tok1 + } + if s.ch == ch2 { + s.next() + if s.ch == '=' { + s.next() + return tok3 + } + return tok2 + } + return tok0 +} + +func isLetter(ch rune) bool { + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || + ch >= utf8.RuneSelf && unicode.IsLetter(ch) +} + +func isDigit(ch rune) bool { + return '0' <= ch && ch <= '9' || + ch >= utf8.RuneSelf && unicode.IsDigit(ch) +} + +func digitVal(ch rune) int { + switch { + case '0' <= ch && ch <= '9': + return int(ch - '0') + case 'a' <= ch && ch <= 'f': + return int(ch - 'a' + 10) + case 'A' <= ch && ch <= 'F': + return int(ch - 'A' + 10) + } + return 16 // larger than any legal digit val +} diff --git a/vendor/github.com/d5/tengo/v2/parser/source_file.go b/vendor/github.com/d5/tengo/v2/parser/source_file.go new file mode 100644 index 00000000..e9f4b0f5 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/source_file.go @@ -0,0 +1,231 @@ +package parser + +import ( + "fmt" + "sort" +) + +// SourceFilePos represents a position information in the file. +type SourceFilePos struct { + Filename string // filename, if any + Offset int // offset, starting at 0 + Line int // line number, starting at 1 + Column int // column number, starting at 1 (byte count) +} + +// IsValid returns true if the position is valid. +func (p SourceFilePos) IsValid() bool { + return p.Line > 0 +} + +// String returns a string in one of several forms: +// +// file:line:column valid position with file name +// file:line valid position with file name but no column (column == 0) +// line:column valid position without file name +// line valid position without file name and no column (column == 0) +// file invalid position with file name +// - invalid position without file name +// +func (p SourceFilePos) String() string { + s := p.Filename + if p.IsValid() { + if s != "" { + s += ":" + } + s += fmt.Sprintf("%d", p.Line) + if p.Column != 0 { + s += fmt.Sprintf(":%d", p.Column) + } + } + if s == "" { + s = "-" + } + return s +} + +// SourceFileSet represents a set of source files. +type SourceFileSet struct { + Base int // base offset for the next file + Files []*SourceFile // list of files in the order added to the set + LastFile *SourceFile // cache of last file looked up +} + +// NewFileSet creates a new file set. +func NewFileSet() *SourceFileSet { + return &SourceFileSet{ + Base: 1, // 0 == NoPos + } +} + +// AddFile adds a new file in the file set. +func (s *SourceFileSet) AddFile(filename string, base, size int) *SourceFile { + if base < 0 { + base = s.Base + } + if base < s.Base || size < 0 { + panic("illegal base or size") + } + f := &SourceFile{ + set: s, + Name: filename, + Base: base, + Size: size, + Lines: []int{0}, + } + base += size + 1 // +1 because EOF also has a position + if base < 0 { + panic("offset overflow (> 2G of source code in file set)") + } + + // add the file to the file set + s.Base = base + s.Files = append(s.Files, f) + s.LastFile = f + return f +} + +// File returns the file that contains the position p. If no such file is +// found (for instance for p == NoPos), the result is nil. +func (s *SourceFileSet) File(p Pos) (f *SourceFile) { + if p != NoPos { + f = s.file(p) + } + return +} + +// Position converts a SourcePos p in the fileset into a SourceFilePos value. +func (s *SourceFileSet) Position(p Pos) (pos SourceFilePos) { + if p != NoPos { + if f := s.file(p); f != nil { + return f.position(p) + } + } + return +} + +func (s *SourceFileSet) file(p Pos) *SourceFile { + // common case: p is in last file + f := s.LastFile + if f != nil && f.Base <= int(p) && int(p) <= f.Base+f.Size { + return f + } + + // p is not in last file - search all files + if i := searchFiles(s.Files, int(p)); i >= 0 { + f := s.Files[i] + + // f.base <= int(p) by definition of searchFiles + if int(p) <= f.Base+f.Size { + s.LastFile = f // race is ok - s.last is only a cache + return f + } + } + return nil +} + +func searchFiles(a []*SourceFile, x int) int { + return sort.Search(len(a), func(i int) bool { return a[i].Base > x }) - 1 +} + +// SourceFile represents a source file. +type SourceFile struct { + // SourceFile set for the file + set *SourceFileSet + // SourceFile name as provided to AddFile + Name string + // SourcePos value range for this file is [base...base+size] + Base int + // SourceFile size as provided to AddFile + Size int + // Lines contains the offset of the first character for each line + // (the first entry is always 0) + Lines []int +} + +// Set returns SourceFileSet. +func (f *SourceFile) Set() *SourceFileSet { + return f.set +} + +// LineCount returns the current number of lines. +func (f *SourceFile) LineCount() int { + return len(f.Lines) +} + +// AddLine adds a new line. +func (f *SourceFile) AddLine(offset int) { + i := len(f.Lines) + if (i == 0 || f.Lines[i-1] < offset) && offset < f.Size { + f.Lines = append(f.Lines, offset) + } +} + +// LineStart returns the position of the first character in the line. +func (f *SourceFile) LineStart(line int) Pos { + if line < 1 { + panic("illegal line number (line numbering starts at 1)") + } + if line > len(f.Lines) { + panic("illegal line number") + } + return Pos(f.Base + f.Lines[line-1]) +} + +// FileSetPos returns the position in the file set. +func (f *SourceFile) FileSetPos(offset int) Pos { + if offset > f.Size { + panic("illegal file offset") + } + return Pos(f.Base + offset) +} + +// Offset translates the file set position into the file offset. +func (f *SourceFile) Offset(p Pos) int { + if int(p) < f.Base || int(p) > f.Base+f.Size { + panic("illegal SourcePos value") + } + return int(p) - f.Base +} + +// Position translates the file set position into the file position. +func (f *SourceFile) Position(p Pos) (pos SourceFilePos) { + if p != NoPos { + if int(p) < f.Base || int(p) > f.Base+f.Size { + panic("illegal SourcePos value") + } + pos = f.position(p) + } + return +} + +func (f *SourceFile) position(p Pos) (pos SourceFilePos) { + offset := int(p) - f.Base + pos.Offset = offset + pos.Filename, pos.Line, pos.Column = f.unpack(offset) + return +} + +func (f *SourceFile) unpack(offset int) (filename string, line, column int) { + filename = f.Name + if i := searchInts(f.Lines, offset); i >= 0 { + line, column = i+1, offset-f.Lines[i]+1 + } + return +} + +func searchInts(a []int, x int) int { + // This function body is a manually inlined version of: + // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1 + i, j := 0, len(a) + for i < j { + h := i + (j-i)/2 // avoid overflow when computing h + // i ≤ h < j + if a[h] <= x { + i = h + 1 + } else { + j = h + } + } + return i - 1 +} diff --git a/vendor/github.com/d5/tengo/v2/parser/stmt.go b/vendor/github.com/d5/tengo/v2/parser/stmt.go new file mode 100644 index 00000000..c0848c48 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/stmt.go @@ -0,0 +1,349 @@ +package parser + +import ( + "strings" + + "github.com/d5/tengo/v2/token" +) + +// Stmt represents a statement in the AST. +type Stmt interface { + Node + stmtNode() +} + +// AssignStmt represents an assignment statement. +type AssignStmt struct { + LHS []Expr + RHS []Expr + Token token.Token + TokenPos Pos +} + +func (s *AssignStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *AssignStmt) Pos() Pos { + return s.LHS[0].Pos() +} + +// End returns the position of first character immediately after the node. +func (s *AssignStmt) End() Pos { + return s.RHS[len(s.RHS)-1].End() +} + +func (s *AssignStmt) String() string { + var lhs, rhs []string + for _, e := range s.LHS { + lhs = append(lhs, e.String()) + } + for _, e := range s.RHS { + rhs = append(rhs, e.String()) + } + return strings.Join(lhs, ", ") + " " + s.Token.String() + + " " + strings.Join(rhs, ", ") +} + +// BadStmt represents a bad statement. +type BadStmt struct { + From Pos + To Pos +} + +func (s *BadStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *BadStmt) Pos() Pos { + return s.From +} + +// End returns the position of first character immediately after the node. +func (s *BadStmt) End() Pos { + return s.To +} + +func (s *BadStmt) String() string { + return "" +} + +// BlockStmt represents a block statement. +type BlockStmt struct { + Stmts []Stmt + LBrace Pos + RBrace Pos +} + +func (s *BlockStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *BlockStmt) Pos() Pos { + return s.LBrace +} + +// End returns the position of first character immediately after the node. +func (s *BlockStmt) End() Pos { + return s.RBrace + 1 +} + +func (s *BlockStmt) String() string { + var list []string + for _, e := range s.Stmts { + list = append(list, e.String()) + } + return "{" + strings.Join(list, "; ") + "}" +} + +// BranchStmt represents a branch statement. +type BranchStmt struct { + Token token.Token + TokenPos Pos + Label *Ident +} + +func (s *BranchStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *BranchStmt) Pos() Pos { + return s.TokenPos +} + +// End returns the position of first character immediately after the node. +func (s *BranchStmt) End() Pos { + if s.Label != nil { + return s.Label.End() + } + + return Pos(int(s.TokenPos) + len(s.Token.String())) +} + +func (s *BranchStmt) String() string { + var label string + if s.Label != nil { + label = " " + s.Label.Name + } + return s.Token.String() + label +} + +// EmptyStmt represents an empty statement. +type EmptyStmt struct { + Semicolon Pos + Implicit bool +} + +func (s *EmptyStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *EmptyStmt) Pos() Pos { + return s.Semicolon +} + +// End returns the position of first character immediately after the node. +func (s *EmptyStmt) End() Pos { + if s.Implicit { + return s.Semicolon + } + return s.Semicolon + 1 +} + +func (s *EmptyStmt) String() string { + return ";" +} + +// ExportStmt represents an export statement. +type ExportStmt struct { + ExportPos Pos + Result Expr +} + +func (s *ExportStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *ExportStmt) Pos() Pos { + return s.ExportPos +} + +// End returns the position of first character immediately after the node. +func (s *ExportStmt) End() Pos { + return s.Result.End() +} + +func (s *ExportStmt) String() string { + return "export " + s.Result.String() +} + +// ExprStmt represents an expression statement. +type ExprStmt struct { + Expr Expr +} + +func (s *ExprStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *ExprStmt) Pos() Pos { + return s.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (s *ExprStmt) End() Pos { + return s.Expr.End() +} + +func (s *ExprStmt) String() string { + return s.Expr.String() +} + +// ForInStmt represents a for-in statement. +type ForInStmt struct { + ForPos Pos + Key *Ident + Value *Ident + Iterable Expr + Body *BlockStmt +} + +func (s *ForInStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *ForInStmt) Pos() Pos { + return s.ForPos +} + +// End returns the position of first character immediately after the node. +func (s *ForInStmt) End() Pos { + return s.Body.End() +} + +func (s *ForInStmt) String() string { + if s.Value != nil { + return "for " + s.Key.String() + ", " + s.Value.String() + + " in " + s.Iterable.String() + " " + s.Body.String() + } + return "for " + s.Key.String() + " in " + s.Iterable.String() + + " " + s.Body.String() +} + +// ForStmt represents a for statement. +type ForStmt struct { + ForPos Pos + Init Stmt + Cond Expr + Post Stmt + Body *BlockStmt +} + +func (s *ForStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *ForStmt) Pos() Pos { + return s.ForPos +} + +// End returns the position of first character immediately after the node. +func (s *ForStmt) End() Pos { + return s.Body.End() +} + +func (s *ForStmt) String() string { + var init, cond, post string + if s.Init != nil { + init = s.Init.String() + } + if s.Cond != nil { + cond = s.Cond.String() + " " + } + if s.Post != nil { + post = s.Post.String() + } + + if init != "" || post != "" { + return "for " + init + " ; " + cond + " ; " + post + s.Body.String() + } + return "for " + cond + s.Body.String() +} + +// IfStmt represents an if statement. +type IfStmt struct { + IfPos Pos + Init Stmt + Cond Expr + Body *BlockStmt + Else Stmt // else branch; or nil +} + +func (s *IfStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *IfStmt) Pos() Pos { + return s.IfPos +} + +// End returns the position of first character immediately after the node. +func (s *IfStmt) End() Pos { + if s.Else != nil { + return s.Else.End() + } + return s.Body.End() +} + +func (s *IfStmt) String() string { + var initStmt, elseStmt string + if s.Init != nil { + initStmt = s.Init.String() + "; " + } + if s.Else != nil { + elseStmt = " else " + s.Else.String() + } + return "if " + initStmt + s.Cond.String() + " " + + s.Body.String() + elseStmt +} + +// IncDecStmt represents increment or decrement statement. +type IncDecStmt struct { + Expr Expr + Token token.Token + TokenPos Pos +} + +func (s *IncDecStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *IncDecStmt) Pos() Pos { + return s.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (s *IncDecStmt) End() Pos { + return Pos(int(s.TokenPos) + 2) +} + +func (s *IncDecStmt) String() string { + return s.Expr.String() + s.Token.String() +} + +// ReturnStmt represents a return statement. +type ReturnStmt struct { + ReturnPos Pos + Result Expr +} + +func (s *ReturnStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *ReturnStmt) Pos() Pos { + return s.ReturnPos +} + +// End returns the position of first character immediately after the node. +func (s *ReturnStmt) End() Pos { + if s.Result != nil { + return s.Result.End() + } + return s.ReturnPos + 6 +} + +func (s *ReturnStmt) String() string { + if s.Result != nil { + return "return " + s.Result.String() + } + return "return" +} diff --git a/vendor/github.com/d5/tengo/v2/script.go b/vendor/github.com/d5/tengo/v2/script.go new file mode 100644 index 00000000..906771d9 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/script.go @@ -0,0 +1,313 @@ +package tengo + +import ( + "context" + "fmt" + "sync" + + "github.com/d5/tengo/v2/parser" +) + +// Script can simplify compilation and execution of embedded scripts. +type Script struct { + variables map[string]*Variable + modules *ModuleMap + input []byte + maxAllocs int64 + maxConstObjects int + enableFileImport bool +} + +// NewScript creates a Script instance with an input script. +func NewScript(input []byte) *Script { + return &Script{ + variables: make(map[string]*Variable), + input: input, + maxAllocs: -1, + maxConstObjects: -1, + } +} + +// Add adds a new variable or updates an existing variable to the script. +func (s *Script) Add(name string, value interface{}) error { + obj, err := FromInterface(value) + if err != nil { + return err + } + s.variables[name] = &Variable{ + name: name, + value: obj, + } + return nil +} + +// Remove removes (undefines) an existing variable for the script. It returns +// false if the variable name is not defined. +func (s *Script) Remove(name string) bool { + if _, ok := s.variables[name]; !ok { + return false + } + delete(s.variables, name) + return true +} + +// SetImports sets import modules. +func (s *Script) SetImports(modules *ModuleMap) { + s.modules = modules +} + +// SetMaxAllocs sets the maximum number of objects allocations during the run +// time. Compiled script will return ErrObjectAllocLimit error if it +// exceeds this limit. +func (s *Script) SetMaxAllocs(n int64) { + s.maxAllocs = n +} + +// SetMaxConstObjects sets the maximum number of objects in the compiled +// constants. +func (s *Script) SetMaxConstObjects(n int) { + s.maxConstObjects = n +} + +// EnableFileImport enables or disables module loading from local files. Local +// file modules are disabled by default. +func (s *Script) EnableFileImport(enable bool) { + s.enableFileImport = enable +} + +// Compile compiles the script with all the defined variables, and, returns +// Compiled object. +func (s *Script) Compile() (*Compiled, error) { + symbolTable, globals, err := s.prepCompile() + if err != nil { + return nil, err + } + + fileSet := parser.NewFileSet() + srcFile := fileSet.AddFile("(main)", -1, len(s.input)) + p := parser.NewParser(srcFile, s.input, nil) + file, err := p.ParseFile() + if err != nil { + return nil, err + } + + c := NewCompiler(srcFile, symbolTable, nil, s.modules, nil) + c.EnableFileImport(s.enableFileImport) + if err := c.Compile(file); err != nil { + return nil, err + } + + // reduce globals size + globals = globals[:symbolTable.MaxSymbols()+1] + + // global symbol names to indexes + globalIndexes := make(map[string]int, len(globals)) + for _, name := range symbolTable.Names() { + symbol, _, _ := symbolTable.Resolve(name) + if symbol.Scope == ScopeGlobal { + globalIndexes[name] = symbol.Index + } + } + + // remove duplicates from constants + bytecode := c.Bytecode() + bytecode.RemoveDuplicates() + + // check the constant objects limit + if s.maxConstObjects >= 0 { + cnt := bytecode.CountObjects() + if cnt > s.maxConstObjects { + return nil, fmt.Errorf("exceeding constant objects limit: %d", cnt) + } + } + return &Compiled{ + globalIndexes: globalIndexes, + bytecode: bytecode, + globals: globals, + maxAllocs: s.maxAllocs, + }, nil +} + +// Run compiles and runs the scripts. Use returned compiled object to access +// global variables. +func (s *Script) Run() (compiled *Compiled, err error) { + compiled, err = s.Compile() + if err != nil { + return + } + err = compiled.Run() + return +} + +// RunContext is like Run but includes a context. +func (s *Script) RunContext( + ctx context.Context, +) (compiled *Compiled, err error) { + compiled, err = s.Compile() + if err != nil { + return + } + err = compiled.RunContext(ctx) + return +} + +func (s *Script) prepCompile() ( + symbolTable *SymbolTable, + globals []Object, + err error, +) { + var names []string + for name := range s.variables { + names = append(names, name) + } + + symbolTable = NewSymbolTable() + for idx, fn := range builtinFuncs { + symbolTable.DefineBuiltin(idx, fn.Name) + } + + globals = make([]Object, GlobalsSize) + + for idx, name := range names { + symbol := symbolTable.Define(name) + if symbol.Index != idx { + panic(fmt.Errorf("wrong symbol index: %d != %d", + idx, symbol.Index)) + } + globals[symbol.Index] = s.variables[name].value + } + return +} + +// Compiled is a compiled instance of the user script. Use Script.Compile() to +// create Compiled object. +type Compiled struct { + globalIndexes map[string]int // global symbol name to index + bytecode *Bytecode + globals []Object + maxAllocs int64 + lock sync.RWMutex +} + +// Run executes the compiled script in the virtual machine. +func (c *Compiled) Run() error { + c.lock.Lock() + defer c.lock.Unlock() + + v := NewVM(c.bytecode, c.globals, c.maxAllocs) + return v.Run() +} + +// RunContext is like Run but includes a context. +func (c *Compiled) RunContext(ctx context.Context) (err error) { + c.lock.Lock() + defer c.lock.Unlock() + + v := NewVM(c.bytecode, c.globals, c.maxAllocs) + ch := make(chan error, 1) + go func() { + ch <- v.Run() + }() + + select { + case <-ctx.Done(): + v.Abort() + <-ch + err = ctx.Err() + case err = <-ch: + } + return +} + +// Clone creates a new copy of Compiled. Cloned copies are safe for concurrent +// use by multiple goroutines. +func (c *Compiled) Clone() *Compiled { + c.lock.Lock() + defer c.lock.Unlock() + + clone := &Compiled{ + globalIndexes: c.globalIndexes, + bytecode: c.bytecode, + globals: make([]Object, len(c.globals)), + maxAllocs: c.maxAllocs, + } + // copy global objects + for idx, g := range c.globals { + if g != nil { + clone.globals[idx] = g + } + } + return clone +} + +// IsDefined returns true if the variable name is defined (has value) before or +// after the execution. +func (c *Compiled) IsDefined(name string) bool { + c.lock.RLock() + defer c.lock.RUnlock() + + idx, ok := c.globalIndexes[name] + if !ok { + return false + } + v := c.globals[idx] + if v == nil { + return false + } + return v != UndefinedValue +} + +// Get returns a variable identified by the name. +func (c *Compiled) Get(name string) *Variable { + c.lock.RLock() + defer c.lock.RUnlock() + + value := UndefinedValue + if idx, ok := c.globalIndexes[name]; ok { + value = c.globals[idx] + if value == nil { + value = UndefinedValue + } + } + return &Variable{ + name: name, + value: value, + } +} + +// GetAll returns all the variables that are defined by the compiled script. +func (c *Compiled) GetAll() []*Variable { + c.lock.RLock() + defer c.lock.RUnlock() + + var vars []*Variable + for name, idx := range c.globalIndexes { + value := c.globals[idx] + if value == nil { + value = UndefinedValue + } + vars = append(vars, &Variable{ + name: name, + value: value, + }) + } + return vars +} + +// Set replaces the value of a global variable identified by the name. An error +// will be returned if the name was not defined during compilation. +func (c *Compiled) Set(name string, value interface{}) error { + c.lock.Lock() + defer c.lock.Unlock() + + obj, err := FromInterface(value) + if err != nil { + return err + } + idx, ok := c.globalIndexes[name] + if !ok { + return fmt.Errorf("'%s' is not defined", name) + } + c.globals[idx] = obj + return nil +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/base64.go b/vendor/github.com/d5/tengo/v2/stdlib/base64.go new file mode 100644 index 00000000..b4c5b56e --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/base64.go @@ -0,0 +1,34 @@ +package stdlib + +import ( + "encoding/base64" + + "github.com/d5/tengo/v2" +) + +var base64Module = map[string]tengo.Object{ + "encode": &tengo.UserFunction{ + Value: FuncAYRS(base64.StdEncoding.EncodeToString), + }, + "decode": &tengo.UserFunction{ + Value: FuncASRYE(base64.StdEncoding.DecodeString), + }, + "raw_encode": &tengo.UserFunction{ + Value: FuncAYRS(base64.RawStdEncoding.EncodeToString), + }, + "raw_decode": &tengo.UserFunction{ + Value: FuncASRYE(base64.RawStdEncoding.DecodeString), + }, + "url_encode": &tengo.UserFunction{ + Value: FuncAYRS(base64.URLEncoding.EncodeToString), + }, + "url_decode": &tengo.UserFunction{ + Value: FuncASRYE(base64.URLEncoding.DecodeString), + }, + "raw_url_encode": &tengo.UserFunction{ + Value: FuncAYRS(base64.RawURLEncoding.EncodeToString), + }, + "raw_url_decode": &tengo.UserFunction{ + Value: FuncASRYE(base64.RawURLEncoding.DecodeString), + }, +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/builtin_modules.go b/vendor/github.com/d5/tengo/v2/stdlib/builtin_modules.go new file mode 100644 index 00000000..cf0e9621 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/builtin_modules.go @@ -0,0 +1,18 @@ +package stdlib + +import ( + "github.com/d5/tengo/v2" +) + +// BuiltinModules are builtin type standard library modules. +var BuiltinModules = map[string]map[string]tengo.Object{ + "math": mathModule, + "os": osModule, + "text": textModule, + "times": timesModule, + "rand": randModule, + "fmt": fmtModule, + "json": jsonModule, + "base64": base64Module, + "hex": hexModule, +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/errors.go b/vendor/github.com/d5/tengo/v2/stdlib/errors.go new file mode 100644 index 00000000..ad83c6f8 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/errors.go @@ -0,0 +1,12 @@ +package stdlib + +import ( + "github.com/d5/tengo/v2" +) + +func wrapError(err error) tengo.Object { + if err == nil { + return tengo.TrueValue + } + return &tengo.Error{Value: &tengo.String{Value: err.Error()}} +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/fmt.go b/vendor/github.com/d5/tengo/v2/stdlib/fmt.go new file mode 100644 index 00000000..9945277f --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/fmt.go @@ -0,0 +1,101 @@ +package stdlib + +import ( + "fmt" + + "github.com/d5/tengo/v2" +) + +var fmtModule = map[string]tengo.Object{ + "print": &tengo.UserFunction{Name: "print", Value: fmtPrint}, + "printf": &tengo.UserFunction{Name: "printf", Value: fmtPrintf}, + "println": &tengo.UserFunction{Name: "println", Value: fmtPrintln}, + "sprintf": &tengo.UserFunction{Name: "sprintf", Value: fmtSprintf}, +} + +func fmtPrint(args ...tengo.Object) (ret tengo.Object, err error) { + printArgs, err := getPrintArgs(args...) + if err != nil { + return nil, err + } + _, _ = fmt.Print(printArgs...) + return nil, nil +} + +func fmtPrintf(args ...tengo.Object) (ret tengo.Object, err error) { + numArgs := len(args) + if numArgs == 0 { + return nil, tengo.ErrWrongNumArguments + } + + format, ok := args[0].(*tengo.String) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "format", + Expected: "string", + Found: args[0].TypeName(), + } + } + if numArgs == 1 { + fmt.Print(format) + return nil, nil + } + + s, err := tengo.Format(format.Value, args[1:]...) + if err != nil { + return nil, err + } + fmt.Print(s) + return nil, nil +} + +func fmtPrintln(args ...tengo.Object) (ret tengo.Object, err error) { + printArgs, err := getPrintArgs(args...) + if err != nil { + return nil, err + } + printArgs = append(printArgs, "\n") + _, _ = fmt.Print(printArgs...) + return nil, nil +} + +func fmtSprintf(args ...tengo.Object) (ret tengo.Object, err error) { + numArgs := len(args) + if numArgs == 0 { + return nil, tengo.ErrWrongNumArguments + } + + format, ok := args[0].(*tengo.String) + if !ok { + return nil, tengo.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 := tengo.Format(format.Value, args[1:]...) + if err != nil { + return nil, err + } + return &tengo.String{Value: s}, nil +} + +func getPrintArgs(args ...tengo.Object) ([]interface{}, error) { + var printArgs []interface{} + l := 0 + for _, arg := range args { + s, _ := tengo.ToString(arg) + slen := len(s) + // make sure length does not exceed the limit + if l+slen > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + l += slen + printArgs = append(printArgs, s) + } + return printArgs, nil +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/func_typedefs.go b/vendor/github.com/d5/tengo/v2/stdlib/func_typedefs.go new file mode 100644 index 00000000..fdac933c --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/func_typedefs.go @@ -0,0 +1,1049 @@ +package stdlib + +import ( + "fmt" + + "github.com/d5/tengo/v2" +) + +// FuncAR transform a function of 'func()' signature into CallableFunc type. +func FuncAR(fn func()) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + fn() + return tengo.UndefinedValue, nil + } +} + +// FuncARI transform a function of 'func() int' signature into CallableFunc +// type. +func FuncARI(fn func() int) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return &tengo.Int{Value: int64(fn())}, nil + } +} + +// FuncARI64 transform a function of 'func() int64' signature into CallableFunc +// type. +func FuncARI64(fn func() int64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return &tengo.Int{Value: fn()}, nil + } +} + +// FuncAI64RI64 transform a function of 'func(int64) int64' signature into +// CallableFunc type. +func FuncAI64RI64(fn func(int64) int64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + return &tengo.Int{Value: fn(i1)}, nil + } +} + +// FuncAI64R transform a function of 'func(int64)' signature into CallableFunc +// type. +func FuncAI64R(fn func(int64)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + fn(i1) + return tengo.UndefinedValue, nil + } +} + +// FuncARB transform a function of 'func() bool' signature into CallableFunc +// type. +func FuncARB(fn func() bool) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + if fn() { + return tengo.TrueValue, nil + } + return tengo.FalseValue, nil + } +} + +// FuncARE transform a function of 'func() error' signature into CallableFunc +// type. +func FuncARE(fn func() error) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return wrapError(fn()), nil + } +} + +// FuncARS transform a function of 'func() string' signature into CallableFunc +// type. +func FuncARS(fn func() string) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + s := fn() + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} + +// FuncARSE transform a function of 'func() (string, error)' signature into +// CallableFunc type. +func FuncARSE(fn func() (string, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + res, err := fn() + if err != nil { + return wrapError(err), nil + } + if len(res) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: res}, nil + } +} + +// FuncARYE transform a function of 'func() ([]byte, error)' signature into +// CallableFunc type. +func FuncARYE(fn func() ([]byte, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + res, err := fn() + if err != nil { + return wrapError(err), nil + } + if len(res) > tengo.MaxBytesLen { + return nil, tengo.ErrBytesLimit + } + return &tengo.Bytes{Value: res}, nil + } +} + +// FuncARF transform a function of 'func() float64' signature into CallableFunc +// type. +func FuncARF(fn func() float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return &tengo.Float{Value: fn()}, nil + } +} + +// FuncARSs transform a function of 'func() []string' signature into +// CallableFunc type. +func FuncARSs(fn func() []string) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + arr := &tengo.Array{} + for _, elem := range fn() { + if len(elem) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: elem}) + } + return arr, nil + } +} + +// FuncARIsE transform a function of 'func() ([]int, error)' signature into +// CallableFunc type. +func FuncARIsE(fn func() ([]int, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + res, err := fn() + if err != nil { + return wrapError(err), nil + } + arr := &tengo.Array{} + for _, v := range res { + arr.Value = append(arr.Value, &tengo.Int{Value: int64(v)}) + } + return arr, nil + } +} + +// FuncAIRIs transform a function of 'func(int) []int' signature into +// CallableFunc type. +func FuncAIRIs(fn func(int) []int) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + res := fn(i1) + arr := &tengo.Array{} + for _, v := range res { + arr.Value = append(arr.Value, &tengo.Int{Value: int64(v)}) + } + return arr, nil + } +} + +// FuncAFRF transform a function of 'func(float64) float64' signature into +// CallableFunc type. +func FuncAFRF(fn func(float64) float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + return &tengo.Float{Value: fn(f1)}, nil + } +} + +// FuncAIR transform a function of 'func(int)' signature into CallableFunc type. +func FuncAIR(fn func(int)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + fn(i1) + return tengo.UndefinedValue, nil + } +} + +// FuncAIRF transform a function of 'func(int) float64' signature into +// CallableFunc type. +func FuncAIRF(fn func(int) float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + return &tengo.Float{Value: fn(i1)}, nil + } +} + +// FuncAFRI transform a function of 'func(float64) int' signature into +// CallableFunc type. +func FuncAFRI(fn func(float64) int) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + return &tengo.Int{Value: int64(fn(f1))}, nil + } +} + +// FuncAFFRF transform a function of 'func(float64, float64) float64' signature +// into CallableFunc type. +func FuncAFFRF(fn func(float64, float64) float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + f2, ok := tengo.ToFloat64(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "float(compatible)", + Found: args[1].TypeName(), + } + } + return &tengo.Float{Value: fn(f1, f2)}, nil + } +} + +// FuncAIFRF transform a function of 'func(int, float64) float64' signature +// into CallableFunc type. +func FuncAIFRF(fn func(int, float64) float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + f2, ok := tengo.ToFloat64(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "float(compatible)", + Found: args[1].TypeName(), + } + } + return &tengo.Float{Value: fn(i1, f2)}, nil + } +} + +// FuncAFIRF transform a function of 'func(float64, int) float64' signature +// into CallableFunc type. +func FuncAFIRF(fn func(float64, int) float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + return &tengo.Float{Value: fn(f1, i2)}, nil + } +} + +// FuncAFIRB transform a function of 'func(float64, int) bool' signature +// into CallableFunc type. +func FuncAFIRB(fn func(float64, int) bool) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + if fn(f1, i2) { + return tengo.TrueValue, nil + } + return tengo.FalseValue, nil + } +} + +// FuncAFRB transform a function of 'func(float64) bool' signature +// into CallableFunc type. +func FuncAFRB(fn func(float64) bool) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + if fn(f1) { + return tengo.TrueValue, nil + } + return tengo.FalseValue, nil + } +} + +// FuncASRS transform a function of 'func(string) string' signature into +// CallableFunc type. User function will return 'true' if underlying native +// function returns nil. +func FuncASRS(fn func(string) string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s := fn(s1) + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} + +// FuncASRSs transform a function of 'func(string) []string' signature into +// CallableFunc type. +func FuncASRSs(fn func(string) []string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res := fn(s1) + arr := &tengo.Array{} + for _, elem := range res { + if len(elem) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: elem}) + } + return arr, nil + } +} + +// FuncASRSE transform a function of 'func(string) (string, error)' signature +// into CallableFunc type. User function will return 'true' if underlying +// native function returns nil. +func FuncASRSE(fn func(string) (string, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res, err := fn(s1) + if err != nil { + return wrapError(err), nil + } + if len(res) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: res}, nil + } +} + +// FuncASRE transform a function of 'func(string) error' signature into +// CallableFunc type. User function will return 'true' if underlying native +// function returns nil. +func FuncASRE(fn func(string) error) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + return wrapError(fn(s1)), nil + } +} + +// FuncASSRE transform a function of 'func(string, string) error' signature +// into CallableFunc type. User function will return 'true' if underlying +// native function returns nil. +func FuncASSRE(fn func(string, string) error) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + return wrapError(fn(s1, s2)), nil + } +} + +// FuncASSRSs transform a function of 'func(string, string) []string' +// signature into CallableFunc type. +func FuncASSRSs(fn func(string, string) []string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + arr := &tengo.Array{} + for _, res := range fn(s1, s2) { + if len(res) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: res}) + } + return arr, nil + } +} + +// FuncASSIRSs transform a function of 'func(string, string, int) []string' +// signature into CallableFunc type. +func FuncASSIRSs(fn func(string, string, int) []string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 3 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + i3, ok := tengo.ToInt(args[2]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + } + arr := &tengo.Array{} + for _, res := range fn(s1, s2, i3) { + if len(res) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: res}) + } + return arr, nil + } +} + +// FuncASSRI transform a function of 'func(string, string) int' signature into +// CallableFunc type. +func FuncASSRI(fn func(string, string) int) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + return &tengo.Int{Value: int64(fn(s1, s2))}, nil + } +} + +// FuncASSRS transform a function of 'func(string, string) string' signature +// into CallableFunc type. +func FuncASSRS(fn func(string, string) string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + s := fn(s1, s2) + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} + +// FuncASSRB transform a function of 'func(string, string) bool' signature +// into CallableFunc type. +func FuncASSRB(fn func(string, string) bool) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + if fn(s1, s2) { + return tengo.TrueValue, nil + } + return tengo.FalseValue, nil + } +} + +// FuncASsSRS transform a function of 'func([]string, string) string' signature +// into CallableFunc type. +func FuncASsSRS(fn func([]string, string) string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + var ss1 []string + switch arg0 := args[0].(type) { + case *tengo.Array: + for idx, a := range arg0.Value { + as, ok := tengo.ToString(a) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("first[%d]", idx), + Expected: "string(compatible)", + Found: a.TypeName(), + } + } + ss1 = append(ss1, as) + } + case *tengo.ImmutableArray: + for idx, a := range arg0.Value { + as, ok := tengo.ToString(a) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("first[%d]", idx), + Expected: "string(compatible)", + Found: a.TypeName(), + } + } + ss1 = append(ss1, as) + } + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "array", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + s := fn(ss1, s2) + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} + +// FuncASI64RE transform a function of 'func(string, int64) error' signature +// into CallableFunc type. +func FuncASI64RE(fn func(string, int64) error) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt64(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + return wrapError(fn(s1, i2)), nil + } +} + +// FuncAIIRE transform a function of 'func(int, int) error' signature +// into CallableFunc type. +func FuncAIIRE(fn func(int, int) error) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + return wrapError(fn(i1, i2)), nil + } +} + +// FuncASIRS transform a function of 'func(string, int) string' signature +// into CallableFunc type. +func FuncASIRS(fn func(string, int) string) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + s := fn(s1, i2) + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} + +// FuncASIIRE transform a function of 'func(string, int, int) error' signature +// into CallableFunc type. +func FuncASIIRE(fn func(string, int, int) error) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 3 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + i3, ok := tengo.ToInt(args[2]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + } + return wrapError(fn(s1, i2, i3)), nil + } +} + +// FuncAYRIE transform a function of 'func([]byte) (int, error)' signature +// into CallableFunc type. +func FuncAYRIE(fn func([]byte) (int, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + y1, ok := tengo.ToByteSlice(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes(compatible)", + Found: args[0].TypeName(), + } + } + res, err := fn(y1) + if err != nil { + return wrapError(err), nil + } + return &tengo.Int{Value: int64(res)}, nil + } +} + +// FuncAYRS transform a function of 'func([]byte) string' signature into +// CallableFunc type. +func FuncAYRS(fn func([]byte) string) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + y1, ok := tengo.ToByteSlice(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes(compatible)", + Found: args[0].TypeName(), + } + } + res := fn(y1) + return &tengo.String{Value: res}, nil + } +} + +// FuncASRIE transform a function of 'func(string) (int, error)' signature +// into CallableFunc type. +func FuncASRIE(fn func(string) (int, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res, err := fn(s1) + if err != nil { + return wrapError(err), nil + } + return &tengo.Int{Value: int64(res)}, nil + } +} + +// FuncASRYE transform a function of 'func(string) ([]byte, error)' signature +// into CallableFunc type. +func FuncASRYE(fn func(string) ([]byte, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res, err := fn(s1) + if err != nil { + return wrapError(err), nil + } + if len(res) > tengo.MaxBytesLen { + return nil, tengo.ErrBytesLimit + } + return &tengo.Bytes{Value: res}, nil + } +} + +// FuncAIRSsE transform a function of 'func(int) ([]string, error)' signature +// into CallableFunc type. +func FuncAIRSsE(fn func(int) ([]string, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + res, err := fn(i1) + if err != nil { + return wrapError(err), nil + } + arr := &tengo.Array{} + for _, r := range res { + if len(r) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: r}) + } + return arr, nil + } +} + +// FuncAIRS transform a function of 'func(int) string' signature into +// CallableFunc type. +func FuncAIRS(fn func(int) string) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + s := fn(i1) + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/hex.go b/vendor/github.com/d5/tengo/v2/stdlib/hex.go new file mode 100644 index 00000000..981da696 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/hex.go @@ -0,0 +1,12 @@ +package stdlib + +import ( + "encoding/hex" + + "github.com/d5/tengo/v2" +) + +var hexModule = map[string]tengo.Object{ + "encode": &tengo.UserFunction{Value: FuncAYRS(hex.EncodeToString)}, + "decode": &tengo.UserFunction{Value: FuncASRYE(hex.DecodeString)}, +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/json.go b/vendor/github.com/d5/tengo/v2/stdlib/json.go new file mode 100644 index 00000000..be2185db --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/json.go @@ -0,0 +1,146 @@ +package stdlib + +import ( + "bytes" + gojson "encoding/json" + + "github.com/d5/tengo/v2" + "github.com/d5/tengo/v2/stdlib/json" +) + +var jsonModule = map[string]tengo.Object{ + "decode": &tengo.UserFunction{ + Name: "decode", + Value: jsonDecode, + }, + "encode": &tengo.UserFunction{ + Name: "encode", + Value: jsonEncode, + }, + "indent": &tengo.UserFunction{ + Name: "encode", + Value: jsonIndent, + }, + "html_escape": &tengo.UserFunction{ + Name: "html_escape", + Value: jsonHTMLEscape, + }, +} + +func jsonDecode(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + switch o := args[0].(type) { + case *tengo.Bytes: + v, err := json.Decode(o.Value) + if err != nil { + return &tengo.Error{ + Value: &tengo.String{Value: err.Error()}, + }, nil + } + return v, nil + case *tengo.String: + v, err := json.Decode([]byte(o.Value)) + if err != nil { + return &tengo.Error{ + Value: &tengo.String{Value: err.Error()}, + }, nil + } + return v, nil + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes/string", + Found: args[0].TypeName(), + } + } +} + +func jsonEncode(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + b, err := json.Encode(args[0]) + if err != nil { + return &tengo.Error{Value: &tengo.String{Value: err.Error()}}, nil + } + + return &tengo.Bytes{Value: b}, nil +} + +func jsonIndent(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 3 { + return nil, tengo.ErrWrongNumArguments + } + + prefix, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "prefix", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + + indent, ok := tengo.ToString(args[2]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "indent", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + } + + switch o := args[0].(type) { + case *tengo.Bytes: + var dst bytes.Buffer + err := gojson.Indent(&dst, o.Value, prefix, indent) + if err != nil { + return &tengo.Error{ + Value: &tengo.String{Value: err.Error()}, + }, nil + } + return &tengo.Bytes{Value: dst.Bytes()}, nil + case *tengo.String: + var dst bytes.Buffer + err := gojson.Indent(&dst, []byte(o.Value), prefix, indent) + if err != nil { + return &tengo.Error{ + Value: &tengo.String{Value: err.Error()}, + }, nil + } + return &tengo.Bytes{Value: dst.Bytes()}, nil + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes/string", + Found: args[0].TypeName(), + } + } +} + +func jsonHTMLEscape(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + switch o := args[0].(type) { + case *tengo.Bytes: + var dst bytes.Buffer + gojson.HTMLEscape(&dst, o.Value) + return &tengo.Bytes{Value: dst.Bytes()}, nil + case *tengo.String: + var dst bytes.Buffer + gojson.HTMLEscape(&dst, []byte(o.Value)) + return &tengo.Bytes{Value: dst.Bytes()}, nil + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes/string", + Found: args[0].TypeName(), + } + } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/json/decode.go b/vendor/github.com/d5/tengo/v2/stdlib/json/decode.go new file mode 100644 index 00000000..6d468ef0 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/json/decode.go @@ -0,0 +1,358 @@ +// A modified version of Go's JSON implementation. + +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package json + +import ( + "strconv" + "unicode" + "unicode/utf16" + "unicode/utf8" + + "github.com/d5/tengo/v2" +) + +// Decode parses the JSON-encoded data and returns the result object. +func Decode(data []byte) (tengo.Object, error) { + var d decodeState + err := checkValid(data, &d.scan) + if err != nil { + return nil, err + } + d.init(data) + d.scan.reset() + d.scanWhile(scanSkipSpace) + return d.value() +} + +// decodeState represents the state while decoding a JSON value. +type decodeState struct { + data []byte + off int // next read offset in data + opcode int // last read result + scan scanner +} + +// readIndex returns the position of the last byte read. +func (d *decodeState) readIndex() int { + return d.off - 1 +} + +const phasePanicMsg = "JSON decoder out of sync - data changing underfoot?" + +func (d *decodeState) init(data []byte) *decodeState { + d.data = data + d.off = 0 + return d +} + +// scanNext processes the byte at d.data[d.off]. +func (d *decodeState) scanNext() { + if d.off < len(d.data) { + d.opcode = d.scan.step(&d.scan, d.data[d.off]) + d.off++ + } else { + d.opcode = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 + } +} + +// scanWhile processes bytes in d.data[d.off:] until it +// receives a scan code not equal to op. +func (d *decodeState) scanWhile(op int) { + s, data, i := &d.scan, d.data, d.off + for i < len(data) { + newOp := s.step(s, data[i]) + i++ + if newOp != op { + d.opcode = newOp + d.off = i + return + } + } + + d.off = len(data) + 1 // mark processed EOF with len+1 + d.opcode = d.scan.eof() +} + +func (d *decodeState) value() (tengo.Object, error) { + switch d.opcode { + default: + panic(phasePanicMsg) + case scanBeginArray: + o, err := d.array() + if err != nil { + return nil, err + } + d.scanNext() + return o, nil + case scanBeginObject: + o, err := d.object() + if err != nil { + return nil, err + } + d.scanNext() + return o, nil + case scanBeginLiteral: + return d.literal() + } +} + +func (d *decodeState) array() (tengo.Object, error) { + var arr []tengo.Object + for { + // Look ahead for ] - can only happen on first iteration. + d.scanWhile(scanSkipSpace) + if d.opcode == scanEndArray { + break + } + o, err := d.value() + if err != nil { + return nil, err + } + arr = append(arr, o) + + // Next token must be , or ]. + if d.opcode == scanSkipSpace { + d.scanWhile(scanSkipSpace) + } + if d.opcode == scanEndArray { + break + } + if d.opcode != scanArrayValue { + panic(phasePanicMsg) + } + } + return &tengo.Array{Value: arr}, nil +} + +func (d *decodeState) object() (tengo.Object, error) { + m := make(map[string]tengo.Object) + for { + // Read opening " of string key or closing }. + d.scanWhile(scanSkipSpace) + if d.opcode == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if d.opcode != scanBeginLiteral { + panic(phasePanicMsg) + } + + // Read string key. + start := d.readIndex() + d.scanWhile(scanContinue) + item := d.data[start:d.readIndex()] + key, ok := unquote(item) + if !ok { + panic(phasePanicMsg) + } + + // Read : before value. + if d.opcode == scanSkipSpace { + d.scanWhile(scanSkipSpace) + } + if d.opcode != scanObjectKey { + panic(phasePanicMsg) + } + d.scanWhile(scanSkipSpace) + + // Read value. + o, err := d.value() + if err != nil { + return nil, err + } + + m[key] = o + + // Next token must be , or }. + if d.opcode == scanSkipSpace { + d.scanWhile(scanSkipSpace) + } + if d.opcode == scanEndObject { + break + } + if d.opcode != scanObjectValue { + panic(phasePanicMsg) + } + } + return &tengo.Map{Value: m}, nil +} + +func (d *decodeState) literal() (tengo.Object, error) { + // All bytes inside literal return scanContinue op code. + start := d.readIndex() + d.scanWhile(scanContinue) + + item := d.data[start:d.readIndex()] + + switch c := item[0]; c { + case 'n': // null + return tengo.UndefinedValue, nil + + case 't', 'f': // true, false + if c == 't' { + return tengo.TrueValue, nil + } + return tengo.FalseValue, nil + + case '"': // string + s, ok := unquote(item) + if !ok { + panic(phasePanicMsg) + } + return &tengo.String{Value: s}, nil + + default: // number + if c != '-' && (c < '0' || c > '9') { + panic(phasePanicMsg) + } + n, _ := strconv.ParseFloat(string(item), 10) + return &tengo.Float{Value: n}, nil + } +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) rune { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + var r rune + for _, c := range s[2:6] { + switch { + case '0' <= c && c <= '9': + c = c - '0' + case 'a' <= c && c <= 'f': + c = c - 'a' + 10 + case 'A' <= c && c <= 'F': + c = c - 'A' + 10 + default: + return -1 + } + r = r*16 + rune(c) + } + return r +} + +// unquote converts a quoted JSON string literal s into an actual string t. +// The rules are different than for Go, so cannot use strconv.Unquote. +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) + t = string(s) + return +} + +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + // Check for unusual characters. If there are none, then no unquoting is + // needed, so return a slice of the original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rr, size := utf8.DecodeRune(s[r:]) + if rr == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + // Out of room? Can only happen if s is full of + // malformed UTF-8 and we're replacing each + // byte with RuneError. + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rr := getu4(s[r:]) + if rr < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rr) { + rr1 := getu4(s[r:]) + dec := utf16.DecodeRune(rr, rr1) + if dec != unicode.ReplacementChar { + // A valid pair; consume. + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + // Invalid surrogate; fall back to replacement rune. + rr = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rr) + } + // Quote, control characters are invalid. + case c == '"', c < ' ': + return + // ASCII + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + // Coerce to well-formed UTF-8. + default: + rr, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rr) + } + } + return b[0:w], true +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/json/encode.go b/vendor/github.com/d5/tengo/v2/stdlib/json/encode.go new file mode 100644 index 00000000..ab7ca6ff --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/json/encode.go @@ -0,0 +1,146 @@ +// A modified version of Go's JSON implementation. + +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package json + +import ( + "encoding/base64" + "errors" + "math" + "strconv" + + "github.com/d5/tengo/v2" +) + +// Encode returns the JSON encoding of the object. +func Encode(o tengo.Object) ([]byte, error) { + var b []byte + + switch o := o.(type) { + case *tengo.Array: + b = append(b, '[') + len1 := len(o.Value) - 1 + for idx, elem := range o.Value { + eb, err := Encode(elem) + if err != nil { + return nil, err + } + b = append(b, eb...) + if idx < len1 { + b = append(b, ',') + } + } + b = append(b, ']') + case *tengo.ImmutableArray: + b = append(b, '[') + len1 := len(o.Value) - 1 + for idx, elem := range o.Value { + eb, err := Encode(elem) + if err != nil { + return nil, err + } + b = append(b, eb...) + if idx < len1 { + b = append(b, ',') + } + } + b = append(b, ']') + case *tengo.Map: + b = append(b, '{') + len1 := len(o.Value) - 1 + idx := 0 + for key, value := range o.Value { + b = strconv.AppendQuote(b, key) + b = append(b, ':') + eb, err := Encode(value) + if err != nil { + return nil, err + } + b = append(b, eb...) + if idx < len1 { + b = append(b, ',') + } + idx++ + } + b = append(b, '}') + case *tengo.ImmutableMap: + b = append(b, '{') + len1 := len(o.Value) - 1 + idx := 0 + for key, value := range o.Value { + b = strconv.AppendQuote(b, key) + b = append(b, ':') + eb, err := Encode(value) + if err != nil { + return nil, err + } + b = append(b, eb...) + if idx < len1 { + b = append(b, ',') + } + idx++ + } + b = append(b, '}') + case *tengo.Bool: + if o.IsFalsy() { + b = strconv.AppendBool(b, false) + } else { + b = strconv.AppendBool(b, true) + } + case *tengo.Bytes: + b = append(b, '"') + encodedLen := base64.StdEncoding.EncodedLen(len(o.Value)) + dst := make([]byte, encodedLen) + base64.StdEncoding.Encode(dst, o.Value) + b = append(b, dst...) + b = append(b, '"') + case *tengo.Char: + b = strconv.AppendInt(b, int64(o.Value), 10) + case *tengo.Float: + var y []byte + + f := o.Value + if math.IsInf(f, 0) || math.IsNaN(f) { + return nil, errors.New("unsupported float value") + } + + // Convert as if by ES6 number to string conversion. + // This matches most other JSON generators. + abs := math.Abs(f) + fmt := byte('f') + if abs != 0 { + if abs < 1e-6 || abs >= 1e21 { + fmt = 'e' + } + } + y = strconv.AppendFloat(y, f, fmt, -1, 64) + if fmt == 'e' { + // clean up e-09 to e-9 + n := len(y) + if n >= 4 && y[n-4] == 'e' && y[n-3] == '-' && y[n-2] == '0' { + y[n-2] = y[n-1] + y = y[:n-1] + } + } + + b = append(b, y...) + case *tengo.Int: + b = strconv.AppendInt(b, o.Value, 10) + case *tengo.String: + b = strconv.AppendQuote(b, o.Value) + case *tengo.Time: + y, err := o.Value.MarshalJSON() + if err != nil { + return nil, err + } + b = append(b, y...) + case *tengo.Undefined: + b = append(b, "null"...) + default: + // unknown type: ignore + } + return b, nil +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/json/scanner.go b/vendor/github.com/d5/tengo/v2/stdlib/json/scanner.go new file mode 100644 index 00000000..aed15cf5 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/json/scanner.go @@ -0,0 +1,562 @@ +// A modified version of Go's JSON implementation. + +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package json + +import "strconv" + +func checkValid(data []byte, scan *scanner) error { + scan.reset() + for _, c := range data { + scan.bytes++ + if scan.step(scan, c) == scanError { + return scan.err + } + } + if scan.eof() == scanError { + return scan.err + } + return nil +} + +// A SyntaxError is a description of a JSON syntax error. +type SyntaxError struct { + msg string // description of error + Offset int64 // error occurred after reading Offset bytes +} + +func (e *SyntaxError) Error() string { return e.msg } + +// A scanner is a JSON scanning state machine. +// Callers call scan.reset() and then pass bytes in one at a time +// by calling scan.step(&scan, c) for each byte. +// The return value, referred to as an opcode, tells the +// caller about significant parsing events like beginning +// and ending literals, objects, and arrays, so that the +// caller can follow along if it wishes. +// The return value scanEnd indicates that a single top-level +// JSON value has been completed, *before* the byte that +// just got passed in. (The indication must be delayed in order +// to recognize the end of numbers: is 123 a whole value or +// the beginning of 12345e+6?). +type scanner struct { + // The step is a func to be called to execute the next transition. + // Also tried using an integer constant and a single func + // with a switch, but using the func directly was 10% faster + // on a 64-bit Mac Mini, and it's nicer to read. + step func(*scanner, byte) int + + // Reached end of top-level value. + endTop bool + + // Stack of what we're in the middle of - array values, object keys, object values. + parseState []int + + // Error that happened, if any. + err error + + // total bytes consumed, updated by decoder.Decode + bytes int64 +} + +// These values are returned by the state transition functions +// assigned to scanner.state and the method scanner.eof. +// They give details about the current state of the scan that +// callers might be interested to know about. +// It is okay to ignore the return value of any particular +// call to scanner.state: if one call returns scanError, +// every subsequent call will return scanError too. +const ( + // Continue. + scanContinue = iota // uninteresting byte + scanBeginLiteral // end implied by next result != scanContinue + scanBeginObject // begin object + scanObjectKey // just finished object key (string) + scanObjectValue // just finished non-last object value + scanEndObject // end object (implies scanObjectValue if possible) + scanBeginArray // begin array + scanArrayValue // just finished array value + scanEndArray // end array (implies scanArrayValue if possible) + scanSkipSpace // space byte; can skip; known to be last "continue" result + + // Stop. + scanEnd // top-level value ended *before* this byte; known to be first "stop" result + scanError // hit an error, scanner.err. +) + +// These values are stored in the parseState stack. +// They give the current state of a composite value +// being scanned. If the parser is inside a nested value +// the parseState describes the nested state, outermost at entry 0. +const ( + parseObjectKey = iota // parsing object key (before colon) + parseObjectValue // parsing object value (after colon) + parseArrayValue // parsing array value +) + +// reset prepares the scanner for use. +// It must be called before calling s.step. +func (s *scanner) reset() { + s.step = stateBeginValue + s.parseState = s.parseState[0:0] + s.err = nil + s.endTop = false +} + +// eof tells the scanner that the end of input has been reached. +// It returns a scan status just as s.step does. +func (s *scanner) eof() int { + if s.err != nil { + return scanError + } + if s.endTop { + return scanEnd + } + s.step(s, ' ') + if s.endTop { + return scanEnd + } + if s.err == nil { + s.err = &SyntaxError{"unexpected end of JSON input", s.bytes} + } + return scanError +} + +// pushParseState pushes a new parse state p onto the parse stack. +func (s *scanner) pushParseState(p int) { + s.parseState = append(s.parseState, p) +} + +// popParseState pops a parse state (already obtained) off the stack +// and updates s.step accordingly. +func (s *scanner) popParseState() { + n := len(s.parseState) - 1 + s.parseState = s.parseState[0:n] + if n == 0 { + s.step = stateEndTop + s.endTop = true + } else { + s.step = stateEndValue + } +} + +func isSpace(c byte) bool { + return c == ' ' || c == '\t' || c == '\r' || c == '\n' +} + +// stateBeginValueOrEmpty is the state after reading `[`. +func stateBeginValueOrEmpty(s *scanner, c byte) int { + if c <= ' ' && isSpace(c) { + return scanSkipSpace + } + if c == ']' { + return stateEndValue(s, c) + } + return stateBeginValue(s, c) +} + +// stateBeginValue is the state at the beginning of the input. +func stateBeginValue(s *scanner, c byte) int { + if c <= ' ' && isSpace(c) { + return scanSkipSpace + } + switch c { + case '{': + s.step = stateBeginStringOrEmpty + s.pushParseState(parseObjectKey) + return scanBeginObject + case '[': + s.step = stateBeginValueOrEmpty + s.pushParseState(parseArrayValue) + return scanBeginArray + case '"': + s.step = stateInString + return scanBeginLiteral + case '-': + s.step = stateNeg + return scanBeginLiteral + case '0': // beginning of 0.123 + s.step = state0 + return scanBeginLiteral + case 't': // beginning of true + s.step = stateT + return scanBeginLiteral + case 'f': // beginning of false + s.step = stateF + return scanBeginLiteral + case 'n': // beginning of null + s.step = stateN + return scanBeginLiteral + } + if '1' <= c && c <= '9' { // beginning of 1234.5 + s.step = state1 + return scanBeginLiteral + } + return s.error(c, "looking for beginning of value") +} + +// stateBeginStringOrEmpty is the state after reading `{`. +func stateBeginStringOrEmpty(s *scanner, c byte) int { + if c <= ' ' && isSpace(c) { + return scanSkipSpace + } + if c == '}' { + n := len(s.parseState) + s.parseState[n-1] = parseObjectValue + return stateEndValue(s, c) + } + return stateBeginString(s, c) +} + +// stateBeginString is the state after reading `{"key": value,`. +func stateBeginString(s *scanner, c byte) int { + if c <= ' ' && isSpace(c) { + return scanSkipSpace + } + if c == '"' { + s.step = stateInString + return scanBeginLiteral + } + return s.error(c, "looking for beginning of object key string") +} + +// stateEndValue is the state after completing a value, +// such as after reading `{}` or `true` or `["x"`. +func stateEndValue(s *scanner, c byte) int { + n := len(s.parseState) + if n == 0 { + // Completed top-level before the current byte. + s.step = stateEndTop + s.endTop = true + return stateEndTop(s, c) + } + if c <= ' ' && isSpace(c) { + s.step = stateEndValue + return scanSkipSpace + } + ps := s.parseState[n-1] + switch ps { + case parseObjectKey: + if c == ':' { + s.parseState[n-1] = parseObjectValue + s.step = stateBeginValue + return scanObjectKey + } + return s.error(c, "after object key") + case parseObjectValue: + if c == ',' { + s.parseState[n-1] = parseObjectKey + s.step = stateBeginString + return scanObjectValue + } + if c == '}' { + s.popParseState() + return scanEndObject + } + return s.error(c, "after object key:value pair") + case parseArrayValue: + if c == ',' { + s.step = stateBeginValue + return scanArrayValue + } + if c == ']' { + s.popParseState() + return scanEndArray + } + return s.error(c, "after array element") + } + return s.error(c, "") +} + +// stateEndTop is the state after finishing the top-level value, +// such as after reading `{}` or `[1,2,3]`. +// Only space characters should be seen now. +func stateEndTop(s *scanner, c byte) int { + if !isSpace(c) { + // Complain about non-space byte on next call. + s.error(c, "after top-level value") + } + return scanEnd +} + +// stateInString is the state after reading `"`. +func stateInString(s *scanner, c byte) int { + if c == '"' { + s.step = stateEndValue + return scanContinue + } + if c == '\\' { + s.step = stateInStringEsc + return scanContinue + } + if c < 0x20 { + return s.error(c, "in string literal") + } + return scanContinue +} + +// stateInStringEsc is the state after reading `"\` during a quoted string. +func stateInStringEsc(s *scanner, c byte) int { + switch c { + case 'b', 'f', 'n', 'r', 't', '\\', '/', '"': + s.step = stateInString + return scanContinue + case 'u': + s.step = stateInStringEscU + return scanContinue + } + return s.error(c, "in string escape code") +} + +// stateInStringEscU is the state after reading `"\u` during a quoted string. +func stateInStringEscU(s *scanner, c byte) int { + if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { + s.step = stateInStringEscU1 + return scanContinue + } + // numbers + return s.error(c, "in \\u hexadecimal character escape") +} + +// stateInStringEscU1 is the state after reading `"\u1` during a quoted string. +func stateInStringEscU1(s *scanner, c byte) int { + if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { + s.step = stateInStringEscU12 + return scanContinue + } + // numbers + return s.error(c, "in \\u hexadecimal character escape") +} + +// stateInStringEscU12 is the state after reading `"\u12` during a quoted string. +func stateInStringEscU12(s *scanner, c byte) int { + if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { + s.step = stateInStringEscU123 + return scanContinue + } + // numbers + return s.error(c, "in \\u hexadecimal character escape") +} + +// stateInStringEscU123 is the state after reading `"\u123` during a quoted string. +func stateInStringEscU123(s *scanner, c byte) int { + if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { + s.step = stateInString + return scanContinue + } + // numbers + return s.error(c, "in \\u hexadecimal character escape") +} + +// stateNeg is the state after reading `-` during a number. +func stateNeg(s *scanner, c byte) int { + if c == '0' { + s.step = state0 + return scanContinue + } + if '1' <= c && c <= '9' { + s.step = state1 + return scanContinue + } + return s.error(c, "in numeric literal") +} + +// state1 is the state after reading a non-zero integer during a number, +// such as after reading `1` or `100` but not `0`. +func state1(s *scanner, c byte) int { + if '0' <= c && c <= '9' { + s.step = state1 + return scanContinue + } + return state0(s, c) +} + +// state0 is the state after reading `0` during a number. +func state0(s *scanner, c byte) int { + if c == '.' { + s.step = stateDot + return scanContinue + } + if c == 'e' || c == 'E' { + s.step = stateE + return scanContinue + } + return stateEndValue(s, c) +} + +// stateDot is the state after reading the integer and decimal point in a number, +// such as after reading `1.`. +func stateDot(s *scanner, c byte) int { + if '0' <= c && c <= '9' { + s.step = stateDot0 + return scanContinue + } + return s.error(c, "after decimal point in numeric literal") +} + +// stateDot0 is the state after reading the integer, decimal point, and subsequent +// digits of a number, such as after reading `3.14`. +func stateDot0(s *scanner, c byte) int { + if '0' <= c && c <= '9' { + return scanContinue + } + if c == 'e' || c == 'E' { + s.step = stateE + return scanContinue + } + return stateEndValue(s, c) +} + +// stateE is the state after reading the mantissa and e in a number, +// such as after reading `314e` or `0.314e`. +func stateE(s *scanner, c byte) int { + if c == '+' || c == '-' { + s.step = stateESign + return scanContinue + } + return stateESign(s, c) +} + +// stateESign is the state after reading the mantissa, e, and sign in a number, +// such as after reading `314e-` or `0.314e+`. +func stateESign(s *scanner, c byte) int { + if '0' <= c && c <= '9' { + s.step = stateE0 + return scanContinue + } + return s.error(c, "in exponent of numeric literal") +} + +// stateE0 is the state after reading the mantissa, e, optional sign, +// and at least one digit of the exponent in a number, +// such as after reading `314e-2` or `0.314e+1` or `3.14e0`. +func stateE0(s *scanner, c byte) int { + if '0' <= c && c <= '9' { + return scanContinue + } + return stateEndValue(s, c) +} + +// stateT is the state after reading `t`. +func stateT(s *scanner, c byte) int { + if c == 'r' { + s.step = stateTr + return scanContinue + } + return s.error(c, "in literal true (expecting 'r')") +} + +// stateTr is the state after reading `tr`. +func stateTr(s *scanner, c byte) int { + if c == 'u' { + s.step = stateTru + return scanContinue + } + return s.error(c, "in literal true (expecting 'u')") +} + +// stateTru is the state after reading `tru`. +func stateTru(s *scanner, c byte) int { + if c == 'e' { + s.step = stateEndValue + return scanContinue + } + return s.error(c, "in literal true (expecting 'e')") +} + +// stateF is the state after reading `f`. +func stateF(s *scanner, c byte) int { + if c == 'a' { + s.step = stateFa + return scanContinue + } + return s.error(c, "in literal false (expecting 'a')") +} + +// stateFa is the state after reading `fa`. +func stateFa(s *scanner, c byte) int { + if c == 'l' { + s.step = stateFal + return scanContinue + } + return s.error(c, "in literal false (expecting 'l')") +} + +// stateFal is the state after reading `fal`. +func stateFal(s *scanner, c byte) int { + if c == 's' { + s.step = stateFals + return scanContinue + } + return s.error(c, "in literal false (expecting 's')") +} + +// stateFals is the state after reading `fals`. +func stateFals(s *scanner, c byte) int { + if c == 'e' { + s.step = stateEndValue + return scanContinue + } + return s.error(c, "in literal false (expecting 'e')") +} + +// stateN is the state after reading `n`. +func stateN(s *scanner, c byte) int { + if c == 'u' { + s.step = stateNu + return scanContinue + } + return s.error(c, "in literal null (expecting 'u')") +} + +// stateNu is the state after reading `nu`. +func stateNu(s *scanner, c byte) int { + if c == 'l' { + s.step = stateNul + return scanContinue + } + return s.error(c, "in literal null (expecting 'l')") +} + +// stateNul is the state after reading `nul`. +func stateNul(s *scanner, c byte) int { + if c == 'l' { + s.step = stateEndValue + return scanContinue + } + return s.error(c, "in literal null (expecting 'l')") +} + +// stateError is the state after reaching a syntax error, +// such as after reading `[1}` or `5.1.2`. +func stateError(_ *scanner, _ byte) int { + return scanError +} + +// error records an error and switches to the error state. +func (s *scanner) error(c byte, context string) int { + s.step = stateError + s.err = &SyntaxError{ + msg: "invalid character " + quoteChar(c) + " " + context, + Offset: s.bytes, + } + return scanError +} + +// quoteChar formats c as a quoted character literal +func quoteChar(c byte) string { + // special cases - different from quoted strings + if c == '\'' { + return `'\''` + } + if c == '"' { + return `'"'` + } + + // use quoted string with different quotation marks + s := strconv.Quote(string(c)) + return "'" + s[1:len(s)-1] + "'" +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/math.go b/vendor/github.com/d5/tengo/v2/stdlib/math.go new file mode 100644 index 00000000..633ea09f --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/math.go @@ -0,0 +1,233 @@ +package stdlib + +import ( + "math" + + "github.com/d5/tengo/v2" +) + +var mathModule = map[string]tengo.Object{ + "e": &tengo.Float{Value: math.E}, + "pi": &tengo.Float{Value: math.Pi}, + "phi": &tengo.Float{Value: math.Phi}, + "sqrt2": &tengo.Float{Value: math.Sqrt2}, + "sqrtE": &tengo.Float{Value: math.SqrtE}, + "sqrtPi": &tengo.Float{Value: math.SqrtPi}, + "sqrtPhi": &tengo.Float{Value: math.SqrtPhi}, + "ln2": &tengo.Float{Value: math.Ln2}, + "log2E": &tengo.Float{Value: math.Log2E}, + "ln10": &tengo.Float{Value: math.Ln10}, + "log10E": &tengo.Float{Value: math.Log10E}, + "abs": &tengo.UserFunction{ + Name: "abs", + Value: FuncAFRF(math.Abs), + }, + "acos": &tengo.UserFunction{ + Name: "acos", + Value: FuncAFRF(math.Acos), + }, + "acosh": &tengo.UserFunction{ + Name: "acosh", + Value: FuncAFRF(math.Acosh), + }, + "asin": &tengo.UserFunction{ + Name: "asin", + Value: FuncAFRF(math.Asin), + }, + "asinh": &tengo.UserFunction{ + Name: "asinh", + Value: FuncAFRF(math.Asinh), + }, + "atan": &tengo.UserFunction{ + Name: "atan", + Value: FuncAFRF(math.Atan), + }, + "atan2": &tengo.UserFunction{ + Name: "atan2", + Value: FuncAFFRF(math.Atan2), + }, + "atanh": &tengo.UserFunction{ + Name: "atanh", + Value: FuncAFRF(math.Atanh), + }, + "cbrt": &tengo.UserFunction{ + Name: "cbrt", + Value: FuncAFRF(math.Cbrt), + }, + "ceil": &tengo.UserFunction{ + Name: "ceil", + Value: FuncAFRF(math.Ceil), + }, + "copysign": &tengo.UserFunction{ + Name: "copysign", + Value: FuncAFFRF(math.Copysign), + }, + "cos": &tengo.UserFunction{ + Name: "cos", + Value: FuncAFRF(math.Cos), + }, + "cosh": &tengo.UserFunction{ + Name: "cosh", + Value: FuncAFRF(math.Cosh), + }, + "dim": &tengo.UserFunction{ + Name: "dim", + Value: FuncAFFRF(math.Dim), + }, + "erf": &tengo.UserFunction{ + Name: "erf", + Value: FuncAFRF(math.Erf), + }, + "erfc": &tengo.UserFunction{ + Name: "erfc", + Value: FuncAFRF(math.Erfc), + }, + "exp": &tengo.UserFunction{ + Name: "exp", + Value: FuncAFRF(math.Exp), + }, + "exp2": &tengo.UserFunction{ + Name: "exp2", + Value: FuncAFRF(math.Exp2), + }, + "expm1": &tengo.UserFunction{ + Name: "expm1", + Value: FuncAFRF(math.Expm1), + }, + "floor": &tengo.UserFunction{ + Name: "floor", + Value: FuncAFRF(math.Floor), + }, + "gamma": &tengo.UserFunction{ + Name: "gamma", + Value: FuncAFRF(math.Gamma), + }, + "hypot": &tengo.UserFunction{ + Name: "hypot", + Value: FuncAFFRF(math.Hypot), + }, + "ilogb": &tengo.UserFunction{ + Name: "ilogb", + Value: FuncAFRI(math.Ilogb), + }, + "inf": &tengo.UserFunction{ + Name: "inf", + Value: FuncAIRF(math.Inf), + }, + "is_inf": &tengo.UserFunction{ + Name: "is_inf", + Value: FuncAFIRB(math.IsInf), + }, + "is_nan": &tengo.UserFunction{ + Name: "is_nan", + Value: FuncAFRB(math.IsNaN), + }, + "j0": &tengo.UserFunction{ + Name: "j0", + Value: FuncAFRF(math.J0), + }, + "j1": &tengo.UserFunction{ + Name: "j1", + Value: FuncAFRF(math.J1), + }, + "jn": &tengo.UserFunction{ + Name: "jn", + Value: FuncAIFRF(math.Jn), + }, + "ldexp": &tengo.UserFunction{ + Name: "ldexp", + Value: FuncAFIRF(math.Ldexp), + }, + "log": &tengo.UserFunction{ + Name: "log", + Value: FuncAFRF(math.Log), + }, + "log10": &tengo.UserFunction{ + Name: "log10", + Value: FuncAFRF(math.Log10), + }, + "log1p": &tengo.UserFunction{ + Name: "log1p", + Value: FuncAFRF(math.Log1p), + }, + "log2": &tengo.UserFunction{ + Name: "log2", + Value: FuncAFRF(math.Log2), + }, + "logb": &tengo.UserFunction{ + Name: "logb", + Value: FuncAFRF(math.Logb), + }, + "max": &tengo.UserFunction{ + Name: "max", + Value: FuncAFFRF(math.Max), + }, + "min": &tengo.UserFunction{ + Name: "min", + Value: FuncAFFRF(math.Min), + }, + "mod": &tengo.UserFunction{ + Name: "mod", + Value: FuncAFFRF(math.Mod), + }, + "nan": &tengo.UserFunction{ + Name: "nan", + Value: FuncARF(math.NaN), + }, + "nextafter": &tengo.UserFunction{ + Name: "nextafter", + Value: FuncAFFRF(math.Nextafter), + }, + "pow": &tengo.UserFunction{ + Name: "pow", + Value: FuncAFFRF(math.Pow), + }, + "pow10": &tengo.UserFunction{ + Name: "pow10", + Value: FuncAIRF(math.Pow10), + }, + "remainder": &tengo.UserFunction{ + Name: "remainder", + Value: FuncAFFRF(math.Remainder), + }, + "signbit": &tengo.UserFunction{ + Name: "signbit", + Value: FuncAFRB(math.Signbit), + }, + "sin": &tengo.UserFunction{ + Name: "sin", + Value: FuncAFRF(math.Sin), + }, + "sinh": &tengo.UserFunction{ + Name: "sinh", + Value: FuncAFRF(math.Sinh), + }, + "sqrt": &tengo.UserFunction{ + Name: "sqrt", + Value: FuncAFRF(math.Sqrt), + }, + "tan": &tengo.UserFunction{ + Name: "tan", + Value: FuncAFRF(math.Tan), + }, + "tanh": &tengo.UserFunction{ + Name: "tanh", + Value: FuncAFRF(math.Tanh), + }, + "trunc": &tengo.UserFunction{ + Name: "trunc", + Value: FuncAFRF(math.Trunc), + }, + "y0": &tengo.UserFunction{ + Name: "y0", + Value: FuncAFRF(math.Y0), + }, + "y1": &tengo.UserFunction{ + Name: "y1", + Value: FuncAFRF(math.Y1), + }, + "yn": &tengo.UserFunction{ + Name: "yn", + Value: FuncAIFRF(math.Yn), + }, +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/os.go b/vendor/github.com/d5/tengo/v2/stdlib/os.go new file mode 100644 index 00000000..576bc94b --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/os.go @@ -0,0 +1,564 @@ +package stdlib + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + + "github.com/d5/tengo/v2" +) + +var osModule = map[string]tengo.Object{ + "o_rdonly": &tengo.Int{Value: int64(os.O_RDONLY)}, + "o_wronly": &tengo.Int{Value: int64(os.O_WRONLY)}, + "o_rdwr": &tengo.Int{Value: int64(os.O_RDWR)}, + "o_append": &tengo.Int{Value: int64(os.O_APPEND)}, + "o_create": &tengo.Int{Value: int64(os.O_CREATE)}, + "o_excl": &tengo.Int{Value: int64(os.O_EXCL)}, + "o_sync": &tengo.Int{Value: int64(os.O_SYNC)}, + "o_trunc": &tengo.Int{Value: int64(os.O_TRUNC)}, + "mode_dir": &tengo.Int{Value: int64(os.ModeDir)}, + "mode_append": &tengo.Int{Value: int64(os.ModeAppend)}, + "mode_exclusive": &tengo.Int{Value: int64(os.ModeExclusive)}, + "mode_temporary": &tengo.Int{Value: int64(os.ModeTemporary)}, + "mode_symlink": &tengo.Int{Value: int64(os.ModeSymlink)}, + "mode_device": &tengo.Int{Value: int64(os.ModeDevice)}, + "mode_named_pipe": &tengo.Int{Value: int64(os.ModeNamedPipe)}, + "mode_socket": &tengo.Int{Value: int64(os.ModeSocket)}, + "mode_setuid": &tengo.Int{Value: int64(os.ModeSetuid)}, + "mode_setgui": &tengo.Int{Value: int64(os.ModeSetgid)}, + "mode_char_device": &tengo.Int{Value: int64(os.ModeCharDevice)}, + "mode_sticky": &tengo.Int{Value: int64(os.ModeSticky)}, + "mode_type": &tengo.Int{Value: int64(os.ModeType)}, + "mode_perm": &tengo.Int{Value: int64(os.ModePerm)}, + "path_separator": &tengo.Char{Value: os.PathSeparator}, + "path_list_separator": &tengo.Char{Value: os.PathListSeparator}, + "dev_null": &tengo.String{Value: os.DevNull}, + "seek_set": &tengo.Int{Value: int64(io.SeekStart)}, + "seek_cur": &tengo.Int{Value: int64(io.SeekCurrent)}, + "seek_end": &tengo.Int{Value: int64(io.SeekEnd)}, + "args": &tengo.UserFunction{ + Name: "args", + Value: osArgs, + }, // args() => array(string) + "chdir": &tengo.UserFunction{ + Name: "chdir", + Value: FuncASRE(os.Chdir), + }, // chdir(dir string) => error + "chmod": osFuncASFmRE("chmod", os.Chmod), // chmod(name string, mode int) => error + "chown": &tengo.UserFunction{ + Name: "chown", + Value: FuncASIIRE(os.Chown), + }, // chown(name string, uid int, gid int) => error + "clearenv": &tengo.UserFunction{ + Name: "clearenv", + Value: FuncAR(os.Clearenv), + }, // clearenv() + "environ": &tengo.UserFunction{ + Name: "environ", + Value: FuncARSs(os.Environ), + }, // environ() => array(string) + "exit": &tengo.UserFunction{ + Name: "exit", + Value: FuncAIR(os.Exit), + }, // exit(code int) + "expand_env": &tengo.UserFunction{ + Name: "expand_env", + Value: osExpandEnv, + }, // expand_env(s string) => string + "getegid": &tengo.UserFunction{ + Name: "getegid", + Value: FuncARI(os.Getegid), + }, // getegid() => int + "getenv": &tengo.UserFunction{ + Name: "getenv", + Value: FuncASRS(os.Getenv), + }, // getenv(s string) => string + "geteuid": &tengo.UserFunction{ + Name: "geteuid", + Value: FuncARI(os.Geteuid), + }, // geteuid() => int + "getgid": &tengo.UserFunction{ + Name: "getgid", + Value: FuncARI(os.Getgid), + }, // getgid() => int + "getgroups": &tengo.UserFunction{ + Name: "getgroups", + Value: FuncARIsE(os.Getgroups), + }, // getgroups() => array(string)/error + "getpagesize": &tengo.UserFunction{ + Name: "getpagesize", + Value: FuncARI(os.Getpagesize), + }, // getpagesize() => int + "getpid": &tengo.UserFunction{ + Name: "getpid", + Value: FuncARI(os.Getpid), + }, // getpid() => int + "getppid": &tengo.UserFunction{ + Name: "getppid", + Value: FuncARI(os.Getppid), + }, // getppid() => int + "getuid": &tengo.UserFunction{ + Name: "getuid", + Value: FuncARI(os.Getuid), + }, // getuid() => int + "getwd": &tengo.UserFunction{ + Name: "getwd", + Value: FuncARSE(os.Getwd), + }, // getwd() => string/error + "hostname": &tengo.UserFunction{ + Name: "hostname", + Value: FuncARSE(os.Hostname), + }, // hostname() => string/error + "lchown": &tengo.UserFunction{ + Name: "lchown", + Value: FuncASIIRE(os.Lchown), + }, // lchown(name string, uid int, gid int) => error + "link": &tengo.UserFunction{ + Name: "link", + Value: FuncASSRE(os.Link), + }, // link(oldname string, newname string) => error + "lookup_env": &tengo.UserFunction{ + Name: "lookup_env", + Value: osLookupEnv, + }, // lookup_env(key string) => string/false + "mkdir": osFuncASFmRE("mkdir", os.Mkdir), // mkdir(name string, perm int) => error + "mkdir_all": osFuncASFmRE("mkdir_all", os.MkdirAll), // mkdir_all(name string, perm int) => error + "readlink": &tengo.UserFunction{ + Name: "readlink", + Value: FuncASRSE(os.Readlink), + }, // readlink(name string) => string/error + "remove": &tengo.UserFunction{ + Name: "remove", + Value: FuncASRE(os.Remove), + }, // remove(name string) => error + "remove_all": &tengo.UserFunction{ + Name: "remove_all", + Value: FuncASRE(os.RemoveAll), + }, // remove_all(name string) => error + "rename": &tengo.UserFunction{ + Name: "rename", + Value: FuncASSRE(os.Rename), + }, // rename(oldpath string, newpath string) => error + "setenv": &tengo.UserFunction{ + Name: "setenv", + Value: FuncASSRE(os.Setenv), + }, // setenv(key string, value string) => error + "symlink": &tengo.UserFunction{ + Name: "symlink", + Value: FuncASSRE(os.Symlink), + }, // symlink(oldname string newname string) => error + "temp_dir": &tengo.UserFunction{ + Name: "temp_dir", + Value: FuncARS(os.TempDir), + }, // temp_dir() => string + "truncate": &tengo.UserFunction{ + Name: "truncate", + Value: FuncASI64RE(os.Truncate), + }, // truncate(name string, size int) => error + "unsetenv": &tengo.UserFunction{ + Name: "unsetenv", + Value: FuncASRE(os.Unsetenv), + }, // unsetenv(key string) => error + "create": &tengo.UserFunction{ + Name: "create", + Value: osCreate, + }, // create(name string) => imap(file)/error + "open": &tengo.UserFunction{ + Name: "open", + Value: osOpen, + }, // open(name string) => imap(file)/error + "open_file": &tengo.UserFunction{ + Name: "open_file", + Value: osOpenFile, + }, // open_file(name string, flag int, perm int) => imap(file)/error + "find_process": &tengo.UserFunction{ + Name: "find_process", + Value: osFindProcess, + }, // find_process(pid int) => imap(process)/error + "start_process": &tengo.UserFunction{ + Name: "start_process", + Value: osStartProcess, + }, // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error + "exec_look_path": &tengo.UserFunction{ + Name: "exec_look_path", + Value: FuncASRSE(exec.LookPath), + }, // exec_look_path(file) => string/error + "exec": &tengo.UserFunction{ + Name: "exec", + Value: osExec, + }, // exec(name, args...) => command + "stat": &tengo.UserFunction{ + Name: "stat", + Value: osStat, + }, // stat(name) => imap(fileinfo)/error + "read_file": &tengo.UserFunction{ + Name: "read_file", + Value: osReadFile, + }, // readfile(name) => array(byte)/error +} + +func osReadFile(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + fname, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + bytes, err := ioutil.ReadFile(fname) + if err != nil { + return wrapError(err), nil + } + if len(bytes) > tengo.MaxBytesLen { + return nil, tengo.ErrBytesLimit + } + return &tengo.Bytes{Value: bytes}, nil +} + +func osStat(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + fname, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + stat, err := os.Stat(fname) + if err != nil { + return wrapError(err), nil + } + fstat := &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "name": &tengo.String{Value: stat.Name()}, + "mtime": &tengo.Time{Value: stat.ModTime()}, + "size": &tengo.Int{Value: stat.Size()}, + "mode": &tengo.Int{Value: int64(stat.Mode())}, + }, + } + if stat.IsDir() { + fstat.Value["directory"] = tengo.TrueValue + } else { + fstat.Value["directory"] = tengo.FalseValue + } + return fstat, nil +} + +func osCreate(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res, err := os.Create(s1) + if err != nil { + return wrapError(err), nil + } + return makeOSFile(res), nil +} + +func osOpen(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res, err := os.Open(s1) + if err != nil { + return wrapError(err), nil + } + return makeOSFile(res), nil +} + +func osOpenFile(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 3 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + i3, ok := tengo.ToInt(args[2]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + } + res, err := os.OpenFile(s1, i2, os.FileMode(i3)) + if err != nil { + return wrapError(err), nil + } + return makeOSFile(res), nil +} + +func osArgs(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + arr := &tengo.Array{} + for _, osArg := range os.Args { + if len(osArg) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: osArg}) + } + return arr, nil +} + +func osFuncASFmRE( + name string, + fn func(string, os.FileMode) error, +) *tengo.UserFunction { + return &tengo.UserFunction{ + Name: name, + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt64(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + return wrapError(fn(s1, os.FileMode(i2))), nil + }, + } +} + +func osLookupEnv(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res, ok := os.LookupEnv(s1) + if !ok { + return tengo.FalseValue, nil + } + if len(res) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: res}, nil +} + +func osExpandEnv(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + var vlen int + var failed bool + s := os.Expand(s1, func(k string) string { + if failed { + return "" + } + v := os.Getenv(k) + + // this does not count the other texts that are not being replaced + // but the code checks the final length at the end + vlen += len(v) + if vlen > tengo.MaxStringLen { + failed = true + return "" + } + return v + }) + if failed || len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil +} + +func osExec(args ...tengo.Object) (tengo.Object, error) { + if len(args) == 0 { + return nil, tengo.ErrWrongNumArguments + } + name, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + var execArgs []string + for idx, arg := range args[1:] { + execArg, ok := tengo.ToString(arg) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("args[%d]", idx), + Expected: "string(compatible)", + Found: args[1+idx].TypeName(), + } + } + execArgs = append(execArgs, execArg) + } + return makeOSExecCommand(exec.Command(name, execArgs...)), nil +} + +func osFindProcess(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + proc, err := os.FindProcess(i1) + if err != nil { + return wrapError(err), nil + } + return makeOSProcess(proc), nil +} + +func osStartProcess(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 4 { + return nil, tengo.ErrWrongNumArguments + } + name, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + var argv []string + var err error + switch arg1 := args[1].(type) { + case *tengo.Array: + argv, err = stringArray(arg1.Value, "second") + if err != nil { + return nil, err + } + case *tengo.ImmutableArray: + argv, err = stringArray(arg1.Value, "second") + if err != nil { + return nil, err + } + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "array", + Found: arg1.TypeName(), + } + } + + dir, ok := tengo.ToString(args[2]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + } + + var env []string + switch arg3 := args[3].(type) { + case *tengo.Array: + env, err = stringArray(arg3.Value, "fourth") + if err != nil { + return nil, err + } + case *tengo.ImmutableArray: + env, err = stringArray(arg3.Value, "fourth") + if err != nil { + return nil, err + } + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "fourth", + Expected: "array", + Found: arg3.TypeName(), + } + } + + proc, err := os.StartProcess(name, argv, &os.ProcAttr{ + Dir: dir, + Env: env, + }) + if err != nil { + return wrapError(err), nil + } + return makeOSProcess(proc), nil +} + +func stringArray(arr []tengo.Object, argName string) ([]string, error) { + var sarr []string + for idx, elem := range arr { + str, ok := elem.(*tengo.String) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("%s[%d]", argName, idx), + Expected: "string", + Found: elem.TypeName(), + } + } + sarr = append(sarr, str.Value) + } + return sarr, nil +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/os_exec.go b/vendor/github.com/d5/tengo/v2/stdlib/os_exec.go new file mode 100644 index 00000000..7ee5c1cd --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/os_exec.go @@ -0,0 +1,119 @@ +package stdlib + +import ( + "os/exec" + + "github.com/d5/tengo/v2" +) + +func makeOSExecCommand(cmd *exec.Cmd) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + // combined_output() => bytes/error + "combined_output": &tengo.UserFunction{ + Name: "combined_output", + Value: FuncARYE(cmd.CombinedOutput), + }, + // output() => bytes/error + "output": &tengo.UserFunction{ + Name: "output", + Value: FuncARYE(cmd.Output), + }, // + // run() => error + "run": &tengo.UserFunction{ + Name: "run", + Value: FuncARE(cmd.Run), + }, // + // start() => error + "start": &tengo.UserFunction{ + Name: "start", + Value: FuncARE(cmd.Start), + }, // + // wait() => error + "wait": &tengo.UserFunction{ + Name: "wait", + Value: FuncARE(cmd.Wait), + }, // + // set_path(path string) + "set_path": &tengo.UserFunction{ + Name: "set_path", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + cmd.Path = s1 + return tengo.UndefinedValue, nil + }, + }, + // set_dir(dir string) + "set_dir": &tengo.UserFunction{ + Name: "set_dir", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + cmd.Dir = s1 + return tengo.UndefinedValue, nil + }, + }, + // set_env(env array(string)) + "set_env": &tengo.UserFunction{ + Name: "set_env", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + var env []string + var err error + switch arg0 := args[0].(type) { + case *tengo.Array: + env, err = stringArray(arg0.Value, "first") + if err != nil { + return nil, err + } + case *tengo.ImmutableArray: + env, err = stringArray(arg0.Value, "first") + if err != nil { + return nil, err + } + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "array", + Found: arg0.TypeName(), + } + } + cmd.Env = env + return tengo.UndefinedValue, nil + }, + }, + // process() => imap(process) + "process": &tengo.UserFunction{ + Name: "process", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return makeOSProcess(cmd.Process), nil + }, + }, + }, + } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/os_file.go b/vendor/github.com/d5/tengo/v2/stdlib/os_file.go new file mode 100644 index 00000000..4f59b4c4 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/os_file.go @@ -0,0 +1,117 @@ +package stdlib + +import ( + "os" + + "github.com/d5/tengo/v2" +) + +func makeOSFile(file *os.File) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + // chdir() => true/error + "chdir": &tengo.UserFunction{ + Name: "chdir", + Value: FuncARE(file.Chdir), + }, // + // chown(uid int, gid int) => true/error + "chown": &tengo.UserFunction{ + Name: "chown", + Value: FuncAIIRE(file.Chown), + }, // + // close() => error + "close": &tengo.UserFunction{ + Name: "close", + Value: FuncARE(file.Close), + }, // + // name() => string + "name": &tengo.UserFunction{ + Name: "name", + Value: FuncARS(file.Name), + }, // + // readdirnames(n int) => array(string)/error + "readdirnames": &tengo.UserFunction{ + Name: "readdirnames", + Value: FuncAIRSsE(file.Readdirnames), + }, // + // sync() => error + "sync": &tengo.UserFunction{ + Name: "sync", + Value: FuncARE(file.Sync), + }, // + // write(bytes) => int/error + "write": &tengo.UserFunction{ + Name: "write", + Value: FuncAYRIE(file.Write), + }, // + // write(string) => int/error + "write_string": &tengo.UserFunction{ + Name: "write_string", + Value: FuncASRIE(file.WriteString), + }, // + // read(bytes) => int/error + "read": &tengo.UserFunction{ + Name: "read", + Value: FuncAYRIE(file.Read), + }, // + // chmod(mode int) => error + "chmod": &tengo.UserFunction{ + Name: "chmod", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + return wrapError(file.Chmod(os.FileMode(i1))), nil + }, + }, + // seek(offset int, whence int) => int/error + "seek": &tengo.UserFunction{ + Name: "seek", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + res, err := file.Seek(i1, i2) + if err != nil { + return wrapError(err), nil + } + return &tengo.Int{Value: res}, nil + }, + }, + // stat() => imap(fileinfo)/error + "stat": &tengo.UserFunction{ + Name: "stat", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return osStat(&tengo.String{Value: file.Name()}) + }, + }, + }, + } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/os_process.go b/vendor/github.com/d5/tengo/v2/stdlib/os_process.go new file mode 100644 index 00000000..7fcf27a4 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/os_process.go @@ -0,0 +1,76 @@ +package stdlib + +import ( + "os" + "syscall" + + "github.com/d5/tengo/v2" +) + +func makeOSProcessState(state *os.ProcessState) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "exited": &tengo.UserFunction{ + Name: "exited", + Value: FuncARB(state.Exited), + }, + "pid": &tengo.UserFunction{ + Name: "pid", + Value: FuncARI(state.Pid), + }, + "string": &tengo.UserFunction{ + Name: "string", + Value: FuncARS(state.String), + }, + "success": &tengo.UserFunction{ + Name: "success", + Value: FuncARB(state.Success), + }, + }, + } +} + +func makeOSProcess(proc *os.Process) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "kill": &tengo.UserFunction{ + Name: "kill", + Value: FuncARE(proc.Kill), + }, + "release": &tengo.UserFunction{ + Name: "release", + Value: FuncARE(proc.Release), + }, + "signal": &tengo.UserFunction{ + Name: "signal", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + return wrapError(proc.Signal(syscall.Signal(i1))), nil + }, + }, + "wait": &tengo.UserFunction{ + Name: "wait", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + state, err := proc.Wait() + if err != nil { + return wrapError(err), nil + } + return makeOSProcessState(state), nil + }, + }, + }, + } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/rand.go b/vendor/github.com/d5/tengo/v2/stdlib/rand.go new file mode 100644 index 00000000..5d21e1df --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/rand.go @@ -0,0 +1,138 @@ +package stdlib + +import ( + "math/rand" + + "github.com/d5/tengo/v2" +) + +var randModule = map[string]tengo.Object{ + "int": &tengo.UserFunction{ + Name: "int", + Value: FuncARI64(rand.Int63), + }, + "float": &tengo.UserFunction{ + Name: "float", + Value: FuncARF(rand.Float64), + }, + "intn": &tengo.UserFunction{ + Name: "intn", + Value: FuncAI64RI64(rand.Int63n), + }, + "exp_float": &tengo.UserFunction{ + Name: "exp_float", + Value: FuncARF(rand.ExpFloat64), + }, + "norm_float": &tengo.UserFunction{ + Name: "norm_float", + Value: FuncARF(rand.NormFloat64), + }, + "perm": &tengo.UserFunction{ + Name: "perm", + Value: FuncAIRIs(rand.Perm), + }, + "seed": &tengo.UserFunction{ + Name: "seed", + Value: FuncAI64R(rand.Seed), + }, + "read": &tengo.UserFunction{ + Name: "read", + Value: func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + y1, ok := args[0].(*tengo.Bytes) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes", + Found: args[0].TypeName(), + } + } + res, err := rand.Read(y1.Value) + if err != nil { + ret = wrapError(err) + return + } + return &tengo.Int{Value: int64(res)}, nil + }, + }, + "rand": &tengo.UserFunction{ + Name: "rand", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + src := rand.NewSource(i1) + return randRand(rand.New(src)), nil + }, + }, +} + +func randRand(r *rand.Rand) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "int": &tengo.UserFunction{ + Name: "int", + Value: FuncARI64(r.Int63), + }, + "float": &tengo.UserFunction{ + Name: "float", + Value: FuncARF(r.Float64), + }, + "intn": &tengo.UserFunction{ + Name: "intn", + Value: FuncAI64RI64(r.Int63n), + }, + "exp_float": &tengo.UserFunction{ + Name: "exp_float", + Value: FuncARF(r.ExpFloat64), + }, + "norm_float": &tengo.UserFunction{ + Name: "norm_float", + Value: FuncARF(r.NormFloat64), + }, + "perm": &tengo.UserFunction{ + Name: "perm", + Value: FuncAIRIs(r.Perm), + }, + "seed": &tengo.UserFunction{ + Name: "seed", + Value: FuncAI64R(r.Seed), + }, + "read": &tengo.UserFunction{ + Name: "read", + Value: func(args ...tengo.Object) ( + ret tengo.Object, + err error, + ) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + y1, ok := args[0].(*tengo.Bytes) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes", + Found: args[0].TypeName(), + } + } + res, err := r.Read(y1.Value) + if err != nil { + ret = wrapError(err) + return + } + return &tengo.Int{Value: int64(res)}, nil + }, + }, + }, + } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/source_modules.go b/vendor/github.com/d5/tengo/v2/stdlib/source_modules.go new file mode 100644 index 00000000..ca69d7d1 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/source_modules.go @@ -0,0 +1,8 @@ +// Code generated using gensrcmods.go; DO NOT EDIT. + +package stdlib + +// SourceModules are source type standard library modules. +var SourceModules = map[string]string{ + "enum": "is_enumerable := func(x) {\n return is_array(x) || is_map(x) || is_immutable_array(x) || is_immutable_map(x)\n}\n\nis_array_like := func(x) {\n return is_array(x) || is_immutable_array(x)\n}\n\nexport {\n // all returns true if the given function `fn` evaluates to a truthy value on\n // all of the items in `x`. It returns undefined if `x` is not enumerable.\n all: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if !fn(k, v) { return false }\n }\n\n return true\n },\n // any returns true if the given function `fn` evaluates to a truthy value on\n // any of the items in `x`. It returns undefined if `x` is not enumerable.\n any: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return true }\n }\n\n return false\n },\n // chunk returns an array of elements split into groups the length of size.\n // If `x` can't be split evenly, the final chunk will be the remaining elements.\n // It returns undefined if `x` is not array.\n chunk: func(x, size) {\n if !is_array_like(x) || !size { return undefined }\n\n numElements := len(x)\n if !numElements { return [] }\n\n res := []\n idx := 0\n for idx < numElements {\n res = append(res, x[idx:idx+size])\n idx += size\n }\n\n return res\n },\n // at returns an element at the given index (if `x` is array) or\n // key (if `x` is map). It returns undefined if `x` is not enumerable.\n at: func(x, key) {\n if !is_enumerable(x) { return undefined }\n\n if is_array_like(x) {\n if !is_int(key) { return undefined }\n } else {\n if !is_string(key) { return undefined }\n }\n\n return x[key]\n },\n // each iterates over elements of `x` and invokes `fn` for each element. `fn` is\n // invoked with two arguments: `key` and `value`. `key` is an int index\n // if `x` is array. `key` is a string key if `x` is map. It does not iterate\n // and returns undefined if `x` is not enumerable.\n each: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n fn(k, v)\n }\n },\n // filter iterates over elements of `x`, returning an array of all elements `fn`\n // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.\n // `key` is an int index if `x` is array. `key` is a string key if `x` is map.\n // It returns undefined if `x` is not enumerable.\n filter: func(x, fn) {\n if !is_array_like(x) { return undefined }\n\n dst := []\n for k, v in x {\n if fn(k, v) { dst = append(dst, v) }\n }\n\n return dst\n },\n // find iterates over elements of `x`, returning value of the first element `fn`\n // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.\n // `key` is an int index if `x` is array. `key` is a string key if `x` is map.\n // It returns undefined if `x` is not enumerable.\n find: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return v }\n }\n },\n // find_key iterates over elements of `x`, returning key or index of the first\n // element `fn` returns truthy for. `fn` is invoked with two arguments: `key`\n // and `value`. `key` is an int index if `x` is array. `key` is a string key if\n // `x` is map. It returns undefined if `x` is not enumerable.\n find_key: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return k }\n }\n },\n // map creates an array of values by running each element in `x` through `fn`.\n // `fn` is invoked with two arguments: `key` and `value`. `key` is an int index\n // if `x` is array. `key` is a string key if `x` is map. It returns undefined\n // if `x` is not enumerable.\n map: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n dst := []\n for k, v in x {\n dst = append(dst, fn(k, v))\n }\n\n return dst\n },\n // key returns the first argument.\n key: func(k, _) { return k },\n // value returns the second argument.\n value: func(_, v) { return v }\n}\n", +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/srcmod_enum.tengo b/vendor/github.com/d5/tengo/v2/stdlib/srcmod_enum.tengo new file mode 100644 index 00000000..7a5ea637 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/srcmod_enum.tengo @@ -0,0 +1,128 @@ +is_enumerable := func(x) { + return is_array(x) || is_map(x) || is_immutable_array(x) || is_immutable_map(x) +} + +is_array_like := func(x) { + return is_array(x) || is_immutable_array(x) +} + +export { + // all returns true if the given function `fn` evaluates to a truthy value on + // all of the items in `x`. It returns undefined if `x` is not enumerable. + all: func(x, fn) { + if !is_enumerable(x) { return undefined } + + for k, v in x { + if !fn(k, v) { return false } + } + + return true + }, + // any returns true if the given function `fn` evaluates to a truthy value on + // any of the items in `x`. It returns undefined if `x` is not enumerable. + any: func(x, fn) { + if !is_enumerable(x) { return undefined } + + for k, v in x { + if fn(k, v) { return true } + } + + return false + }, + // chunk returns an array of elements split into groups the length of size. + // If `x` can't be split evenly, the final chunk will be the remaining elements. + // It returns undefined if `x` is not array. + chunk: func(x, size) { + if !is_array_like(x) || !size { return undefined } + + numElements := len(x) + if !numElements { return [] } + + res := [] + idx := 0 + for idx < numElements { + res = append(res, x[idx:idx+size]) + idx += size + } + + return res + }, + // at returns an element at the given index (if `x` is array) or + // key (if `x` is map). It returns undefined if `x` is not enumerable. + at: func(x, key) { + if !is_enumerable(x) { return undefined } + + if is_array_like(x) { + if !is_int(key) { return undefined } + } else { + if !is_string(key) { return undefined } + } + + return x[key] + }, + // each iterates over elements of `x` and invokes `fn` for each element. `fn` is + // invoked with two arguments: `key` and `value`. `key` is an int index + // if `x` is array. `key` is a string key if `x` is map. It does not iterate + // and returns undefined if `x` is not enumerable. + each: func(x, fn) { + if !is_enumerable(x) { return undefined } + + for k, v in x { + fn(k, v) + } + }, + // filter iterates over elements of `x`, returning an array of all elements `fn` + // returns truthy for. `fn` is invoked with two arguments: `key` and `value`. + // `key` is an int index if `x` is array. `key` is a string key if `x` is map. + // It returns undefined if `x` is not enumerable. + filter: func(x, fn) { + if !is_array_like(x) { return undefined } + + dst := [] + for k, v in x { + if fn(k, v) { dst = append(dst, v) } + } + + return dst + }, + // find iterates over elements of `x`, returning value of the first element `fn` + // returns truthy for. `fn` is invoked with two arguments: `key` and `value`. + // `key` is an int index if `x` is array. `key` is a string key if `x` is map. + // It returns undefined if `x` is not enumerable. + find: func(x, fn) { + if !is_enumerable(x) { return undefined } + + for k, v in x { + if fn(k, v) { return v } + } + }, + // find_key iterates over elements of `x`, returning key or index of the first + // element `fn` returns truthy for. `fn` is invoked with two arguments: `key` + // and `value`. `key` is an int index if `x` is array. `key` is a string key if + // `x` is map. It returns undefined if `x` is not enumerable. + find_key: func(x, fn) { + if !is_enumerable(x) { return undefined } + + for k, v in x { + if fn(k, v) { return k } + } + }, + // map creates an array of values by running each element in `x` through `fn`. + // `fn` is invoked with two arguments: `key` and `value`. `key` is an int index + // if `x` is array. `key` is a string key if `x` is map. It returns undefined + // if `x` is not enumerable. + map: func(x, fn) { + if !is_enumerable(x) { return undefined } + + dst := [] + for k, v in x { + dst = append(dst, fn(k, v)) + } + + return dst + }, + // key returns the first argument. + key: func(k, _) { return k }, + // value returns the second argument. + value: func(_, v) { return v } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/stdlib.go b/vendor/github.com/d5/tengo/v2/stdlib/stdlib.go new file mode 100644 index 00000000..16c369a0 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/stdlib.go @@ -0,0 +1,34 @@ +package stdlib + +//go:generate go run gensrcmods.go + +import ( + "github.com/d5/tengo/v2" +) + +// AllModuleNames returns a list of all default module names. +func AllModuleNames() []string { + var names []string + for name := range BuiltinModules { + names = append(names, name) + } + for name := range SourceModules { + names = append(names, name) + } + return names +} + +// GetModuleMap returns the module map that includes all modules +// for the given module names. +func GetModuleMap(names ...string) *tengo.ModuleMap { + modules := tengo.NewModuleMap() + for _, name := range names { + if mod := BuiltinModules[name]; mod != nil { + modules.AddBuiltinModule(name, mod) + } + if mod := SourceModules[name]; mod != "" { + modules.AddSourceModule(name, []byte(mod)) + } + } + return modules +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/text.go b/vendor/github.com/d5/tengo/v2/stdlib/text.go new file mode 100644 index 00000000..d7d5d1da --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/text.go @@ -0,0 +1,1072 @@ +package stdlib + +import ( + "fmt" + "regexp" + "strconv" + "strings" + "unicode/utf8" + + "github.com/d5/tengo/v2" +) + +var textModule = map[string]tengo.Object{ + "re_match": &tengo.UserFunction{ + Name: "re_match", + Value: textREMatch, + }, // re_match(pattern, text) => bool/error + "re_find": &tengo.UserFunction{ + Name: "re_find", + Value: textREFind, + }, // re_find(pattern, text, count) => [[{text:,begin:,end:}]]/undefined + "re_replace": &tengo.UserFunction{ + Name: "re_replace", + Value: textREReplace, + }, // re_replace(pattern, text, repl) => string/error + "re_split": &tengo.UserFunction{ + Name: "re_split", + Value: textRESplit, + }, // re_split(pattern, text, count) => [string]/error + "re_compile": &tengo.UserFunction{ + Name: "re_compile", + Value: textRECompile, + }, // re_compile(pattern) => Regexp/error + "compare": &tengo.UserFunction{ + Name: "compare", + Value: FuncASSRI(strings.Compare), + }, // compare(a, b) => int + "contains": &tengo.UserFunction{ + Name: "contains", + Value: FuncASSRB(strings.Contains), + }, // contains(s, substr) => bool + "contains_any": &tengo.UserFunction{ + Name: "contains_any", + Value: FuncASSRB(strings.ContainsAny), + }, // contains_any(s, chars) => bool + "count": &tengo.UserFunction{ + Name: "count", + Value: FuncASSRI(strings.Count), + }, // count(s, substr) => int + "equal_fold": &tengo.UserFunction{ + Name: "equal_fold", + Value: FuncASSRB(strings.EqualFold), + }, // "equal_fold(s, t) => bool + "fields": &tengo.UserFunction{ + Name: "fields", + Value: FuncASRSs(strings.Fields), + }, // fields(s) => [string] + "has_prefix": &tengo.UserFunction{ + Name: "has_prefix", + Value: FuncASSRB(strings.HasPrefix), + }, // has_prefix(s, prefix) => bool + "has_suffix": &tengo.UserFunction{ + Name: "has_suffix", + Value: FuncASSRB(strings.HasSuffix), + }, // has_suffix(s, suffix) => bool + "index": &tengo.UserFunction{ + Name: "index", + Value: FuncASSRI(strings.Index), + }, // index(s, substr) => int + "index_any": &tengo.UserFunction{ + Name: "index_any", + Value: FuncASSRI(strings.IndexAny), + }, // index_any(s, chars) => int + "join": &tengo.UserFunction{ + Name: "join", + Value: textJoin, + }, // join(arr, sep) => string + "last_index": &tengo.UserFunction{ + Name: "last_index", + Value: FuncASSRI(strings.LastIndex), + }, // last_index(s, substr) => int + "last_index_any": &tengo.UserFunction{ + Name: "last_index_any", + Value: FuncASSRI(strings.LastIndexAny), + }, // last_index_any(s, chars) => int + "repeat": &tengo.UserFunction{ + Name: "repeat", + Value: textRepeat, + }, // repeat(s, count) => string + "replace": &tengo.UserFunction{ + Name: "replace", + Value: textReplace, + }, // replace(s, old, new, n) => string + "substr": &tengo.UserFunction{ + Name: "substr", + Value: textSubstring, + }, // substr(s, lower, upper) => string + "split": &tengo.UserFunction{ + Name: "split", + Value: FuncASSRSs(strings.Split), + }, // split(s, sep) => [string] + "split_after": &tengo.UserFunction{ + Name: "split_after", + Value: FuncASSRSs(strings.SplitAfter), + }, // split_after(s, sep) => [string] + "split_after_n": &tengo.UserFunction{ + Name: "split_after_n", + Value: FuncASSIRSs(strings.SplitAfterN), + }, // split_after_n(s, sep, n) => [string] + "split_n": &tengo.UserFunction{ + Name: "split_n", + Value: FuncASSIRSs(strings.SplitN), + }, // split_n(s, sep, n) => [string] + "title": &tengo.UserFunction{ + Name: "title", + Value: FuncASRS(strings.Title), + }, // title(s) => string + "to_lower": &tengo.UserFunction{ + Name: "to_lower", + Value: FuncASRS(strings.ToLower), + }, // to_lower(s) => string + "to_title": &tengo.UserFunction{ + Name: "to_title", + Value: FuncASRS(strings.ToTitle), + }, // to_title(s) => string + "to_upper": &tengo.UserFunction{ + Name: "to_upper", + Value: FuncASRS(strings.ToUpper), + }, // to_upper(s) => string + "pad_left": &tengo.UserFunction{ + Name: "pad_left", + Value: textPadLeft, + }, // pad_left(s, pad_len, pad_with) => string + "pad_right": &tengo.UserFunction{ + Name: "pad_right", + Value: textPadRight, + }, // pad_right(s, pad_len, pad_with) => string + "trim": &tengo.UserFunction{ + Name: "trim", + Value: FuncASSRS(strings.Trim), + }, // trim(s, cutset) => string + "trim_left": &tengo.UserFunction{ + Name: "trim_left", + Value: FuncASSRS(strings.TrimLeft), + }, // trim_left(s, cutset) => string + "trim_prefix": &tengo.UserFunction{ + Name: "trim_prefix", + Value: FuncASSRS(strings.TrimPrefix), + }, // trim_prefix(s, prefix) => string + "trim_right": &tengo.UserFunction{ + Name: "trim_right", + Value: FuncASSRS(strings.TrimRight), + }, // trim_right(s, cutset) => string + "trim_space": &tengo.UserFunction{ + Name: "trim_space", + Value: FuncASRS(strings.TrimSpace), + }, // trim_space(s) => string + "trim_suffix": &tengo.UserFunction{ + Name: "trim_suffix", + Value: FuncASSRS(strings.TrimSuffix), + }, // trim_suffix(s, suffix) => string + "atoi": &tengo.UserFunction{ + Name: "atoi", + Value: FuncASRIE(strconv.Atoi), + }, // atoi(str) => int/error + "format_bool": &tengo.UserFunction{ + Name: "format_bool", + Value: textFormatBool, + }, // format_bool(b) => string + "format_float": &tengo.UserFunction{ + Name: "format_float", + Value: textFormatFloat, + }, // format_float(f, fmt, prec, bits) => string + "format_int": &tengo.UserFunction{ + Name: "format_int", + Value: textFormatInt, + }, // format_int(i, base) => string + "itoa": &tengo.UserFunction{ + Name: "itoa", + Value: FuncAIRS(strconv.Itoa), + }, // itoa(i) => string + "parse_bool": &tengo.UserFunction{ + Name: "parse_bool", + Value: textParseBool, + }, // parse_bool(str) => bool/error + "parse_float": &tengo.UserFunction{ + Name: "parse_float", + Value: textParseFloat, + }, // parse_float(str, bits) => float/error + "parse_int": &tengo.UserFunction{ + Name: "parse_int", + Value: textParseInt, + }, // parse_int(str, base, bits) => int/error + "quote": &tengo.UserFunction{ + Name: "quote", + Value: FuncASRS(strconv.Quote), + }, // quote(str) => string + "unquote": &tengo.UserFunction{ + Name: "unquote", + Value: FuncASRSE(strconv.Unquote), + }, // unquote(str) => string/error +} + +func textREMatch(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + matched, err := regexp.MatchString(s1, s2) + if err != nil { + ret = wrapError(err) + return + } + + if matched { + ret = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return +} + +func textREFind(args ...tengo.Object) (ret tengo.Object, err error) { + numArgs := len(args) + if numArgs != 2 && numArgs != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + re, err := regexp.Compile(s1) + if err != nil { + ret = wrapError(err) + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + if numArgs < 3 { + m := re.FindStringSubmatchIndex(s2) + if m == nil { + ret = tengo.UndefinedValue + return + } + + arr := &tengo.Array{} + for i := 0; i < len(m); i += 2 { + arr.Value = append(arr.Value, + &tengo.ImmutableMap{Value: map[string]tengo.Object{ + "text": &tengo.String{Value: s2[m[i]:m[i+1]]}, + "begin": &tengo.Int{Value: int64(m[i])}, + "end": &tengo.Int{Value: int64(m[i+1])}, + }}) + } + + ret = &tengo.Array{Value: []tengo.Object{arr}} + + return + } + + i3, ok := tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + m := re.FindAllStringSubmatchIndex(s2, i3) + if m == nil { + ret = tengo.UndefinedValue + return + } + + arr := &tengo.Array{} + for _, m := range m { + subMatch := &tengo.Array{} + for i := 0; i < len(m); i += 2 { + subMatch.Value = append(subMatch.Value, + &tengo.ImmutableMap{Value: map[string]tengo.Object{ + "text": &tengo.String{Value: s2[m[i]:m[i+1]]}, + "begin": &tengo.Int{Value: int64(m[i])}, + "end": &tengo.Int{Value: int64(m[i+1])}, + }}) + } + + arr.Value = append(arr.Value, subMatch) + } + + ret = arr + + return +} + +func textREReplace(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + s3, ok := tengo.ToString(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + return + } + + re, err := regexp.Compile(s1) + if err != nil { + ret = wrapError(err) + } else { + s, ok := doTextRegexpReplace(re, s2, s3) + if !ok { + return nil, tengo.ErrStringLimit + } + + ret = &tengo.String{Value: s} + } + + return +} + +func textRESplit(args ...tengo.Object) (ret tengo.Object, err error) { + numArgs := len(args) + if numArgs != 2 && numArgs != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + var i3 = -1 + if numArgs > 2 { + i3, ok = tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + } + + re, err := regexp.Compile(s1) + if err != nil { + ret = wrapError(err) + return + } + + arr := &tengo.Array{} + for _, s := range re.Split(s2, i3) { + arr.Value = append(arr.Value, &tengo.String{Value: s}) + } + + ret = arr + + return +} + +func textRECompile(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + re, err := regexp.Compile(s1) + if err != nil { + ret = wrapError(err) + } else { + ret = makeTextRegexp(re) + } + + return +} + +func textReplace(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 4 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + s3, ok := tengo.ToString(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + return + } + + i4, ok := tengo.ToInt(args[3]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "fourth", + Expected: "int(compatible)", + Found: args[3].TypeName(), + } + return + } + + s, ok := doTextReplace(s1, s2, s3, i4) + if !ok { + err = tengo.ErrStringLimit + return + } + + ret = &tengo.String{Value: s} + + return +} + +func textSubstring(args ...tengo.Object) (ret tengo.Object, err error) { + argslen := len(args) + if argslen != 2 && argslen != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + strlen := len(s1) + i3 := strlen + if argslen == 3 { + i3, ok = tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + } + + if i2 > i3 { + err = tengo.ErrInvalidIndexType + return + } + + if i2 < 0 { + i2 = 0 + } else if i2 > strlen { + i2 = strlen + } + + if i3 < 0 { + i3 = 0 + } else if i3 > strlen { + i3 = strlen + } + + ret = &tengo.String{Value: s1[i2:i3]} + + return +} + +func textPadLeft(args ...tengo.Object) (ret tengo.Object, err error) { + argslen := len(args) + if argslen != 2 && argslen != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + if i2 > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + + sLen := len(s1) + if sLen >= i2 { + ret = &tengo.String{Value: s1} + return + } + + s3 := " " + if argslen == 3 { + s3, ok = tengo.ToString(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + return + } + } + + padStrLen := len(s3) + if padStrLen == 0 { + ret = &tengo.String{Value: s1} + return + } + + padCount := ((i2 - padStrLen) / padStrLen) + 1 + retStr := strings.Repeat(s3, padCount) + s1 + ret = &tengo.String{Value: retStr[len(retStr)-i2:]} + + return +} + +func textPadRight(args ...tengo.Object) (ret tengo.Object, err error) { + argslen := len(args) + if argslen != 2 && argslen != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + if i2 > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + + sLen := len(s1) + if sLen >= i2 { + ret = &tengo.String{Value: s1} + return + } + + s3 := " " + if argslen == 3 { + s3, ok = tengo.ToString(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + return + } + } + + padStrLen := len(s3) + if padStrLen == 0 { + ret = &tengo.String{Value: s1} + return + } + + padCount := ((i2 - padStrLen) / padStrLen) + 1 + retStr := s1 + strings.Repeat(s3, padCount) + ret = &tengo.String{Value: retStr[:i2]} + + return +} + +func textRepeat(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + + if len(s1)*i2 > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + + return &tengo.String{Value: strings.Repeat(s1, i2)}, nil +} + +func textJoin(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + + var slen int + var ss1 []string + switch arg0 := args[0].(type) { + case *tengo.Array: + for idx, a := range arg0.Value { + as, ok := tengo.ToString(a) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("first[%d]", idx), + Expected: "string(compatible)", + Found: a.TypeName(), + } + } + slen += len(as) + ss1 = append(ss1, as) + } + case *tengo.ImmutableArray: + for idx, a := range arg0.Value { + as, ok := tengo.ToString(a) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("first[%d]", idx), + Expected: "string(compatible)", + Found: a.TypeName(), + } + } + slen += len(as) + ss1 = append(ss1, as) + } + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "array", + Found: args[0].TypeName(), + } + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + + // make sure output length does not exceed the limit + if slen+len(s2)*(len(ss1)-1) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + + return &tengo.String{Value: strings.Join(ss1, s2)}, nil +} + +func textFormatBool(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + b1, ok := args[0].(*tengo.Bool) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bool", + Found: args[0].TypeName(), + } + return + } + + if b1 == tengo.TrueValue { + ret = &tengo.String{Value: "true"} + } else { + ret = &tengo.String{Value: "false"} + } + + return +} + +func textFormatFloat(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 4 { + err = tengo.ErrWrongNumArguments + return + } + + f1, ok := args[0].(*tengo.Float) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + i3, ok := tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + + i4, ok := tengo.ToInt(args[3]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "fourth", + Expected: "int(compatible)", + Found: args[3].TypeName(), + } + return + } + + ret = &tengo.String{Value: strconv.FormatFloat(f1.Value, s2[0], i3, i4)} + + return +} + +func textFormatInt(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := args[0].(*tengo.Int) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + ret = &tengo.String{Value: strconv.FormatInt(i1.Value, i2)} + + return +} + +func textParseBool(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := args[0].(*tengo.String) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string", + Found: args[0].TypeName(), + } + return + } + + parsed, err := strconv.ParseBool(s1.Value) + if err != nil { + ret = wrapError(err) + return + } + + if parsed { + ret = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return +} + +func textParseFloat(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := args[0].(*tengo.String) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + parsed, err := strconv.ParseFloat(s1.Value, i2) + if err != nil { + ret = wrapError(err) + return + } + + ret = &tengo.Float{Value: parsed} + + return +} + +func textParseInt(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := args[0].(*tengo.String) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + i3, ok := tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + + parsed, err := strconv.ParseInt(s1.Value, i2, i3) + if err != nil { + ret = wrapError(err) + return + } + + ret = &tengo.Int{Value: parsed} + + return +} + +// Modified implementation of strings.Replace +// to limit the maximum length of output string. +func doTextReplace(s, old, new string, n int) (string, bool) { + if old == new || n == 0 { + return s, true // avoid allocation + } + + // Compute number of replacements. + if m := strings.Count(s, old); m == 0 { + return s, true // avoid allocation + } else if n < 0 || m < n { + n = m + } + + // Apply replacements to buffer. + t := make([]byte, len(s)+n*(len(new)-len(old))) + w := 0 + start := 0 + for i := 0; i < n; i++ { + j := start + if len(old) == 0 { + if i > 0 { + _, wid := utf8.DecodeRuneInString(s[start:]) + j += wid + } + } else { + j += strings.Index(s[start:], old) + } + + ssj := s[start:j] + if w+len(ssj)+len(new) > tengo.MaxStringLen { + return "", false + } + + w += copy(t[w:], ssj) + w += copy(t[w:], new) + start = j + len(old) + } + + ss := s[start:] + if w+len(ss) > tengo.MaxStringLen { + return "", false + } + + w += copy(t[w:], ss) + + return string(t[0:w]), true +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/text_regexp.go b/vendor/github.com/d5/tengo/v2/stdlib/text_regexp.go new file mode 100644 index 00000000..1a7ecf07 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/text_regexp.go @@ -0,0 +1,251 @@ +package stdlib + +import ( + "regexp" + + "github.com/d5/tengo/v2" +) + +func makeTextRegexp(re *regexp.Regexp) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + // match(text) => bool + "match": &tengo.UserFunction{ + Value: func(args ...tengo.Object) ( + ret tengo.Object, + err error, + ) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + if re.MatchString(s1) { + ret = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return + }, + }, + + // find(text) => array(array({text:,begin:,end:}))/undefined + // find(text, maxCount) => array(array({text:,begin:,end:}))/undefined + "find": &tengo.UserFunction{ + Value: func(args ...tengo.Object) ( + ret tengo.Object, + err error, + ) { + numArgs := len(args) + if numArgs != 1 && numArgs != 2 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + if numArgs == 1 { + m := re.FindStringSubmatchIndex(s1) + if m == nil { + ret = tengo.UndefinedValue + return + } + + arr := &tengo.Array{} + for i := 0; i < len(m); i += 2 { + arr.Value = append(arr.Value, + &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "text": &tengo.String{ + Value: s1[m[i]:m[i+1]], + }, + "begin": &tengo.Int{ + Value: int64(m[i]), + }, + "end": &tengo.Int{ + Value: int64(m[i+1]), + }, + }}) + } + + ret = &tengo.Array{Value: []tengo.Object{arr}} + + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + m := re.FindAllStringSubmatchIndex(s1, i2) + if m == nil { + ret = tengo.UndefinedValue + return + } + + arr := &tengo.Array{} + for _, m := range m { + subMatch := &tengo.Array{} + for i := 0; i < len(m); i += 2 { + subMatch.Value = append(subMatch.Value, + &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "text": &tengo.String{ + Value: s1[m[i]:m[i+1]], + }, + "begin": &tengo.Int{ + Value: int64(m[i]), + }, + "end": &tengo.Int{ + Value: int64(m[i+1]), + }, + }}) + } + + arr.Value = append(arr.Value, subMatch) + } + + ret = arr + + return + }, + }, + + // replace(src, repl) => string + "replace": &tengo.UserFunction{ + Value: func(args ...tengo.Object) ( + ret tengo.Object, + err error, + ) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + s, ok := doTextRegexpReplace(re, s1, s2) + if !ok { + return nil, tengo.ErrStringLimit + } + + ret = &tengo.String{Value: s} + + return + }, + }, + + // split(text) => array(string) + // split(text, maxCount) => array(string) + "split": &tengo.UserFunction{ + Value: func(args ...tengo.Object) ( + ret tengo.Object, + err error, + ) { + numArgs := len(args) + if numArgs != 1 && numArgs != 2 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + var i2 = -1 + if numArgs > 1 { + i2, ok = tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + } + + arr := &tengo.Array{} + for _, s := range re.Split(s1, i2) { + arr.Value = append(arr.Value, + &tengo.String{Value: s}) + } + + ret = arr + + return + }, + }, + }, + } +} + +// Size-limit checking implementation of regexp.ReplaceAllString. +func doTextRegexpReplace(re *regexp.Regexp, src, repl string) (string, bool) { + idx := 0 + out := "" + for _, m := range re.FindAllStringSubmatchIndex(src, -1) { + var exp []byte + exp = re.ExpandString(exp, repl, src, m) + if len(out)+m[0]-idx+len(exp) > tengo.MaxStringLen { + return "", false + } + out += src[idx:m[0]] + string(exp) + idx = m[1] + } + if idx < len(src) { + if len(out)+len(src)-idx > tengo.MaxStringLen { + return "", false + } + out += src[idx:] + } + return out, true +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/times.go b/vendor/github.com/d5/tengo/v2/stdlib/times.go new file mode 100644 index 00000000..0b6f7bd4 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/times.go @@ -0,0 +1,1135 @@ +package stdlib + +import ( + "time" + + "github.com/d5/tengo/v2" +) + +var timesModule = map[string]tengo.Object{ + "format_ansic": &tengo.String{Value: time.ANSIC}, + "format_unix_date": &tengo.String{Value: time.UnixDate}, + "format_ruby_date": &tengo.String{Value: time.RubyDate}, + "format_rfc822": &tengo.String{Value: time.RFC822}, + "format_rfc822z": &tengo.String{Value: time.RFC822Z}, + "format_rfc850": &tengo.String{Value: time.RFC850}, + "format_rfc1123": &tengo.String{Value: time.RFC1123}, + "format_rfc1123z": &tengo.String{Value: time.RFC1123Z}, + "format_rfc3339": &tengo.String{Value: time.RFC3339}, + "format_rfc3339_nano": &tengo.String{Value: time.RFC3339Nano}, + "format_kitchen": &tengo.String{Value: time.Kitchen}, + "format_stamp": &tengo.String{Value: time.Stamp}, + "format_stamp_milli": &tengo.String{Value: time.StampMilli}, + "format_stamp_micro": &tengo.String{Value: time.StampMicro}, + "format_stamp_nano": &tengo.String{Value: time.StampNano}, + "nanosecond": &tengo.Int{Value: int64(time.Nanosecond)}, + "microsecond": &tengo.Int{Value: int64(time.Microsecond)}, + "millisecond": &tengo.Int{Value: int64(time.Millisecond)}, + "second": &tengo.Int{Value: int64(time.Second)}, + "minute": &tengo.Int{Value: int64(time.Minute)}, + "hour": &tengo.Int{Value: int64(time.Hour)}, + "january": &tengo.Int{Value: int64(time.January)}, + "february": &tengo.Int{Value: int64(time.February)}, + "march": &tengo.Int{Value: int64(time.March)}, + "april": &tengo.Int{Value: int64(time.April)}, + "may": &tengo.Int{Value: int64(time.May)}, + "june": &tengo.Int{Value: int64(time.June)}, + "july": &tengo.Int{Value: int64(time.July)}, + "august": &tengo.Int{Value: int64(time.August)}, + "september": &tengo.Int{Value: int64(time.September)}, + "october": &tengo.Int{Value: int64(time.October)}, + "november": &tengo.Int{Value: int64(time.November)}, + "december": &tengo.Int{Value: int64(time.December)}, + "sleep": &tengo.UserFunction{ + Name: "sleep", + Value: timesSleep, + }, // sleep(int) + "parse_duration": &tengo.UserFunction{ + Name: "parse_duration", + Value: timesParseDuration, + }, // parse_duration(str) => int + "since": &tengo.UserFunction{ + Name: "since", + Value: timesSince, + }, // since(time) => int + "until": &tengo.UserFunction{ + Name: "until", + Value: timesUntil, + }, // until(time) => int + "duration_hours": &tengo.UserFunction{ + Name: "duration_hours", + Value: timesDurationHours, + }, // duration_hours(int) => float + "duration_minutes": &tengo.UserFunction{ + Name: "duration_minutes", + Value: timesDurationMinutes, + }, // duration_minutes(int) => float + "duration_nanoseconds": &tengo.UserFunction{ + Name: "duration_nanoseconds", + Value: timesDurationNanoseconds, + }, // duration_nanoseconds(int) => int + "duration_seconds": &tengo.UserFunction{ + Name: "duration_seconds", + Value: timesDurationSeconds, + }, // duration_seconds(int) => float + "duration_string": &tengo.UserFunction{ + Name: "duration_string", + Value: timesDurationString, + }, // duration_string(int) => string + "month_string": &tengo.UserFunction{ + Name: "month_string", + Value: timesMonthString, + }, // month_string(int) => string + "date": &tengo.UserFunction{ + Name: "date", + Value: timesDate, + }, // date(year, month, day, hour, min, sec, nsec) => time + "now": &tengo.UserFunction{ + Name: "now", + Value: timesNow, + }, // now() => time + "parse": &tengo.UserFunction{ + Name: "parse", + Value: timesParse, + }, // parse(format, str) => time + "unix": &tengo.UserFunction{ + Name: "unix", + Value: timesUnix, + }, // unix(sec, nsec) => time + "add": &tengo.UserFunction{ + Name: "add", + Value: timesAdd, + }, // add(time, int) => time + "add_date": &tengo.UserFunction{ + Name: "add_date", + Value: timesAddDate, + }, // add_date(time, years, months, days) => time + "sub": &tengo.UserFunction{ + Name: "sub", + Value: timesSub, + }, // sub(t time, u time) => int + "after": &tengo.UserFunction{ + Name: "after", + Value: timesAfter, + }, // after(t time, u time) => bool + "before": &tengo.UserFunction{ + Name: "before", + Value: timesBefore, + }, // before(t time, u time) => bool + "time_year": &tengo.UserFunction{ + Name: "time_year", + Value: timesTimeYear, + }, // time_year(time) => int + "time_month": &tengo.UserFunction{ + Name: "time_month", + Value: timesTimeMonth, + }, // time_month(time) => int + "time_day": &tengo.UserFunction{ + Name: "time_day", + Value: timesTimeDay, + }, // time_day(time) => int + "time_weekday": &tengo.UserFunction{ + Name: "time_weekday", + Value: timesTimeWeekday, + }, // time_weekday(time) => int + "time_hour": &tengo.UserFunction{ + Name: "time_hour", + Value: timesTimeHour, + }, // time_hour(time) => int + "time_minute": &tengo.UserFunction{ + Name: "time_minute", + Value: timesTimeMinute, + }, // time_minute(time) => int + "time_second": &tengo.UserFunction{ + Name: "time_second", + Value: timesTimeSecond, + }, // time_second(time) => int + "time_nanosecond": &tengo.UserFunction{ + Name: "time_nanosecond", + Value: timesTimeNanosecond, + }, // time_nanosecond(time) => int + "time_unix": &tengo.UserFunction{ + Name: "time_unix", + Value: timesTimeUnix, + }, // time_unix(time) => int + "time_unix_nano": &tengo.UserFunction{ + Name: "time_unix_nano", + Value: timesTimeUnixNano, + }, // time_unix_nano(time) => int + "time_format": &tengo.UserFunction{ + Name: "time_format", + Value: timesTimeFormat, + }, // time_format(time, format) => string + "time_location": &tengo.UserFunction{ + Name: "time_location", + Value: timesTimeLocation, + }, // time_location(time) => string + "time_string": &tengo.UserFunction{ + Name: "time_string", + Value: timesTimeString, + }, // time_string(time) => string + "is_zero": &tengo.UserFunction{ + Name: "is_zero", + Value: timesIsZero, + }, // is_zero(time) => bool + "to_local": &tengo.UserFunction{ + Name: "to_local", + Value: timesToLocal, + }, // to_local(time) => time + "to_utc": &tengo.UserFunction{ + Name: "to_utc", + Value: timesToUTC, + }, // to_utc(time) => time +} + +func timesSleep(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + time.Sleep(time.Duration(i1)) + ret = tengo.UndefinedValue + + return +} + +func timesParseDuration(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + dur, err := time.ParseDuration(s1) + if err != nil { + ret = wrapError(err) + return + } + + ret = &tengo.Int{Value: int64(dur)} + + return +} + +func timesSince(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(time.Since(t1))} + + return +} + +func timesUntil(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(time.Until(t1))} + + return +} + +func timesDurationHours(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Float{Value: time.Duration(i1).Hours()} + + return +} + +func timesDurationMinutes(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Float{Value: time.Duration(i1).Minutes()} + + return +} + +func timesDurationNanoseconds(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: time.Duration(i1).Nanoseconds()} + + return +} + +func timesDurationSeconds(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Float{Value: time.Duration(i1).Seconds()} + + return +} + +func timesDurationString(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.String{Value: time.Duration(i1).String()} + + return +} + +func timesMonthString(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.String{Value: time.Month(i1).String()} + + return +} + +func timesDate(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 7 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + i3, ok := tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + i4, ok := tengo.ToInt(args[3]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "fourth", + Expected: "int(compatible)", + Found: args[3].TypeName(), + } + return + } + i5, ok := tengo.ToInt(args[4]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "fifth", + Expected: "int(compatible)", + Found: args[4].TypeName(), + } + return + } + i6, ok := tengo.ToInt(args[5]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "sixth", + Expected: "int(compatible)", + Found: args[5].TypeName(), + } + return + } + i7, ok := tengo.ToInt(args[6]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "seventh", + Expected: "int(compatible)", + Found: args[6].TypeName(), + } + return + } + + ret = &tengo.Time{ + Value: time.Date(i1, + time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location()), + } + + return +} + +func timesNow(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + err = tengo.ErrWrongNumArguments + return + } + + ret = &tengo.Time{Value: time.Now()} + + return +} + +func timesParse(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + parsed, err := time.Parse(s1, s2) + if err != nil { + ret = wrapError(err) + return + } + + ret = &tengo.Time{Value: parsed} + + return +} + +func timesUnix(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt64(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + ret = &tengo.Time{Value: time.Unix(i1, i2)} + + return +} + +func timesAdd(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt64(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + ret = &tengo.Time{Value: t1.Add(time.Duration(i2))} + + return +} + +func timesSub(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + t2, ok := tengo.ToTime(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "time(compatible)", + Found: args[1].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Sub(t2))} + + return +} + +func timesAddDate(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 4 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + i3, ok := tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + + i4, ok := tengo.ToInt(args[3]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "fourth", + Expected: "int(compatible)", + Found: args[3].TypeName(), + } + return + } + + ret = &tengo.Time{Value: t1.AddDate(i2, i3, i4)} + + return +} + +func timesAfter(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + t2, ok := tengo.ToTime(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "time(compatible)", + Found: args[1].TypeName(), + } + return + } + + if t1.After(t2) { + ret = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return +} + +func timesBefore(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + t2, ok := tengo.ToTime(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + if t1.Before(t2) { + ret = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return +} + +func timesTimeYear(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Year())} + + return +} + +func timesTimeMonth(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Month())} + + return +} + +func timesTimeDay(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Day())} + + return +} + +func timesTimeWeekday(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Weekday())} + + return +} + +func timesTimeHour(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Hour())} + + return +} + +func timesTimeMinute(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Minute())} + + return +} + +func timesTimeSecond(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Second())} + + return +} + +func timesTimeNanosecond(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Nanosecond())} + + return +} + +func timesTimeUnix(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: t1.Unix()} + + return +} + +func timesTimeUnixNano(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: t1.UnixNano()} + + return +} + +func timesTimeFormat(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + s := t1.Format(s2) + if len(s) > tengo.MaxStringLen { + + return nil, tengo.ErrStringLimit + } + + ret = &tengo.String{Value: s} + + return +} + +func timesIsZero(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + if t1.IsZero() { + ret = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return +} + +func timesToLocal(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Time{Value: t1.Local()} + + return +} + +func timesToUTC(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Time{Value: t1.UTC()} + + return +} + +func timesTimeLocation(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.String{Value: t1.Location().String()} + + return +} + +func timesTimeString(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.String{Value: t1.String()} + + return +} diff --git a/vendor/github.com/d5/tengo/v2/symbol_table.go b/vendor/github.com/d5/tengo/v2/symbol_table.go new file mode 100644 index 00000000..6ae5d7d3 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/symbol_table.go @@ -0,0 +1,165 @@ +package tengo + +// SymbolScope represents a symbol scope. +type SymbolScope string + +// List of symbol scopes +const ( + ScopeGlobal SymbolScope = "GLOBAL" + ScopeLocal SymbolScope = "LOCAL" + ScopeBuiltin SymbolScope = "BUILTIN" + ScopeFree SymbolScope = "FREE" +) + +// Symbol represents a symbol in the symbol table. +type Symbol struct { + Name string + Scope SymbolScope + Index int + LocalAssigned bool // if the local symbol is assigned at least once +} + +// SymbolTable represents a symbol table. +type SymbolTable struct { + parent *SymbolTable + block bool + store map[string]*Symbol + numDefinition int + maxDefinition int + freeSymbols []*Symbol + builtinSymbols []*Symbol +} + +// NewSymbolTable creates a SymbolTable. +func NewSymbolTable() *SymbolTable { + return &SymbolTable{ + store: make(map[string]*Symbol), + } +} + +// Define adds a new symbol in the current scope. +func (t *SymbolTable) Define(name string) *Symbol { + symbol := &Symbol{Name: name, Index: t.nextIndex()} + t.numDefinition++ + + if t.Parent(true) == nil { + symbol.Scope = ScopeGlobal + } else { + symbol.Scope = ScopeLocal + } + t.store[name] = symbol + t.updateMaxDefs(symbol.Index + 1) + return symbol +} + +// DefineBuiltin adds a symbol for builtin function. +func (t *SymbolTable) DefineBuiltin(index int, name string) *Symbol { + if t.parent != nil { + return t.parent.DefineBuiltin(index, name) + } + + symbol := &Symbol{ + Name: name, + Index: index, + Scope: ScopeBuiltin, + } + t.store[name] = symbol + t.builtinSymbols = append(t.builtinSymbols, symbol) + return symbol +} + +// Resolve resolves a symbol with a given name. +func (t *SymbolTable) Resolve( + name string, +) (symbol *Symbol, depth int, ok bool) { + symbol, ok = t.store[name] + if !ok && t.parent != nil { + symbol, depth, ok = t.parent.Resolve(name) + if !ok { + return + } + depth++ + + // if symbol is defined in parent table and if it's not global/builtin + // then it's free variable. + if !t.block && depth > 0 && + symbol.Scope != ScopeGlobal && + symbol.Scope != ScopeBuiltin { + return t.defineFree(symbol), depth, true + } + return + } + return +} + +// Fork creates a new symbol table for a new scope. +func (t *SymbolTable) Fork(block bool) *SymbolTable { + return &SymbolTable{ + store: make(map[string]*Symbol), + parent: t, + block: block, + } +} + +// Parent returns the outer scope of the current symbol table. +func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable { + if skipBlock && t.block { + return t.parent.Parent(skipBlock) + } + return t.parent +} + +// MaxSymbols returns the total number of symbols defined in the scope. +func (t *SymbolTable) MaxSymbols() int { + return t.maxDefinition +} + +// FreeSymbols returns free symbols for the scope. +func (t *SymbolTable) FreeSymbols() []*Symbol { + return t.freeSymbols +} + +// BuiltinSymbols returns builtin symbols for the scope. +func (t *SymbolTable) BuiltinSymbols() []*Symbol { + if t.parent != nil { + return t.parent.BuiltinSymbols() + } + return t.builtinSymbols +} + +// Names returns the name of all the symbols. +func (t *SymbolTable) Names() []string { + var names []string + for name := range t.store { + names = append(names, name) + } + return names +} + +func (t *SymbolTable) nextIndex() int { + if t.block { + return t.parent.nextIndex() + t.numDefinition + } + return t.numDefinition +} + +func (t *SymbolTable) updateMaxDefs(numDefs int) { + if numDefs > t.maxDefinition { + t.maxDefinition = numDefs + } + if t.block { + t.parent.updateMaxDefs(numDefs) + } +} + +func (t *SymbolTable) defineFree(original *Symbol) *Symbol { + // TODO: should we check duplicates? + t.freeSymbols = append(t.freeSymbols, original) + symbol := &Symbol{ + Name: original.Name, + Index: len(t.freeSymbols) - 1, + Scope: ScopeFree, + } + t.store[original.Name] = symbol + return symbol +} diff --git a/vendor/github.com/d5/tengo/v2/tengo.go b/vendor/github.com/d5/tengo/v2/tengo.go new file mode 100644 index 00000000..098a1970 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/tengo.go @@ -0,0 +1,306 @@ +package tengo + +import ( + "errors" + "fmt" + "strconv" + "time" +) + +var ( + // MaxStringLen is the maximum byte-length for string value. Note this + // limit applies to all compiler/VM instances in the process. + MaxStringLen = 2147483647 + + // MaxBytesLen is the maximum length for bytes value. Note this limit + // applies to all compiler/VM instances in the process. + MaxBytesLen = 2147483647 +) + +const ( + // GlobalsSize is the maximum number of global variables for a VM. + GlobalsSize = 1024 + + // StackSize is the maximum stack size for a VM. + StackSize = 2048 + + // MaxFrames is the maximum number of function frames for a VM. + MaxFrames = 1024 +) + +// CallableFunc is a function signature for the callable functions. +type CallableFunc = func(args ...Object) (ret Object, err error) + +// CountObjects returns the number of objects that a given object o contains. +// For scalar value types, it will always be 1. For compound value types, +// this will include its elements and all of their elements recursively. +func CountObjects(o Object) (c int) { + c = 1 + switch o := o.(type) { + case *Array: + for _, v := range o.Value { + c += CountObjects(v) + } + case *ImmutableArray: + for _, v := range o.Value { + c += CountObjects(v) + } + case *Map: + for _, v := range o.Value { + c += CountObjects(v) + } + case *ImmutableMap: + for _, v := range o.Value { + c += CountObjects(v) + } + case *Error: + c += CountObjects(o.Value) + } + return +} + +// ToString will try to convert object o to string value. +func ToString(o Object) (v string, ok bool) { + if o == UndefinedValue { + return + } + ok = true + if str, isStr := o.(*String); isStr { + v = str.Value + } else { + v = o.String() + } + return +} + +// ToInt will try to convert object o to int value. +func ToInt(o Object) (v int, ok bool) { + switch o := o.(type) { + case *Int: + v = int(o.Value) + ok = true + case *Float: + v = int(o.Value) + ok = true + case *Char: + v = int(o.Value) + ok = true + case *Bool: + if o == TrueValue { + v = 1 + } + ok = true + case *String: + c, err := strconv.ParseInt(o.Value, 10, 64) + if err == nil { + v = int(c) + ok = true + } + } + return +} + +// ToInt64 will try to convert object o to int64 value. +func ToInt64(o Object) (v int64, ok bool) { + switch o := o.(type) { + case *Int: + v = o.Value + ok = true + case *Float: + v = int64(o.Value) + ok = true + case *Char: + v = int64(o.Value) + ok = true + case *Bool: + if o == TrueValue { + v = 1 + } + ok = true + case *String: + c, err := strconv.ParseInt(o.Value, 10, 64) + if err == nil { + v = c + ok = true + } + } + return +} + +// ToFloat64 will try to convert object o to float64 value. +func ToFloat64(o Object) (v float64, ok bool) { + switch o := o.(type) { + case *Int: + v = float64(o.Value) + ok = true + case *Float: + v = o.Value + ok = true + case *String: + c, err := strconv.ParseFloat(o.Value, 64) + if err == nil { + v = c + ok = true + } + } + return +} + +// ToBool will try to convert object o to bool value. +func ToBool(o Object) (v bool, ok bool) { + ok = true + v = !o.IsFalsy() + return +} + +// ToRune will try to convert object o to rune value. +func ToRune(o Object) (v rune, ok bool) { + switch o := o.(type) { + case *Int: + v = rune(o.Value) + ok = true + case *Char: + v = o.Value + ok = true + } + return +} + +// ToByteSlice will try to convert object o to []byte value. +func ToByteSlice(o Object) (v []byte, ok bool) { + switch o := o.(type) { + case *Bytes: + v = o.Value + ok = true + case *String: + v = []byte(o.Value) + ok = true + } + return +} + +// ToTime will try to convert object o to time.Time value. +func ToTime(o Object) (v time.Time, ok bool) { + switch o := o.(type) { + case *Time: + v = o.Value + ok = true + case *Int: + v = time.Unix(o.Value, 0) + ok = true + } + return +} + +// ToInterface attempts to convert an object o to an interface{} value +func ToInterface(o Object) (res interface{}) { + switch o := o.(type) { + case *Int: + res = o.Value + case *String: + res = o.Value + case *Float: + res = o.Value + case *Bool: + res = o == TrueValue + case *Char: + res = o.Value + case *Bytes: + res = o.Value + case *Array: + res = make([]interface{}, len(o.Value)) + for i, val := range o.Value { + res.([]interface{})[i] = ToInterface(val) + } + case *ImmutableArray: + res = make([]interface{}, len(o.Value)) + for i, val := range o.Value { + res.([]interface{})[i] = ToInterface(val) + } + case *Map: + res = make(map[string]interface{}) + for key, v := range o.Value { + res.(map[string]interface{})[key] = ToInterface(v) + } + case *ImmutableMap: + res = make(map[string]interface{}) + for key, v := range o.Value { + res.(map[string]interface{})[key] = ToInterface(v) + } + case *Time: + res = o.Value + case *Error: + res = errors.New(o.String()) + case *Undefined: + res = nil + case Object: + return o + } + return +} + +// FromInterface will attempt to convert an interface{} v to a Tengo Object +func FromInterface(v interface{}) (Object, error) { + switch v := v.(type) { + case nil: + return UndefinedValue, nil + case string: + if len(v) > MaxStringLen { + return nil, ErrStringLimit + } + return &String{Value: v}, nil + case int64: + return &Int{Value: v}, nil + case int: + return &Int{Value: int64(v)}, nil + case bool: + if v { + return TrueValue, nil + } + return FalseValue, nil + case rune: + return &Char{Value: v}, nil + case byte: + return &Char{Value: rune(v)}, nil + case float64: + return &Float{Value: v}, nil + case []byte: + if len(v) > MaxBytesLen { + return nil, ErrBytesLimit + } + return &Bytes{Value: v}, nil + case error: + return &Error{Value: &String{Value: v.Error()}}, nil + case map[string]Object: + return &Map{Value: v}, nil + case map[string]interface{}: + kv := make(map[string]Object) + for vk, vv := range v { + vo, err := FromInterface(vv) + if err != nil { + return nil, err + } + kv[vk] = vo + } + return &Map{Value: kv}, nil + case []Object: + return &Array{Value: v}, nil + case []interface{}: + arr := make([]Object, len(v)) + for i, e := range v { + vo, err := FromInterface(e) + if err != nil { + return nil, err + } + arr[i] = vo + } + return &Array{Value: arr}, nil + case time.Time: + return &Time{Value: v}, nil + case Object: + return v, nil + case CallableFunc: + return &UserFunction{Value: v}, nil + } + return nil, fmt.Errorf("cannot convert to object: %T", v) +} diff --git a/vendor/github.com/d5/tengo/v2/token/token.go b/vendor/github.com/d5/tengo/v2/token/token.go new file mode 100644 index 00000000..4e6aa80a --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/token/token.go @@ -0,0 +1,225 @@ +package token + +import "strconv" + +var keywords map[string]Token + +// Token represents a token. +type Token int + +// List of tokens +const ( + Illegal Token = iota + EOF + Comment + _literalBeg + Ident + Int + Float + Char + String + _literalEnd + _operatorBeg + Add // + + Sub // - + Mul // * + Quo // / + Rem // % + And // & + Or // | + Xor // ^ + Shl // << + Shr // >> + AndNot // &^ + AddAssign // += + SubAssign // -= + MulAssign // *= + QuoAssign // /= + RemAssign // %= + AndAssign // &= + OrAssign // |= + XorAssign // ^= + ShlAssign // <<= + ShrAssign // >>= + AndNotAssign // &^= + LAnd // && + LOr // || + Inc // ++ + Dec // -- + Equal // == + Less // < + Greater // > + Assign // = + Not // ! + NotEqual // != + LessEq // <= + GreaterEq // >= + Define // := + Ellipsis // ... + LParen // ( + LBrack // [ + LBrace // { + Comma // , + Period // . + RParen // ) + RBrack // ] + RBrace // } + Semicolon // ; + Colon // : + Question // ? + _operatorEnd + _keywordBeg + Break + Continue + Else + For + Func + Error + Immutable + If + Return + Export + True + False + In + Undefined + Import + _keywordEnd +) + +var tokens = [...]string{ + Illegal: "ILLEGAL", + EOF: "EOF", + Comment: "COMMENT", + Ident: "IDENT", + Int: "INT", + Float: "FLOAT", + Char: "CHAR", + String: "STRING", + Add: "+", + Sub: "-", + Mul: "*", + Quo: "/", + Rem: "%", + And: "&", + Or: "|", + Xor: "^", + Shl: "<<", + Shr: ">>", + AndNot: "&^", + AddAssign: "+=", + SubAssign: "-=", + MulAssign: "*=", + QuoAssign: "/=", + RemAssign: "%=", + AndAssign: "&=", + OrAssign: "|=", + XorAssign: "^=", + ShlAssign: "<<=", + ShrAssign: ">>=", + AndNotAssign: "&^=", + LAnd: "&&", + LOr: "||", + Inc: "++", + Dec: "--", + Equal: "==", + Less: "<", + Greater: ">", + Assign: "=", + Not: "!", + NotEqual: "!=", + LessEq: "<=", + GreaterEq: ">=", + Define: ":=", + Ellipsis: "...", + LParen: "(", + LBrack: "[", + LBrace: "{", + Comma: ",", + Period: ".", + RParen: ")", + RBrack: "]", + RBrace: "}", + Semicolon: ";", + Colon: ":", + Question: "?", + Break: "break", + Continue: "continue", + Else: "else", + For: "for", + Func: "func", + Error: "error", + Immutable: "immutable", + If: "if", + Return: "return", + Export: "export", + True: "true", + False: "false", + In: "in", + Undefined: "undefined", + Import: "import", +} + +func (tok Token) String() string { + s := "" + + if 0 <= tok && tok < Token(len(tokens)) { + s = tokens[tok] + } + + if s == "" { + s = "token(" + strconv.Itoa(int(tok)) + ")" + } + + return s +} + +// LowestPrec represents lowest operator precedence. +const LowestPrec = 0 + +// Precedence returns the precedence for the operator token. +func (tok Token) Precedence() int { + switch tok { + case LOr: + return 1 + case LAnd: + return 2 + case Equal, NotEqual, Less, LessEq, Greater, GreaterEq: + return 3 + case Add, Sub, Or, Xor: + return 4 + case Mul, Quo, Rem, Shl, Shr, And, AndNot: + return 5 + } + return LowestPrec +} + +// IsLiteral returns true if the token is a literal. +func (tok Token) IsLiteral() bool { + return _literalBeg < tok && tok < _literalEnd +} + +// IsOperator returns true if the token is an operator. +func (tok Token) IsOperator() bool { + return _operatorBeg < tok && tok < _operatorEnd +} + +// IsKeyword returns true if the token is a keyword. +func (tok Token) IsKeyword() bool { + return _keywordBeg < tok && tok < _keywordEnd +} + +// Lookup returns corresponding keyword if ident is a keyword. +func Lookup(ident string) Token { + if tok, isKeyword := keywords[ident]; isKeyword { + return tok + } + return Ident +} + +func init() { + keywords = make(map[string]Token) + for i := _keywordBeg + 1; i < _keywordEnd; i++ { + keywords[tokens[i]] = i + } +} diff --git a/vendor/github.com/d5/tengo/v2/variable.go b/vendor/github.com/d5/tengo/v2/variable.go new file mode 100644 index 00000000..481b36b8 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/variable.go @@ -0,0 +1,136 @@ +package tengo + +import ( + "errors" +) + +// Variable is a user-defined variable for the script. +type Variable struct { + name string + value Object +} + +// NewVariable creates a Variable. +func NewVariable(name string, value interface{}) (*Variable, error) { + obj, err := FromInterface(value) + if err != nil { + return nil, err + } + return &Variable{ + name: name, + value: obj, + }, nil +} + +// Name returns the name of the variable. +func (v *Variable) Name() string { + return v.name +} + +// Value returns an empty interface of the variable value. +func (v *Variable) Value() interface{} { + return ToInterface(v.value) +} + +// ValueType returns the name of the value type. +func (v *Variable) ValueType() string { + return v.value.TypeName() +} + +// Int returns int value of the variable value. +// It returns 0 if the value is not convertible to int. +func (v *Variable) Int() int { + c, _ := ToInt(v.value) + return c +} + +// Int64 returns int64 value of the variable value. It returns 0 if the value +// is not convertible to int64. +func (v *Variable) Int64() int64 { + c, _ := ToInt64(v.value) + return c +} + +// Float returns float64 value of the variable value. It returns 0.0 if the +// value is not convertible to float64. +func (v *Variable) Float() float64 { + c, _ := ToFloat64(v.value) + return c +} + +// Char returns rune value of the variable value. It returns 0 if the value is +// not convertible to rune. +func (v *Variable) Char() rune { + c, _ := ToRune(v.value) + return c +} + +// Bool returns bool value of the variable value. It returns 0 if the value is +// not convertible to bool. +func (v *Variable) Bool() bool { + c, _ := ToBool(v.value) + return c +} + +// Array returns []interface value of the variable value. It returns 0 if the +// value is not convertible to []interface. +func (v *Variable) Array() []interface{} { + switch val := v.value.(type) { + case *Array: + var arr []interface{} + for _, e := range val.Value { + arr = append(arr, ToInterface(e)) + } + return arr + } + return nil +} + +// Map returns map[string]interface{} value of the variable value. It returns +// 0 if the value is not convertible to map[string]interface{}. +func (v *Variable) Map() map[string]interface{} { + switch val := v.value.(type) { + case *Map: + kv := make(map[string]interface{}) + for mk, mv := range val.Value { + kv[mk] = ToInterface(mv) + } + return kv + } + return nil +} + +// String returns string value of the variable value. It returns 0 if the value +// is not convertible to string. +func (v *Variable) String() string { + c, _ := ToString(v.value) + return c +} + +// Bytes returns a byte slice of the variable value. It returns nil if the +// value is not convertible to byte slice. +func (v *Variable) Bytes() []byte { + c, _ := ToByteSlice(v.value) + return c +} + +// Error returns an error if the underlying value is error object. If not, +// this returns nil. +func (v *Variable) Error() error { + err, ok := v.value.(*Error) + if ok { + return errors.New(err.String()) + } + return nil +} + +// Object returns an underlying Object of the variable value. Note that +// returned Object is a copy of an actual Object used in the script. +func (v *Variable) Object() Object { + return v.value +} + +// IsUndefined returns true if the underlying value is undefined. +func (v *Variable) IsUndefined() bool { + return v.value == UndefinedValue +} diff --git a/vendor/github.com/d5/tengo/v2/vm.go b/vendor/github.com/d5/tengo/v2/vm.go new file mode 100644 index 00000000..783a54a9 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/vm.go @@ -0,0 +1,883 @@ +package tengo + +import ( + "fmt" + "sync/atomic" + + "github.com/d5/tengo/v2/parser" + "github.com/d5/tengo/v2/token" +) + +// frame represents a function call frame. +type frame struct { + fn *CompiledFunction + freeVars []*ObjectPtr + ip int + basePointer int +} + +// VM is a virtual machine that executes the bytecode compiled by Compiler. +type VM struct { + constants []Object + stack [StackSize]Object + sp int + globals []Object + fileSet *parser.SourceFileSet + frames [MaxFrames]frame + framesIndex int + curFrame *frame + curInsts []byte + ip int + aborting int64 + maxAllocs int64 + allocs int64 + err error +} + +// NewVM creates a VM. +func NewVM( + bytecode *Bytecode, + globals []Object, + maxAllocs int64, +) *VM { + if globals == nil { + globals = make([]Object, GlobalsSize) + } + v := &VM{ + constants: bytecode.Constants, + sp: 0, + globals: globals, + fileSet: bytecode.FileSet, + framesIndex: 1, + ip: -1, + maxAllocs: maxAllocs, + } + v.frames[0].fn = bytecode.MainFunction + v.frames[0].ip = -1 + v.curFrame = &v.frames[0] + v.curInsts = v.curFrame.fn.Instructions + return v +} + +// Abort aborts the execution. +func (v *VM) Abort() { + atomic.StoreInt64(&v.aborting, 1) +} + +// Run starts the execution. +func (v *VM) Run() (err error) { + // reset VM states + v.sp = 0 + v.curFrame = &(v.frames[0]) + v.curInsts = v.curFrame.fn.Instructions + v.framesIndex = 1 + v.ip = -1 + v.allocs = v.maxAllocs + 1 + + v.run() + atomic.StoreInt64(&v.aborting, 0) + err = v.err + if err != nil { + filePos := v.fileSet.Position( + v.curFrame.fn.SourcePos(v.ip - 1)) + err = fmt.Errorf("Runtime Error: %s\n\tat %s", + err.Error(), filePos) + for v.framesIndex > 1 { + v.framesIndex-- + v.curFrame = &v.frames[v.framesIndex-1] + filePos = v.fileSet.Position( + v.curFrame.fn.SourcePos(v.curFrame.ip - 1)) + err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos) + } + return err + } + return nil +} + +func (v *VM) run() { + for atomic.LoadInt64(&v.aborting) == 0 { + v.ip++ + + switch v.curInsts[v.ip] { + case parser.OpConstant: + v.ip += 2 + cidx := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + + v.stack[v.sp] = v.constants[cidx] + v.sp++ + case parser.OpNull: + v.stack[v.sp] = UndefinedValue + v.sp++ + case parser.OpBinaryOp: + v.ip++ + right := v.stack[v.sp-1] + left := v.stack[v.sp-2] + tok := token.Token(v.curInsts[v.ip]) + res, e := left.BinaryOp(tok, right) + if e != nil { + v.sp -= 2 + if e == ErrInvalidOperator { + v.err = fmt.Errorf("invalid operation: %s %s %s", + left.TypeName(), tok.String(), right.TypeName()) + return + } + v.err = e + return + } + + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + + v.stack[v.sp-2] = res + v.sp-- + case parser.OpEqual: + right := v.stack[v.sp-1] + left := v.stack[v.sp-2] + v.sp -= 2 + if left.Equals(right) { + v.stack[v.sp] = TrueValue + } else { + v.stack[v.sp] = FalseValue + } + v.sp++ + case parser.OpNotEqual: + right := v.stack[v.sp-1] + left := v.stack[v.sp-2] + v.sp -= 2 + if left.Equals(right) { + v.stack[v.sp] = FalseValue + } else { + v.stack[v.sp] = TrueValue + } + v.sp++ + case parser.OpPop: + v.sp-- + case parser.OpTrue: + v.stack[v.sp] = TrueValue + v.sp++ + case parser.OpFalse: + v.stack[v.sp] = FalseValue + v.sp++ + case parser.OpLNot: + operand := v.stack[v.sp-1] + v.sp-- + if operand.IsFalsy() { + v.stack[v.sp] = TrueValue + } else { + v.stack[v.sp] = FalseValue + } + v.sp++ + case parser.OpBComplement: + operand := v.stack[v.sp-1] + v.sp-- + + switch x := operand.(type) { + case *Int: + var res Object = &Int{Value: ^x.Value} + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = res + v.sp++ + default: + v.err = fmt.Errorf("invalid operation: ^%s", + operand.TypeName()) + return + } + case parser.OpMinus: + operand := v.stack[v.sp-1] + v.sp-- + + switch x := operand.(type) { + case *Int: + var res Object = &Int{Value: -x.Value} + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = res + v.sp++ + case *Float: + var res Object = &Float{Value: -x.Value} + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = res + v.sp++ + default: + v.err = fmt.Errorf("invalid operation: -%s", + operand.TypeName()) + return + } + case parser.OpJumpFalsy: + v.ip += 2 + v.sp-- + if v.stack[v.sp].IsFalsy() { + pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + v.ip = pos - 1 + } + case parser.OpAndJump: + v.ip += 2 + if v.stack[v.sp-1].IsFalsy() { + pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + v.ip = pos - 1 + } else { + v.sp-- + } + case parser.OpOrJump: + v.ip += 2 + if v.stack[v.sp-1].IsFalsy() { + v.sp-- + } else { + pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + v.ip = pos - 1 + } + case parser.OpJump: + pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 + v.ip = pos - 1 + case parser.OpSetGlobal: + v.ip += 2 + v.sp-- + globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + v.globals[globalIndex] = v.stack[v.sp] + case parser.OpSetSelGlobal: + v.ip += 3 + globalIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8 + numSelectors := int(v.curInsts[v.ip]) + + // selectors and RHS value + selectors := make([]Object, numSelectors) + for i := 0; i < numSelectors; i++ { + selectors[i] = v.stack[v.sp-numSelectors+i] + } + val := v.stack[v.sp-numSelectors-1] + v.sp -= numSelectors + 1 + e := indexAssign(v.globals[globalIndex], val, selectors) + if e != nil { + v.err = e + return + } + case parser.OpGetGlobal: + v.ip += 2 + globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + val := v.globals[globalIndex] + v.stack[v.sp] = val + v.sp++ + case parser.OpArray: + v.ip += 2 + numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + + var elements []Object + for i := v.sp - numElements; i < v.sp; i++ { + elements = append(elements, v.stack[i]) + } + v.sp -= numElements + + var arr Object = &Array{Value: elements} + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + + v.stack[v.sp] = arr + v.sp++ + case parser.OpMap: + v.ip += 2 + numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + kv := make(map[string]Object) + for i := v.sp - numElements; i < v.sp; i += 2 { + key := v.stack[i] + value := v.stack[i+1] + kv[key.(*String).Value] = value + } + v.sp -= numElements + + var m Object = &Map{Value: kv} + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = m + v.sp++ + case parser.OpError: + value := v.stack[v.sp-1] + var e Object = &Error{ + Value: value, + } + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp-1] = e + case parser.OpImmutable: + value := v.stack[v.sp-1] + switch value := value.(type) { + case *Array: + var immutableArray Object = &ImmutableArray{ + Value: value.Value, + } + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp-1] = immutableArray + case *Map: + var immutableMap Object = &ImmutableMap{ + Value: value.Value, + } + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp-1] = immutableMap + } + case parser.OpIndex: + index := v.stack[v.sp-1] + left := v.stack[v.sp-2] + v.sp -= 2 + + val, err := left.IndexGet(index) + if err != nil { + if err == ErrNotIndexable { + v.err = fmt.Errorf("not indexable: %s", index.TypeName()) + return + } + if err == ErrInvalidIndexType { + v.err = fmt.Errorf("invalid index type: %s", + index.TypeName()) + return + } + v.err = err + return + } + if val == nil { + val = UndefinedValue + } + v.stack[v.sp] = val + v.sp++ + case parser.OpSliceIndex: + high := v.stack[v.sp-1] + low := v.stack[v.sp-2] + left := v.stack[v.sp-3] + v.sp -= 3 + + var lowIdx int64 + if low != UndefinedValue { + if low, ok := low.(*Int); ok { + lowIdx = low.Value + } else { + v.err = fmt.Errorf("invalid slice index type: %s", + low.TypeName()) + return + } + } + + switch left := left.(type) { + case *Array: + numElements := int64(len(left.Value)) + var highIdx int64 + if high == UndefinedValue { + highIdx = numElements + } else if high, ok := high.(*Int); ok { + highIdx = high.Value + } else { + v.err = fmt.Errorf("invalid slice index type: %s", + high.TypeName()) + return + } + if lowIdx > highIdx { + v.err = fmt.Errorf("invalid slice index: %d > %d", + lowIdx, highIdx) + return + } + if lowIdx < 0 { + lowIdx = 0 + } else if lowIdx > numElements { + lowIdx = numElements + } + if highIdx < 0 { + highIdx = 0 + } else if highIdx > numElements { + highIdx = numElements + } + var val Object = &Array{ + Value: left.Value[lowIdx:highIdx], + } + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = val + v.sp++ + case *ImmutableArray: + numElements := int64(len(left.Value)) + var highIdx int64 + if high == UndefinedValue { + highIdx = numElements + } else if high, ok := high.(*Int); ok { + highIdx = high.Value + } else { + v.err = fmt.Errorf("invalid slice index type: %s", + high.TypeName()) + return + } + if lowIdx > highIdx { + v.err = fmt.Errorf("invalid slice index: %d > %d", + lowIdx, highIdx) + return + } + if lowIdx < 0 { + lowIdx = 0 + } else if lowIdx > numElements { + lowIdx = numElements + } + if highIdx < 0 { + highIdx = 0 + } else if highIdx > numElements { + highIdx = numElements + } + var val Object = &Array{ + Value: left.Value[lowIdx:highIdx], + } + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = val + v.sp++ + case *String: + numElements := int64(len(left.Value)) + var highIdx int64 + if high == UndefinedValue { + highIdx = numElements + } else if high, ok := high.(*Int); ok { + highIdx = high.Value + } else { + v.err = fmt.Errorf("invalid slice index type: %s", + high.TypeName()) + return + } + if lowIdx > highIdx { + v.err = fmt.Errorf("invalid slice index: %d > %d", + lowIdx, highIdx) + return + } + if lowIdx < 0 { + lowIdx = 0 + } else if lowIdx > numElements { + lowIdx = numElements + } + if highIdx < 0 { + highIdx = 0 + } else if highIdx > numElements { + highIdx = numElements + } + var val Object = &String{ + Value: left.Value[lowIdx:highIdx], + } + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = val + v.sp++ + case *Bytes: + numElements := int64(len(left.Value)) + var highIdx int64 + if high == UndefinedValue { + highIdx = numElements + } else if high, ok := high.(*Int); ok { + highIdx = high.Value + } else { + v.err = fmt.Errorf("invalid slice index type: %s", + high.TypeName()) + return + } + if lowIdx > highIdx { + v.err = fmt.Errorf("invalid slice index: %d > %d", + lowIdx, highIdx) + return + } + if lowIdx < 0 { + lowIdx = 0 + } else if lowIdx > numElements { + lowIdx = numElements + } + if highIdx < 0 { + highIdx = 0 + } else if highIdx > numElements { + highIdx = numElements + } + var val Object = &Bytes{ + Value: left.Value[lowIdx:highIdx], + } + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = val + v.sp++ + } + case parser.OpCall: + numArgs := int(v.curInsts[v.ip+1]) + v.ip++ + value := v.stack[v.sp-1-numArgs] + if !value.CanCall() { + v.err = fmt.Errorf("not callable: %s", value.TypeName()) + return + } + if callee, ok := value.(*CompiledFunction); ok { + if callee.VarArgs { + // if the closure is variadic, + // roll up all variadic parameters into an array + realArgs := callee.NumParameters - 1 + varArgs := numArgs - realArgs + if varArgs >= 0 { + numArgs = realArgs + 1 + args := make([]Object, varArgs) + spStart := v.sp - varArgs + for i := spStart; i < v.sp; i++ { + args[i-spStart] = v.stack[i] + } + v.stack[spStart] = &Array{Value: args} + v.sp = spStart + 1 + } + } + if numArgs != callee.NumParameters { + if callee.VarArgs { + v.err = fmt.Errorf( + "wrong number of arguments: want>=%d, got=%d", + callee.NumParameters-1, numArgs) + } else { + v.err = fmt.Errorf( + "wrong number of arguments: want=%d, got=%d", + callee.NumParameters, numArgs) + } + return + } + + // test if it's tail-call + if callee == v.curFrame.fn { // recursion + nextOp := v.curInsts[v.ip+1] + if nextOp == parser.OpReturn || + (nextOp == parser.OpPop && + parser.OpReturn == v.curInsts[v.ip+2]) { + for p := 0; p < numArgs; p++ { + v.stack[v.curFrame.basePointer+p] = + v.stack[v.sp-numArgs+p] + } + v.sp -= numArgs + 1 + v.ip = -1 // reset IP to beginning of the frame + continue + } + } + if v.framesIndex >= MaxFrames { + v.err = ErrStackOverflow + return + } + + // update call frame + v.curFrame.ip = v.ip // store current ip before call + v.curFrame = &(v.frames[v.framesIndex]) + v.curFrame.fn = callee + v.curFrame.freeVars = callee.Free + v.curFrame.basePointer = v.sp - numArgs + v.curInsts = callee.Instructions + v.ip = -1 + v.framesIndex++ + v.sp = v.sp - numArgs + callee.NumLocals + } else { + var args []Object + args = append(args, v.stack[v.sp-numArgs:v.sp]...) + ret, e := value.Call(args...) + v.sp -= numArgs + 1 + + // runtime error + if e != nil { + if e == ErrWrongNumArguments { + v.err = fmt.Errorf( + "wrong number of arguments in call to '%s'", + value.TypeName()) + return + } + if e, ok := e.(ErrInvalidArgumentType); ok { + v.err = fmt.Errorf( + "invalid type for argument '%s' in call to '%s': "+ + "expected %s, found %s", + e.Name, value.TypeName(), e.Expected, e.Found) + return + } + v.err = e + return + } + + // nil return -> undefined + if ret == nil { + ret = UndefinedValue + } + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = ret + v.sp++ + } + case parser.OpReturn: + v.ip++ + var retVal Object + if int(v.curInsts[v.ip]) == 1 { + retVal = v.stack[v.sp-1] + } else { + retVal = UndefinedValue + } + //v.sp-- + v.framesIndex-- + v.curFrame = &v.frames[v.framesIndex-1] + v.curInsts = v.curFrame.fn.Instructions + v.ip = v.curFrame.ip + //v.sp = lastFrame.basePointer - 1 + v.sp = v.frames[v.framesIndex].basePointer + // skip stack overflow check because (newSP) <= (oldSP) + v.stack[v.sp-1] = retVal + //v.sp++ + case parser.OpDefineLocal: + v.ip++ + localIndex := int(v.curInsts[v.ip]) + sp := v.curFrame.basePointer + localIndex + + // local variables can be mutated by other actions + // so always store the copy of popped value + val := v.stack[v.sp-1] + v.sp-- + v.stack[sp] = val + case parser.OpSetLocal: + localIndex := int(v.curInsts[v.ip+1]) + v.ip++ + sp := v.curFrame.basePointer + localIndex + + // update pointee of v.stack[sp] instead of replacing the pointer + // itself. this is needed because there can be free variables + // referencing the same local variables. + val := v.stack[v.sp-1] + v.sp-- + if obj, ok := v.stack[sp].(*ObjectPtr); ok { + *obj.Value = val + val = obj + } + v.stack[sp] = val // also use a copy of popped value + case parser.OpSetSelLocal: + localIndex := int(v.curInsts[v.ip+1]) + numSelectors := int(v.curInsts[v.ip+2]) + v.ip += 2 + + // selectors and RHS value + selectors := make([]Object, numSelectors) + for i := 0; i < numSelectors; i++ { + selectors[i] = v.stack[v.sp-numSelectors+i] + } + val := v.stack[v.sp-numSelectors-1] + v.sp -= numSelectors + 1 + dst := v.stack[v.curFrame.basePointer+localIndex] + if obj, ok := dst.(*ObjectPtr); ok { + dst = *obj.Value + } + if e := indexAssign(dst, val, selectors); e != nil { + v.err = e + return + } + case parser.OpGetLocal: + v.ip++ + localIndex := int(v.curInsts[v.ip]) + val := v.stack[v.curFrame.basePointer+localIndex] + if obj, ok := val.(*ObjectPtr); ok { + val = *obj.Value + } + v.stack[v.sp] = val + v.sp++ + case parser.OpGetBuiltin: + v.ip++ + builtinIndex := int(v.curInsts[v.ip]) + v.stack[v.sp] = builtinFuncs[builtinIndex] + v.sp++ + case parser.OpClosure: + v.ip += 3 + constIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8 + numFree := int(v.curInsts[v.ip]) + fn, ok := v.constants[constIndex].(*CompiledFunction) + if !ok { + v.err = fmt.Errorf("not function: %s", fn.TypeName()) + return + } + free := make([]*ObjectPtr, numFree) + for i := 0; i < numFree; i++ { + switch freeVar := (v.stack[v.sp-numFree+i]).(type) { + case *ObjectPtr: + free[i] = freeVar + default: + free[i] = &ObjectPtr{ + Value: &v.stack[v.sp-numFree+i], + } + } + } + v.sp -= numFree + cl := &CompiledFunction{ + Instructions: fn.Instructions, + NumLocals: fn.NumLocals, + NumParameters: fn.NumParameters, + VarArgs: fn.VarArgs, + Free: free, + } + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = cl + v.sp++ + case parser.OpGetFreePtr: + v.ip++ + freeIndex := int(v.curInsts[v.ip]) + val := v.curFrame.freeVars[freeIndex] + v.stack[v.sp] = val + v.sp++ + case parser.OpGetFree: + v.ip++ + freeIndex := int(v.curInsts[v.ip]) + val := *v.curFrame.freeVars[freeIndex].Value + v.stack[v.sp] = val + v.sp++ + case parser.OpSetFree: + v.ip++ + freeIndex := int(v.curInsts[v.ip]) + *v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1] + v.sp-- + case parser.OpGetLocalPtr: + v.ip++ + localIndex := int(v.curInsts[v.ip]) + sp := v.curFrame.basePointer + localIndex + val := v.stack[sp] + var freeVar *ObjectPtr + if obj, ok := val.(*ObjectPtr); ok { + freeVar = obj + } else { + freeVar = &ObjectPtr{Value: &val} + v.stack[sp] = freeVar + } + v.stack[v.sp] = freeVar + v.sp++ + case parser.OpSetSelFree: + v.ip += 2 + freeIndex := int(v.curInsts[v.ip-1]) + numSelectors := int(v.curInsts[v.ip]) + + // selectors and RHS value + selectors := make([]Object, numSelectors) + for i := 0; i < numSelectors; i++ { + selectors[i] = v.stack[v.sp-numSelectors+i] + } + val := v.stack[v.sp-numSelectors-1] + v.sp -= numSelectors + 1 + e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, + val, selectors) + if e != nil { + v.err = e + return + } + case parser.OpIteratorInit: + var iterator Object + dst := v.stack[v.sp-1] + v.sp-- + if !dst.CanIterate() { + v.err = fmt.Errorf("not iterable: %s", dst.TypeName()) + return + } + iterator = dst.Iterate() + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } + v.stack[v.sp] = iterator + v.sp++ + case parser.OpIteratorNext: + iterator := v.stack[v.sp-1] + v.sp-- + hasMore := iterator.(Iterator).Next() + if hasMore { + v.stack[v.sp] = TrueValue + } else { + v.stack[v.sp] = FalseValue + } + v.sp++ + case parser.OpIteratorKey: + iterator := v.stack[v.sp-1] + v.sp-- + val := iterator.(Iterator).Key() + v.stack[v.sp] = val + v.sp++ + case parser.OpIteratorValue: + iterator := v.stack[v.sp-1] + v.sp-- + val := iterator.(Iterator).Value() + v.stack[v.sp] = val + v.sp++ + case parser.OpSuspend: + return + default: + v.err = fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip]) + return + } + } +} + +// IsStackEmpty tests if the stack is empty or not. +func (v *VM) IsStackEmpty() bool { + return v.sp == 0 +} + +func indexAssign(dst, src Object, selectors []Object) error { + numSel := len(selectors) + for sidx := numSel - 1; sidx > 0; sidx-- { + next, err := dst.IndexGet(selectors[sidx]) + if err != nil { + if err == ErrNotIndexable { + return fmt.Errorf("not indexable: %s", dst.TypeName()) + } + if err == ErrInvalidIndexType { + return fmt.Errorf("invalid index type: %s", + selectors[sidx].TypeName()) + } + return err + } + dst = next + } + + if err := dst.IndexSet(selectors[0], src); err != nil { + if err == ErrNotIndexAssignable { + return fmt.Errorf("not index-assignable: %s", dst.TypeName()) + } + if err == ErrInvalidIndexValueType { + return fmt.Errorf("invaid index value type: %s", src.TypeName()) + } + return err + } + return nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 22bb538c..32b59b59 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -25,19 +25,12 @@ github.com/Rhymen/go-whatsapp/crypto/curve25519 github.com/Rhymen/go-whatsapp/crypto/hkdf # github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43 github.com/bwmarrin/discordgo -# github.com/d5/tengo v1.24.8 -github.com/d5/tengo -github.com/d5/tengo/compiler -github.com/d5/tengo/compiler/ast -github.com/d5/tengo/compiler/parser -github.com/d5/tengo/compiler/scanner -github.com/d5/tengo/compiler/source -github.com/d5/tengo/compiler/token -github.com/d5/tengo/objects -github.com/d5/tengo/runtime -github.com/d5/tengo/script -github.com/d5/tengo/stdlib -github.com/d5/tengo/stdlib/json +# github.com/d5/tengo/v2 v2.0.2 +github.com/d5/tengo/v2 +github.com/d5/tengo/v2/parser +github.com/d5/tengo/v2/stdlib +github.com/d5/tengo/v2/stdlib/json +github.com/d5/tengo/v2/token # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew # github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec -- cgit v1.2.3