From 05a48d6a810b02f660264d65340662179e637479 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Tue, 10 Feb 2026 18:29:21 +0100 Subject: [PATCH 1/7] feat: add 'apply' to call a function with a set of arguments from a list --- CHANGELOG.md | 4 + include/Ark/Compiler/Common.hpp | 2 + include/Ark/Compiler/Instructions.hpp | 155 +++++++++-------- include/Ark/Compiler/Lowerer/ASTLowerer.hpp | 1 + include/Ark/VM/VM.inl | 2 +- lib/std | 2 +- .../Compiler/Lowerer/ASTLowerer.cpp | 34 +++- .../NameResolution/NameResolutionPass.cpp | 1 + src/arkreactor/VM/VM.cpp | 33 ++++ src/arkscript/REPL/Utils.cpp | 10 +- .../optimized_ir/fused_math_ops.expected | 162 +++++++++--------- .../runtime/apply_arity_error.ark | 4 + .../runtime/apply_arity_error.expected | 9 + .../typeChecking/apply_fun_num.ark | 4 + .../typeChecking/apply_fun_num.expected | 30 ++++ .../typeChecking/apply_num_list.ark | 4 + .../typeChecking/apply_num_list.expected | 30 ++++ .../resources/LangSuite/vm-tests.ark | 5 + 18 files changed, 327 insertions(+), 165 deletions(-) create mode 100644 tests/unittests/resources/DiagnosticsSuite/runtime/apply_arity_error.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/runtime/apply_arity_error.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_fun_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_fun_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_num_list.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_num_list.expected diff --git a/CHANGELOG.md b/CHANGELOG.md index 26543ffa..3a3492ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## [Unreleased changes] - 2026-XX-YY +### Added +- `apply` function: `(apply func [args...])`, to call a function with a set of arguments stored in a list. Works with functions, closures and builtins + ## [4.2.0] - 2026-02-04 ### Breaking changes - `assert` is no longer an instruction but a builtin diff --git a/include/Ark/Compiler/Common.hpp b/include/Ark/Compiler/Common.hpp index 1cfa0a72..5b23ba20 100644 --- a/include/Ark/Compiler/Common.hpp +++ b/include/Ark/Compiler/Common.hpp @@ -134,6 +134,8 @@ namespace Ark::internal constexpr std::string_view And = "and"; constexpr std::string_view Or = "or"; + constexpr std::string_view Apply = "apply"; + constexpr std::string_view Undef = "$undef"; constexpr std::string_view Symcat = "$symcat"; constexpr std::string_view Argcount = "$argcount"; diff --git a/include/Ark/Compiler/Instructions.hpp b/include/Ark/Compiler/Instructions.hpp index 99e63609..34dda4ab 100644 --- a/include/Ark/Compiler/Instructions.hpp +++ b/include/Ark/Compiler/Instructions.hpp @@ -171,283 +171,285 @@ namespace Ark::internal // @role Push the current page address as a value on the stack GET_CURRENT_PAGE_ADDR = 0x25, - FIRST_OPERATOR = 0x26, + APPLY = 0x26, + + FIRST_OPERATOR = 0x27, // @role Pop the top of the stack, if it's true, trigger the debugger - BREAKPOINT = 0x26, + BREAKPOINT = 0x27, // @role Push `TS1 + TS` - ADD = 0x27, + ADD = 0x28, // @role Push `TS1 - TS` - SUB = 0x28, + SUB = 0x29, // @role Push `TS1 * TS` - MUL = 0x29, + MUL = 0x2a, // @role Push `TS1 / TS` - DIV = 0x2a, + DIV = 0x2b, // @role Push `TS1 > TS` - GT = 0x2b, + GT = 0x2c, // @role Push `TS1 < TS` - LT = 0x2c, + LT = 0x2d, // @role Push `TS1 <= TS` - LE = 0x2d, + LE = 0x2e, // @role Push `TS1 >= TS` - GE = 0x2e, + GE = 0x2f, // @role Push `TS1 != TS` - NEQ = 0x2f, + NEQ = 0x30, // @role Push `TS1 == TS` - EQ = 0x30, + EQ = 0x31, // @role Push `len(TS)`, TS must be a list - LEN = 0x31, + LEN = 0x32, // @role Push `empty?(TS)`, TS must be a list or string - IS_EMPTY = 0x32, + IS_EMPTY = 0x33, // @role Push `tail(TS)`, all the elements of TS except the first one. TS must be a list or string - TAIL = 0x33, + TAIL = 0x34, // @role Push `head(TS)`, the first element of TS or nil if empty. TS must be a list or string - HEAD = 0x34, + HEAD = 0x35, // @role Push true if TS is nil, false otherwise - IS_NIL = 0x35, + IS_NIL = 0x36, // @role Convert TS to number (must be a string) - TO_NUM = 0x36, + TO_NUM = 0x37, // @role Convert TS to string - TO_STR = 0x37, + TO_STR = 0x38, // @role Push the value at index TS (must be a number) in TS1, which must be a list or string - AT = 0x38, + AT = 0x39, // @role Push the value at index TS (must be a number), inside the list or string at index TS1 (must be a number) in the list at TS2 - AT_AT = 0x39, + AT_AT = 0x3a, // @role Push `TS1 % TS` - MOD = 0x3a, + MOD = 0x3b, // @role Push the type of TS as a string - TYPE = 0x3b, + TYPE = 0x3c, // @role Check if TS1 is a closure field of TS. TS must be a Closure, TS1 a String - HAS_FIELD = 0x3c, + HAS_FIELD = 0x3d, // @role Push `!TS` - NOT = 0x3d, + NOT = 0x3e, // @args constant id, constant id // @role Load two consts (`primary` then `secondary`) on the stack in one instruction - LOAD_CONST_LOAD_CONST = 0x3e, + LOAD_CONST_LOAD_CONST = 0x3f, // @args constant id, symbol id // @role Load const `primary` into the symbol `secondary` (create a variable) - LOAD_CONST_STORE = 0x3f, + LOAD_CONST_STORE = 0x40, // @args constant id, symbol id // @role Load const `primary` into the symbol `secondary` (search for the variable with the given symbol id) - LOAD_CONST_SET_VAL = 0x40, + LOAD_CONST_SET_VAL = 0x41, // @args symbol id, symbol id // @role Store the value of the symbol `primary` into a new variable `secondary` - STORE_FROM = 0x41, + STORE_FROM = 0x42, // @args symbol index, symbol id // @role Store the value of the symbol `primary` into a new variable `secondary` - STORE_FROM_INDEX = 0x42, + STORE_FROM_INDEX = 0x43, // @args symbol id, symbol id // @role Store the value of the symbol `primary` into an existing variable `secondary` - SET_VAL_FROM = 0x43, + SET_VAL_FROM = 0x44, // @args symbol index, symbol id // @role Store the value of the symbol `primary` into an existing variable `secondary` - SET_VAL_FROM_INDEX = 0x44, + SET_VAL_FROM_INDEX = 0x45, // @args symbol id, count // @role Increment the variable `primary` by `count` and push its value on the stack - INCREMENT = 0x45, + INCREMENT = 0x46, // @args symbol index, count // @role Increment the variable `primary` by `count` and push its value on the stack - INCREMENT_BY_INDEX = 0x46, + INCREMENT_BY_INDEX = 0x47, // @args symbol id, count // @role Increment the variable `primary` by `count` and store its value in the given symbol id - INCREMENT_STORE = 0x47, + INCREMENT_STORE = 0x48, // @args symbol id, count // @role Decrement the variable `primary` by `count` and push its value on the stack - DECREMENT = 0x48, + DECREMENT = 0x49, // @args symbol index, count // @role Decrement the variable `primary` by `count` and push its value on the stack - DECREMENT_BY_INDEX = 0x49, + DECREMENT_BY_INDEX = 0x4a, // @args symbol id, count // @role Decrement the variable `primary` by `count` and store its value in the given symbol id - DECREMENT_STORE = 0x4a, + DECREMENT_STORE = 0x4b, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary` - STORE_TAIL = 0x4b, + STORE_TAIL = 0x4c, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary` - STORE_TAIL_BY_INDEX = 0x4c, + STORE_TAIL_BY_INDEX = 0x4d, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary` - STORE_HEAD = 0x4d, + STORE_HEAD = 0x4e, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary` - STORE_HEAD_BY_INDEX = 0x4e, + STORE_HEAD_BY_INDEX = 0x4f, // @args number, symbol id // @role Create a list of `number` elements, and store it in a new variable `secondary` - STORE_LIST = 0x4f, + STORE_LIST = 0x50, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary` - SET_VAL_TAIL = 0x50, + SET_VAL_TAIL = 0x51, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary` - SET_VAL_TAIL_BY_INDEX = 0x51, + SET_VAL_TAIL_BY_INDEX = 0x52, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary` - SET_VAL_HEAD = 0x52, + SET_VAL_HEAD = 0x53, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary` - SET_VAL_HEAD_BY_INDEX = 0x53, + SET_VAL_HEAD_BY_INDEX = 0x54, // @args builtin id, argument count // @role Call a builtin by its id in `primary`, with `secondary` arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack - CALL_BUILTIN = 0x54, + CALL_BUILTIN = 0x55, // @args builtin id, argument count // @role Call a builtin by its id in `primary`, with `secondary` arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack, as well as the return address removal - CALL_BUILTIN_WITHOUT_RETURN_ADDRESS = 0x55, + CALL_BUILTIN_WITHOUT_RETURN_ADDRESS = 0x56, // @args constant id, absolute address to jump to // @role Compare `TS < constant`, if the comparison fails, jump to the given address. Otherwise, does nothing - LT_CONST_JUMP_IF_FALSE = 0x56, + LT_CONST_JUMP_IF_FALSE = 0x57, // @args constant id, absolute address to jump to // @role Compare `TS < constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - LT_CONST_JUMP_IF_TRUE = 0x57, + LT_CONST_JUMP_IF_TRUE = 0x58, // @args symbol id, absolute address to jump to // @role Compare `TS < symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing - LT_SYM_JUMP_IF_FALSE = 0x58, + LT_SYM_JUMP_IF_FALSE = 0x59, // @args constant id, absolute address to jump to // @role Compare `TS > constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - GT_CONST_JUMP_IF_TRUE = 0x59, + GT_CONST_JUMP_IF_TRUE = 0x5a, // @args constant id, absolute address to jump to // @role Compare `TS > constant`, if the comparison fails, jump to the given address. Otherwise, does nothing - GT_CONST_JUMP_IF_FALSE = 0x5a, + GT_CONST_JUMP_IF_FALSE = 0x5b, // @args symbol id, absolute address to jump to // @role Compare `TS > symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing - GT_SYM_JUMP_IF_FALSE = 0x5b, + GT_SYM_JUMP_IF_FALSE = 0x5c, // @args constant id, absolute address to jump to // @role Compare `TS == constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - EQ_CONST_JUMP_IF_TRUE = 0x5c, + EQ_CONST_JUMP_IF_TRUE = 0x5d, // @args symbol index, absolute address to jump to // @role Compare `TS == symbol`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - EQ_SYM_INDEX_JUMP_IF_TRUE = 0x5d, + EQ_SYM_INDEX_JUMP_IF_TRUE = 0x5e, // @args constant id, absolute address to jump to // @role Compare `TS != constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - NEQ_CONST_JUMP_IF_TRUE = 0x5e, + NEQ_CONST_JUMP_IF_TRUE = 0x5f, // @args symbol id, absolute address to jump to // @role Compare `TS != symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing - NEQ_SYM_JUMP_IF_FALSE = 0x5f, + NEQ_SYM_JUMP_IF_FALSE = 0x60, // @args symbol id, argument count // @role Call a symbol by its id in `primary`, with `secondary` arguments - CALL_SYMBOL = 0x60, + CALL_SYMBOL = 0x61, // @args symbol id (function name), argument count // @role Call the current page with `secondary` arguments - CALL_CURRENT_PAGE = 0x61, + CALL_CURRENT_PAGE = 0x62, // @args symbol id, field id in symbols table // @role Push the field of a given symbol (which has to be a closure) on the stack - GET_FIELD_FROM_SYMBOL = 0x62, + GET_FIELD_FROM_SYMBOL = 0x63, // @args symbol index, field id in symbols table // @role Push the field of a given symbol (which has to be a closure) on the stack - GET_FIELD_FROM_SYMBOL_INDEX = 0x63, + GET_FIELD_FROM_SYMBOL_INDEX = 0x64, // @args symbol id, symbol id2 // @role Push symbol[symbol2] - AT_SYM_SYM = 0x64, + AT_SYM_SYM = 0x65, // @args symbol index, symbol index2 // @role Push symbol[symbol2] - AT_SYM_INDEX_SYM_INDEX = 0x65, + AT_SYM_INDEX_SYM_INDEX = 0x66, // @args symbol index, constant id // @role Push symbol[constant] - AT_SYM_INDEX_CONST = 0x66, + AT_SYM_INDEX_CONST = 0x67, // @args symbol id, constant id // @role Check that the type of symbol is the given constant, push true if so, false otherwise - CHECK_TYPE_OF = 0x67, + CHECK_TYPE_OF = 0x68, // @args symbol index, constant id // @role Check that the type of symbol is the given constant, push true if so, false otherwise - CHECK_TYPE_OF_BY_INDEX = 0x68, + CHECK_TYPE_OF_BY_INDEX = 0x69, // @args symbol id, number of elements // @role Append N elements to a reference to a list (symbol id), the list is being mutated in-place, no new object created. Elements are stored in TS(1)..TS(N). Follows the function calling convention - APPEND_IN_PLACE_SYM = 0x69, + APPEND_IN_PLACE_SYM = 0x6a, // @args symbol index, number of elements // @role Append N elements to a reference to a list (symbol index), the list is being mutated in-place, no new object created. Elements are stored in TS(1)..TS(N). Follows the function calling convention - APPEND_IN_PLACE_SYM_INDEX = 0x6a, + APPEND_IN_PLACE_SYM_INDEX = 0x6b, // @args symbol index, symbol id // @role Compute the length of the list or string at symbol index, and store it in a variable (symbol id) - STORE_LEN = 0x6b, + STORE_LEN = 0x6c, // @args symbol id, absolute address to jump to // @role Compute the length of a symbol (list or string), and pop TS to compare it, then jump if false - LT_LEN_SYM_JUMP_IF_FALSE = 0x6c, + LT_LEN_SYM_JUMP_IF_FALSE = 0x6d, // @args symbol id, offset number // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack - MUL_BY = 0x6d, + MUL_BY = 0x6e, // @args symbol index, offset number // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack - MUL_BY_INDEX = 0x6e, + MUL_BY_INDEX = 0x6f, // @args symbol id, offset number // @role Multiply the symbol by (offset symbol - 2048), then store the result using the given symbol id - MUL_SET_VAL = 0x6f, + MUL_SET_VAL = 0x70, // @args op1, op2, op3 // @role Pop 3 or 4 values from the stack, and apply the ops sequentially (only ADD, SUB, MUL, and DIV are supported). Push the result to the stack. Only op3 may be NOP. - FUSED_MATH = 0x70, + FUSED_MATH = 0x71, InstructionsCount }; @@ -491,6 +493,7 @@ namespace Ark::internal "RESET_SCOPE_JUMP", "POP_SCOPE", "GET_CURRENT_PAGE_ADDR", + "APPLY", // operators "BREAKPOINT", "ADD", diff --git a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp index f0f84008..2af2aaec 100644 --- a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp +++ b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp @@ -221,6 +221,7 @@ namespace Ark::internal void compileSymbol(const Node& x, Page p, bool is_result_unused, bool can_use_ref); void compileListInstruction(Node& x, Page p, bool is_result_unused); + void compileApplyInstruction(Node& x, Page p, bool is_result_unused); void compileIf(Node& x, Page p, bool is_result_unused, bool is_terminal); void compileFunction(Node& x, Page p, bool is_result_unused); void compileLetMutSet(Keyword n, Node& x, Page p); diff --git a/include/Ark/VM/VM.inl b/include/Ark/VM/VM.inl index 8791b9d4..bf2d5651 100644 --- a/include/Ark/VM/VM.inl +++ b/include/Ark/VM/VM.inl @@ -220,7 +220,7 @@ inline Value* VM::findNearestVariable(const uint16_t id, internal::ExecutionCont { for (auto it = context.locals.rbegin(), it_end = context.locals.rend(); it != it_end; ++it) { - if (const auto val = (*it)[id]; val != nullptr) + if (Value* val = (*it)[id]; val != nullptr) return val; } return nullptr; diff --git a/lib/std b/lib/std index bd5e4144..69ab3c56 160000 --- a/lib/std +++ b/lib/std @@ -1 +1 @@ -Subproject commit bd5e414440a4ef6bebf8b740b78a0dd549086620 +Subproject commit 69ab3c56405f15300cdc36b9190946dfcd24c77e diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index d441aa09..c88ee432 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -196,6 +196,9 @@ namespace Ark::internal // list instructions else if (const auto head = x.constList()[0]; head.nodeType() == NodeType::Symbol && getListInstruction(head.string()).has_value()) compileListInstruction(x, p, is_result_unused); + // todo: make a function "compile special form" for list and apply (and probably others in the future) + else if (const auto head2 = x.constList()[0]; head2.nodeType() == NodeType::Symbol && head2.string() == Language::Apply) + compileApplyInstruction(x, p, is_result_unused); // registering structures else if (x.constList()[0].nodeType() == NodeType::Keyword) { @@ -351,6 +354,32 @@ namespace Ark::internal } } + void ASTLowerer::compileApplyInstruction(Node& x, const Page p, const bool is_result_unused) + { + const Node head = x.constList()[0]; + const auto argc = x.constList().size() - 1u; + + if (argc != 2) + buildAndThrowError(fmt::format("Expected 2 arguments (function, arguments) for apply, got {}", argc), head); + + const auto label_return = IR::Entity::Label(m_current_label++); + page(p).emplace_back(IR::Entity::Goto(label_return, PUSH_RETURN_ADDRESS)); + + for (Node& node : x.list() | std::ranges::views::drop(1)) + { + if (nodeProducesOutput(node)) + compileExpression(node, p, false, false); + else + buildAndThrowError("Invalid node inside call to apply", node); + } + page(p).emplace_back(APPLY); + // patch the PUSH_RETURN_ADDRESS instruction with the return location (IP=CALL instruction IP) + page(p).emplace_back(label_return); + + if (is_result_unused) + page(p).emplace_back(POP); + } + void ASTLowerer::compileIf(Node& x, const Page p, const bool is_result_unused, const bool is_terminal) { if (x.constList().size() == 1) @@ -651,7 +680,7 @@ namespace Ark::internal } else if (!maybe_operator.has_value()) { - if (is_terminal && node.nodeType() == NodeType::Symbol && !m_opened_vars.empty() && m_opened_vars.top() == node.string()) + if (is_terminal && node.nodeType() == NodeType::Symbol && !m_opened_vars.empty() && m_opened_vars.top() == node.string()) // todo: see L671 { pushFunctionCallArguments(x, p, /* is_tail_call= */ true); @@ -668,7 +697,7 @@ namespace Ark::internal const auto proc_page = createNewCodePage(/* temp= */ true); // compile the function resolution to a separate page - if (node.nodeType() == NodeType::Symbol && !m_opened_vars.empty() && m_opened_vars.top() == node.string()) + if (node.nodeType() == NodeType::Symbol && !m_opened_vars.empty() && m_opened_vars.top() == node.string()) // todo: make a method to identify if the current function compiled is itself { // The function is trying to call itself, but this isn't a tail call. // We can skip the LOAD_FAST function_name and directly push the current @@ -739,6 +768,7 @@ namespace Ark::internal page(p).emplace_back(op); } + // todo: allow using operators with 0 or 1 argument, but push their builtin counterpart (todo as well) if (isBreakpoint(x)) { if (exp_count > 1) diff --git a/src/arkreactor/Compiler/NameResolution/NameResolutionPass.cpp b/src/arkreactor/Compiler/NameResolution/NameResolutionPass.cpp index 329ce11c..8ebd0b40 100644 --- a/src/arkreactor/Compiler/NameResolution/NameResolutionPass.cpp +++ b/src/arkreactor/Compiler/NameResolution/NameResolutionPass.cpp @@ -20,6 +20,7 @@ namespace Ark::internal m_language_symbols.emplace(Language::Or); m_language_symbols.emplace(Language::SysArgs); m_language_symbols.emplace(Language::SysProgramName); + m_language_symbols.emplace(Language::Apply); } void NameResolutionPass::process(const Node& ast) diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index a9e13404..7f818764 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -613,6 +613,7 @@ namespace Ark &&TARGET_RESET_SCOPE_JUMP, &&TARGET_POP_SCOPE, &&TARGET_GET_CURRENT_PAGE_ADDR, + &&TARGET_APPLY, &&TARGET_BREAKPOINT, &&TARGET_ADD, &&TARGET_SUB, @@ -1187,6 +1188,38 @@ namespace Ark DISPATCH(); } + TARGET(APPLY) + { + { + const Value args_list = *popAndResolveAsPtr(context), + func = *popAndResolveAsPtr(context); + if (args_list.valueType() != ValueType::List || !func.isFunction()) + { + throw types::TypeCheckingError( + "apply", + { { + types::Contract { + { types::Typedef("func", ValueType::PageAddr), + types::Typedef("args", ValueType::List) } }, + types::Contract { + { types::Typedef("func", ValueType::Closure), + types::Typedef("args", ValueType::List) } }, + types::Contract { + { types::Typedef("func", ValueType::CProc), + types::Typedef("args", ValueType::List) } }, + } }, + { func, args_list }); + } + + for (const Value& a : args_list.constList() | std::ranges::views::reverse) + push(a, context); + push(func, context); + + call(context, static_cast(args_list.constList().size())); + } + DISPATCH(); + } + #pragma endregion #pragma region "Operators" diff --git a/src/arkscript/REPL/Utils.cpp b/src/arkscript/REPL/Utils.cpp index bbe62d29..97172f9d 100644 --- a/src/arkscript/REPL/Utils.cpp +++ b/src/arkscript/REPL/Utils.cpp @@ -28,8 +28,9 @@ namespace Ark::internal return string; }); - output.emplace_back("and"); - output.emplace_back("or"); + output.emplace_back(Language::And); + output.emplace_back(Language::Or); + output.emplace_back(Language::Apply); return output; } @@ -59,8 +60,9 @@ namespace Ark::internal return std::make_pair(string, Replxx::Color::GREEN); }); - output.emplace_back("and", Replxx::Color::BRIGHTBLUE); - output.emplace_back("or", Replxx::Color::BRIGHTBLUE); + output.emplace_back(Language::And, Replxx::Color::BRIGHTBLUE); + output.emplace_back(Language::Or, Replxx::Color::BRIGHTBLUE); + output.emplace_back(Language::Apply, Replxx::Color::BRIGHTBLUE); output.emplace_back("[\\-|+]?[0-9]+(\\.[0-9]+)?", Replxx::Color::YELLOW); output.emplace_back("\".*\"", Replxx::Color::MAGENTA); diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected index d1926012..f1634e86 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected @@ -8,387 +8,387 @@ page_0 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 42 + FUSED_MATH 43, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 40, 42 + FUSED_MATH 43, 41, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 41, 42 + FUSED_MATH 43, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 39, 42 + FUSED_MATH 43, 40, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 41 + FUSED_MATH 43, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 40, 41 + FUSED_MATH 43, 41, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 41, 41 + FUSED_MATH 43, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 39, 41 + FUSED_MATH 43, 40, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 40 + FUSED_MATH 43, 43, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 40, 40 + FUSED_MATH 43, 41, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 41, 40 + FUSED_MATH 43, 42, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 39, 40 + FUSED_MATH 43, 40, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 39 + FUSED_MATH 43, 43, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 40, 39 + FUSED_MATH 43, 41, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 41, 39 + FUSED_MATH 43, 42, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 39, 39 + FUSED_MATH 43, 40, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 42, 42 + FUSED_MATH 42, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 40, 42 + FUSED_MATH 42, 41, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 41, 42 + FUSED_MATH 42, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 39, 42 + FUSED_MATH 42, 40, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 42, 41 + FUSED_MATH 42, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 40, 41 + FUSED_MATH 42, 41, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 41, 41 + FUSED_MATH 42, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 39, 41 + FUSED_MATH 42, 40, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 42, 40 + FUSED_MATH 42, 43, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 40, 40 + FUSED_MATH 42, 41, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 41, 40 + FUSED_MATH 42, 42, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 39, 40 + FUSED_MATH 42, 40, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 42, 39 + FUSED_MATH 42, 43, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 40, 39 + FUSED_MATH 42, 41, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 41, 39 + FUSED_MATH 42, 42, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 39, 39 + FUSED_MATH 42, 40, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 42, 42 + FUSED_MATH 41, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 40, 42 + FUSED_MATH 41, 41, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 41, 42 + FUSED_MATH 41, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 39, 42 + FUSED_MATH 41, 40, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 42, 41 + FUSED_MATH 41, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 40, 41 + FUSED_MATH 41, 41, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 41, 41 + FUSED_MATH 41, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 39, 41 + FUSED_MATH 41, 40, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 42, 40 + FUSED_MATH 41, 43, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 40, 40 + FUSED_MATH 41, 41, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 41, 40 + FUSED_MATH 41, 42, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 39, 40 + FUSED_MATH 41, 40, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 42, 39 + FUSED_MATH 41, 43, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 40, 39 + FUSED_MATH 41, 41, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 41, 39 + FUSED_MATH 41, 42, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 39, 39 + FUSED_MATH 41, 40, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 42, 42 + FUSED_MATH 40, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 40, 42 + FUSED_MATH 40, 41, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 41, 42 + FUSED_MATH 40, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 39, 42 + FUSED_MATH 40, 40, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 42, 41 + FUSED_MATH 40, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 40, 41 + FUSED_MATH 40, 41, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 41, 41 + FUSED_MATH 40, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 39, 41 + FUSED_MATH 40, 40, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 42, 40 + FUSED_MATH 40, 43, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 40, 40 + FUSED_MATH 40, 41, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 41, 40 + FUSED_MATH 40, 42, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 39, 40 + FUSED_MATH 40, 40, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 42, 39 + FUSED_MATH 40, 43, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 40, 39 + FUSED_MATH 40, 41, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 41, 39 + FUSED_MATH 40, 42, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 39, 39 + FUSED_MATH 40, 40, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 42, 0 + FUSED_MATH 43, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 40, 42, 0 + FUSED_MATH 41, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 41, 42, 0 + FUSED_MATH 42, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 39, 42, 0 + FUSED_MATH 40, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 41, 0 + FUSED_MATH 43, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 40, 41, 0 + FUSED_MATH 41, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 41, 41, 0 + FUSED_MATH 42, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 39, 41, 0 + FUSED_MATH 40, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 40, 0 + FUSED_MATH 43, 41, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 40, 40, 0 + FUSED_MATH 41, 41, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 41, 40, 0 + FUSED_MATH 42, 41, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 39, 40, 0 + FUSED_MATH 40, 41, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 39, 0 + FUSED_MATH 43, 40, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 40, 39, 0 + FUSED_MATH 41, 40, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 41, 39, 0 + FUSED_MATH 42, 40, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 39, 39, 0 + FUSED_MATH 40, 40, 0 LIST 80 CALL_BUILTIN 9, 1 .L0: - HALT 0 \ No newline at end of file + HALT 0 diff --git a/tests/unittests/resources/DiagnosticsSuite/runtime/apply_arity_error.ark b/tests/unittests/resources/DiagnosticsSuite/runtime/apply_arity_error.ark new file mode 100644 index 00000000..eb0f11d8 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/runtime/apply_arity_error.ark @@ -0,0 +1,4 @@ +(let foo (fun (a b c) { + (print (format "a={} b={} c={}" a b c)) + (+ a b c) })) +(print (apply foo [1 2])) diff --git a/tests/unittests/resources/DiagnosticsSuite/runtime/apply_arity_error.expected b/tests/unittests/resources/DiagnosticsSuite/runtime/apply_arity_error.expected new file mode 100644 index 00000000..808e63bf --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/runtime/apply_arity_error.expected @@ -0,0 +1,9 @@ +ArityError: When calling `(foo 1 2)', received 2 arguments, but expected 3: `(foo a b c)' + +In file tests/unittests/resources/DiagnosticsSuite/runtime/apply_arity_error.ark:4 + 1 | (let foo (fun (a b c) { + 2 | (print (format "a={} b={} c={}" a b c)) + 3 | (+ a b c) })) + 4 | (print (apply foo [1 2])) + | ^~~~~~~~~~~~~~~~~~~~~~~~ + 5 | diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_fun_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_fun_num.ark new file mode 100644 index 00000000..11729d75 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_fun_num.ark @@ -0,0 +1,4 @@ +(let foo (fun (a b c) { + (print (format "a={} b={} c={}" a b c)) + (+ a b c) })) +(print (apply foo 5)) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_fun_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_fun_num.expected new file mode 100644 index 00000000..8d6976eb --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_fun_num.expected @@ -0,0 +1,30 @@ +Function apply expected 2 arguments +Call + ↳ (apply Function@1 5) +Signature + ↳ (apply func args) +Arguments + → `func' (expected Function) ✓ + → `args' (expected List), got 5 (Number) + +Alternative 2: +Signature + ↳ (apply func args) +Arguments + → `func' (expected Closure), got Function@1 (Function) + → `args' (expected List), got 5 (Number) + +Alternative 3: +Signature + ↳ (apply func args) +Arguments + → `func' (expected CProc), got Function@1 (Function) + → `args' (expected List), got 5 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_fun_num.ark:4 + 1 | (let foo (fun (a b c) { + 2 | (print (format "a={} b={} c={}" a b c)) + 3 | (+ a b c) })) + 4 | (print (apply foo 5)) + | ^~~~~~~~~~~~~~~~~~~~ + 5 | diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_num_list.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_num_list.ark new file mode 100644 index 00000000..c7f8f69f --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_num_list.ark @@ -0,0 +1,4 @@ +(let foo (fun (a b c) { + (print (format "a={} b={} c={}" a b c)) + (+ a b c) })) +(print (apply 5 [1 2 3])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_num_list.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_num_list.expected new file mode 100644 index 00000000..e33b9ff7 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_num_list.expected @@ -0,0 +1,30 @@ +Function apply expected 2 arguments +Call + ↳ (apply 5 [1 2 3]) +Signature + ↳ (apply func args) +Arguments + → `func' (expected Function), got 5 (Number) + → `args' (expected List) ✓ + +Alternative 2: +Signature + ↳ (apply func args) +Arguments + → `func' (expected Closure), got 5 (Number) + → `args' (expected List) ✓ + +Alternative 3: +Signature + ↳ (apply func args) +Arguments + → `func' (expected CProc), got 5 (Number) + → `args' (expected List) ✓ + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/apply_num_list.ark:4 + 1 | (let foo (fun (a b c) { + 2 | (print (format "a={} b={} c={}" a b c)) + 3 | (+ a b c) })) + 4 | (print (apply 5 [1 2 3])) + | ^~~~~~~~~~~~~~~~~~~~~~~~ + 5 | diff --git a/tests/unittests/resources/LangSuite/vm-tests.ark b/tests/unittests/resources/LangSuite/vm-tests.ark index ffe332da..cded8a02 100644 --- a/tests/unittests/resources/LangSuite/vm-tests.ark +++ b/tests/unittests/resources/LangSuite/vm-tests.ark @@ -2,6 +2,7 @@ (let tests 0) (let foo 12) +(let bar (fun (a b c) [a b c])) (let closure (fun (&tests &foo) ())) (let make (fun (a b c) (fun (&a &b &c) ()))) @@ -108,6 +109,10 @@ (test:eq (toString ["12"]) "[\"12\"]") (test:eq (toString (dict "key" "value")) "{key: value}") }) + (test:case "apply" { + (test:eq (apply bar [1 2 3]) [1 2 3]) + (test:eq (apply (fun () 1) []) 1) }) + (test:case "indexing" { (test:eq (@ "hello" 1) "e") (test:eq (@ "hello" -1) "o") From 9ddb5921df8079d06de6e7e71e969e7267791d1c Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Tue, 10 Feb 2026 19:45:05 +0100 Subject: [PATCH 2/7] refacto(ast lowerer): add method to check for recursive self call --- include/Ark/Compiler/Lowerer/ASTLowerer.hpp | 11 +++++++++++ src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp index 2af2aaec..51c2ca4a 100644 --- a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp +++ b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp @@ -135,6 +135,17 @@ namespace Ark::internal return m_temp_pages[page.index]; } + /** + * @brief Check if we are in a recursive self call + * + * @param name symbol name being compiled + * @return true if the name passed is the name of the last function we entered + */ + [[nodiscard]] bool isFunctionCallingItself(const std::string& name) noexcept + { + return !m_opened_vars.empty() && m_opened_vars.top() == name; + } + /** * @brief Checking if a symbol is an operator * diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index c88ee432..38817d53 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -680,7 +680,7 @@ namespace Ark::internal } else if (!maybe_operator.has_value()) { - if (is_terminal && node.nodeType() == NodeType::Symbol && !m_opened_vars.empty() && m_opened_vars.top() == node.string()) // todo: see L671 + if (is_terminal && node.nodeType() == NodeType::Symbol && isFunctionCallingItself(node.string())) { pushFunctionCallArguments(x, p, /* is_tail_call= */ true); @@ -697,7 +697,7 @@ namespace Ark::internal const auto proc_page = createNewCodePage(/* temp= */ true); // compile the function resolution to a separate page - if (node.nodeType() == NodeType::Symbol && !m_opened_vars.empty() && m_opened_vars.top() == node.string()) // todo: make a method to identify if the current function compiled is itself + if (node.nodeType() == NodeType::Symbol && isFunctionCallingItself(node.string())) { // The function is trying to call itself, but this isn't a tail call. // We can skip the LOAD_FAST function_name and directly push the current From 80371e6cecbf3f7a1d0e425f6e7606bf80158689 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Tue, 10 Feb 2026 22:11:55 +0100 Subject: [PATCH 3/7] feat(compiler, builtins): ARK-331, add corresponding builtins for the operators, so that we can compile operators to their corresponding builtins and use them as functions in list:map and others --- CHANGELOG.md | 1 + include/Ark/Builtins/Builtins.hpp | 26 ++ include/Ark/Compiler/Lowerer/ASTLowerer.hpp | 14 +- include/Ark/VM/Helpers.hpp | 183 +++++++++ src/arkreactor/Builtins/Builtins.cpp | 26 +- src/arkreactor/Builtins/Operators.cpp | 377 ++++++++++++++++++ .../Compiler/Lowerer/ASTLowerer.cpp | 45 +-- src/arkreactor/VM/VM.cpp | 160 +------- src/arkscript/REPL/Utils.cpp | 25 +- .../resources/CompilerSuite/ir/operators.ark | 26 ++ .../CompilerSuite/ir/operators.expected | 72 ++++ .../ir/operators_as_builtins.ark | 1 + .../ir/operators_as_builtins.expected | 28 ++ .../compileTime/freestanding.ark | 2 - .../compileTime/freestanding.expected | 6 - .../compileTime/freestanding_breakpoint.ark | 1 + .../freestanding_breakpoint.expected | 5 + .../compileTime/freestanding_hasfield.ark | 1 + .../freestanding_hasfield.expected | 5 + .../typeChecking/builtin_add_nil.ark | 1 + .../typeChecking/builtin_add_nil.expected | 20 + .../typeChecking/builtin_add_num_nil.ark | 1 + .../typeChecking/builtin_add_num_nil.expected | 24 ++ .../typeChecking/builtin_add_num_num_nil.ark | 1 + .../builtin_add_num_num_nil.expected | 26 ++ .../typeChecking/builtin_at_num_zero.ark | 1 + .../typeChecking/builtin_at_num_zero.expected | 20 + .../typeChecking/builtin_atat_num_zero.ark | 1 + .../builtin_atat_num_zero.expected | 14 + .../typeChecking/builtin_div_nil.ark | 1 + .../typeChecking/builtin_div_nil.expected | 13 + .../typeChecking/builtin_div_num_nil.ark | 1 + .../typeChecking/builtin_div_num_nil.expected | 15 + .../typeChecking/builtin_div_num_num_nil.ark | 1 + .../builtin_div_num_num_nil.expected | 16 + .../typeChecking/builtin_div_num_zero.ark | 1 + .../builtin_div_num_zero.expected | 6 + .../typeChecking/builtin_eq_num.ark | 1 + .../typeChecking/builtin_eq_num.expected | 13 + .../typeChecking/builtin_ge_num.ark | 1 + .../typeChecking/builtin_ge_num.expected | 13 + .../typeChecking/builtin_gt_num.ark | 1 + .../typeChecking/builtin_gt_num.expected | 13 + .../typeChecking/builtin_head_num.ark | 1 + .../typeChecking/builtin_head_num.expected | 18 + .../typeChecking/builtin_head_num_num.ark | 1 + .../builtin_head_num_num.expected | 20 + .../typeChecking/builtin_isempty_num.ark | 1 + .../typeChecking/builtin_isempty_num.expected | 30 ++ .../typeChecking/builtin_isempty_num_num.ark | 1 + .../builtin_isempty_num_num.expected | 34 ++ .../typeChecking/builtin_isnil_num_num.ark | 1 + .../builtin_isnil_num_num.expected | 13 + .../typeChecking/builtin_le_num.ark | 1 + .../typeChecking/builtin_le_num.expected | 13 + .../typeChecking/builtin_len_num.ark | 1 + .../typeChecking/builtin_len_num.expected | 24 ++ .../typeChecking/builtin_len_num_num.ark | 1 + .../typeChecking/builtin_len_num_num.expected | 27 ++ .../typeChecking/builtin_lt_num.ark | 1 + .../typeChecking/builtin_lt_num.expected | 13 + .../typeChecking/builtin_mod_num_nil.ark | 1 + .../typeChecking/builtin_mod_num_nil.expected | 13 + .../typeChecking/builtin_mul_nil.ark | 1 + .../typeChecking/builtin_mul_nil.expected | 13 + .../typeChecking/builtin_mul_num_nil.ark | 1 + .../typeChecking/builtin_mul_num_nil.expected | 15 + .../typeChecking/builtin_mul_num_num_nil.ark | 1 + .../builtin_mul_num_num_nil.expected | 16 + .../typeChecking/builtin_neq_num.ark | 1 + .../typeChecking/builtin_neq_num.expected | 13 + .../typeChecking/builtin_not_num_num.ark | 1 + .../typeChecking/builtin_not_num_num.expected | 13 + .../typeChecking/builtin_sub_nil.ark | 1 + .../typeChecking/builtin_sub_nil.expected | 13 + .../typeChecking/builtin_sub_num_nil.ark | 1 + .../typeChecking/builtin_sub_num_nil.expected | 15 + .../typeChecking/builtin_sub_num_num_nil.ark | 1 + .../builtin_sub_num_num_nil.expected | 16 + .../typeChecking/builtin_tail_num.ark | 1 + .../typeChecking/builtin_tail_num.expected | 18 + .../typeChecking/builtin_tail_num_num.ark | 1 + .../builtin_tail_num_num.expected | 20 + .../typeChecking/builtin_tonum_nil.ark | 1 + .../typeChecking/builtin_tonum_nil.expected | 12 + .../typeChecking/builtin_tonum_num_num.ark | 1 + .../builtin_tonum_num_num.expected | 13 + .../typeChecking/builtin_tostr_num_num.ark | 1 + .../builtin_tostr_num_num.expected | 13 + .../typeChecking/builtin_type_num_zero.ark | 1 + .../builtin_type_num_zero.expected | 13 + .../resources/LangSuite/operators-tests.ark | 191 +++++++++ .../resources/LangSuite/unittests.ark | 2 + 93 files changed, 1634 insertions(+), 200 deletions(-) create mode 100644 include/Ark/VM/Helpers.hpp create mode 100644 src/arkreactor/Builtins/Operators.cpp create mode 100644 tests/unittests/resources/CompilerSuite/ir/operators.ark create mode 100644 tests/unittests/resources/CompilerSuite/ir/operators.expected create mode 100644 tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.ark create mode 100644 tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected delete mode 100644 tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding.ark delete mode 100644 tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_breakpoint.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_breakpoint.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_hasfield.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_hasfield.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_num_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_num_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_at_num_zero.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_at_num_zero.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_atat_num_zero.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_atat_num_zero.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_num_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_num_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_zero.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_zero.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_eq_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_eq_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_ge_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_ge_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_gt_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_gt_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isnil_num_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isnil_num_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_le_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_le_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_lt_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_lt_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mod_num_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mod_num_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_num_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_num_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_neq_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_neq_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_not_num_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_not_num_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_num_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_num_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_nil.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_nil.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_num_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_num_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tostr_num_num.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tostr_num_num.expected create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_type_num_zero.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_type_num_zero.expected create mode 100644 tests/unittests/resources/LangSuite/operators-tests.ark diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a3492ff..f27e804f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased changes] - 2026-XX-YY ### Added - `apply` function: `(apply func [args...])`, to call a function with a set of arguments stored in a list. Works with functions, closures and builtins +- `+`, `-`, `*`, `/` and many other operators can now be passed around, like builtins. This now works: `(list:reduce [1 2 3] +)`, where before we would get a compile time error about a "freestanding operator '+'" ## [4.2.0] - 2026-02-04 ### Breaking changes diff --git a/include/Ark/Builtins/Builtins.hpp b/include/Ark/Builtins/Builtins.hpp index d87b6d1c..9ce02da2 100644 --- a/include/Ark/Builtins/Builtins.hpp +++ b/include/Ark/Builtins/Builtins.hpp @@ -136,6 +136,32 @@ namespace Ark::internal::Builtins { ARK_BUILTIN(disassemble); } + + namespace Operators + { + ARK_BUILTIN(add); + ARK_BUILTIN(sub); + ARK_BUILTIN(mul); + ARK_BUILTIN(div); + ARK_BUILTIN(mod); + ARK_BUILTIN(toNumber); + ARK_BUILTIN(toString); + ARK_BUILTIN(lessThan); + ARK_BUILTIN(lessOrEq); + ARK_BUILTIN(greaterThan); + ARK_BUILTIN(greaterOrEq); + ARK_BUILTIN(eq); + ARK_BUILTIN(notEq); + ARK_BUILTIN(not_); + ARK_BUILTIN(len); + ARK_BUILTIN(isEmpty); + ARK_BUILTIN(isNil); + ARK_BUILTIN(tail); + ARK_BUILTIN(head); + ARK_BUILTIN(at); + ARK_BUILTIN(atAt); + ARK_BUILTIN(type); + } } #undef ARK_BUILTIN diff --git a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp index 51c2ca4a..9e9fddc4 100644 --- a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp +++ b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp @@ -190,8 +190,7 @@ namespace Ark::internal * @brief Check if a given instruction is unary (takes only one argument) * * @param inst - * @return true the instruction is unary - * @return false + * @return true the instruction is unary, false otherwise */ static bool isUnaryInst(Instruction inst) noexcept; @@ -199,11 +198,18 @@ namespace Ark::internal * @brief Check if a given instruction is ternary (takes three arguments) * * @param inst - * @return true the instruction is ternary - * @return false + * @return true the instruction is ternary, false otherwise */ static bool isTernaryInst(Instruction inst) noexcept; + /** + * @brief Check if an operator can be repeated + * + * @param inst + * @return true the instruction can be repeated, eg (+ 1 2 3) compiles to (+ (+ 1 2) 3), false otherwise + */ + static bool isRepeatableOperation(Instruction inst) noexcept; + /** * @brief Display a warning message * diff --git a/include/Ark/VM/Helpers.hpp b/include/Ark/VM/Helpers.hpp new file mode 100644 index 00000000..8c56f332 --- /dev/null +++ b/include/Ark/VM/Helpers.hpp @@ -0,0 +1,183 @@ +/** + * @file Helpers.hpp + * @author Lexy Plateau (lexplt.dev@gmail.com) + * @brief Helpers for the VM + * @date 2026-02-11 + * + * @copyright Copyright (c) 2026-02-11 + * + */ + +#ifndef ARK_VM_HELPERS_HPP +#define ARK_VM_HELPERS_HPP + +#include +#include +#include +#include + +namespace Ark::helper +{ + using namespace internal; + + inline Value tail(Value* a) + { + if (a->valueType() == ValueType::List) + { + if (a->constList().size() < 2) + return Value(ValueType::List); + + std::vector tmp(a->constList().size() - 1); + for (std::size_t i = 1, end = a->constList().size(); i < end; ++i) + tmp[i - 1] = a->constList()[i]; + return Value(std::move(tmp)); + } + if (a->valueType() == ValueType::String) + { + if (a->string().size() < 2) + return Value(ValueType::String); + + Value b { *a }; + b.stringRef().erase(b.stringRef().begin()); + return b; + } + + throw types::TypeCheckingError( + "tail", + { { types::Contract { { types::Typedef("value", ValueType::List) } }, + types::Contract { { types::Typedef("value", ValueType::String) } } } }, + { *a }); + } + + inline Value head(Value* a) + { + if (a->valueType() == ValueType::List) + { + if (a->constList().empty()) + return Builtins::nil; + return a->constList()[0]; + } + if (a->valueType() == ValueType::String) + { + if (a->string().empty()) + return Value(ValueType::String); + return Value(std::string(1, a->stringRef()[0])); + } + + throw types::TypeCheckingError( + "head", + { { types::Contract { { types::Typedef("value", ValueType::List) } }, + types::Contract { { types::Typedef("value", ValueType::String) } } } }, + { *a }); + } + + inline Value at(Value& container, Value& index, VM& vm) + { + if (index.valueType() != ValueType::Number) + throw types::TypeCheckingError( + "@", + { { types::Contract { { types::Typedef("src", ValueType::List), types::Typedef("idx", ValueType::Number) } }, + types::Contract { { types::Typedef("src", ValueType::String), types::Typedef("idx", ValueType::Number) } } } }, + { container, index }); + + const auto num = static_cast(index.number()); + + if (container.valueType() == ValueType::List) + { + const auto i = static_cast(num < 0 ? static_cast(container.list().size()) + num : num); + if (i < container.list().size()) + return container.list()[i]; + else + VM::throwVMError( + ErrorKind::Index, + fmt::format("{} out of range {} (length {})", num, container.toString(vm), container.list().size())); + } + else if (container.valueType() == ValueType::String) + { + const auto i = static_cast(num < 0 ? static_cast(container.string().size()) + num : num); + if (i < container.string().size()) + return Value(std::string(1, container.string()[i])); + else + VM::throwVMError( + ErrorKind::Index, + fmt::format("{} out of range \"{}\" (length {})", num, container.string(), container.string().size())); + } + else + throw types::TypeCheckingError( + "@", + { { types::Contract { { types::Typedef("src", ValueType::List), types::Typedef("idx", ValueType::Number) } }, + types::Contract { { types::Typedef("src", ValueType::String), types::Typedef("idx", ValueType::Number) } } } }, + { container, index }); + } + + inline Value atAt(const Value* x, const Value* y, Value& list) + { + if (y->valueType() != ValueType::Number || x->valueType() != ValueType::Number || + list.valueType() != ValueType::List) + throw types::TypeCheckingError( + "@@", + { { types::Contract { + { types::Typedef("src", ValueType::List), + types::Typedef("y", ValueType::Number), + types::Typedef("x", ValueType::Number) } } } }, + { list, *y, *x }); + + long idx_y = static_cast(y->number()); + idx_y = idx_y < 0 ? static_cast(list.list().size()) + idx_y : idx_y; + if (std::cmp_greater_equal(idx_y, list.list().size()) || idx_y < 0) + VM::throwVMError( + ErrorKind::Index, + fmt::format("@@ index ({}) out of range (list size: {})", idx_y, list.list().size())); + + const bool is_list = list.list()[static_cast(idx_y)].valueType() == ValueType::List; + const std::size_t size = + is_list + ? list.list()[static_cast(idx_y)].list().size() + : list.list()[static_cast(idx_y)].stringRef().size(); + + long idx_x = static_cast(x->number()); + idx_x = idx_x < 0 ? static_cast(size) + idx_x : idx_x; + if (std::cmp_greater_equal(idx_x, size) || idx_x < 0) + VM::throwVMError( + ErrorKind::Index, + fmt::format("@@ index (x: {}) out of range (inner indexable size: {})", idx_x, size)); + + if (is_list) + return list.list()[static_cast(idx_y)].list()[static_cast(idx_x)]; + else + return Value(std::string(1, list.list()[static_cast(idx_y)].stringRef()[static_cast(idx_x)])); + } + + inline double doMath(double a, double b, const Instruction op) + { + if (op == ADD) + a += b; + else if (op == SUB) + a -= b; + else if (op == MUL) + a *= b; + else if (op == DIV) + { + if (b == 0) + Ark::VM::throwVMError(ErrorKind::DivisionByZero, fmt::format("Can not compute expression (/ {} {})", a, b)); + a /= b; + } + + return a; + } + + inline std::string mathInstToStr(const Instruction op) + { + if (op == ADD) + return "+"; + if (op == SUB) + return "-"; + if (op == MUL) + return "*"; + if (op == DIV) + return "/"; + return "???"; + } +} + +#endif // ARK_VM_HELPERS_HPP diff --git a/src/arkreactor/Builtins/Builtins.cpp b/src/arkreactor/Builtins/Builtins.cpp index 4738f896..f532194b 100644 --- a/src/arkreactor/Builtins/Builtins.cpp +++ b/src/arkreactor/Builtins/Builtins.cpp @@ -108,6 +108,30 @@ namespace Ark::internal::Builtins { "builtin__dict:size", Value(Dict::size) }, // Bytecode - { "disassemble", Value(Bytecode::disassemble) } + { "disassemble", Value(Bytecode::disassemble) }, + + // Operators that can also be used as builtins + { "+", Value(Operators::add) }, + { "-", Value(Operators::sub) }, + { "*", Value(Operators::mul) }, + { "/", Value(Operators::div) }, + { "mod", Value(Operators::mod) }, + { "toNumber", Value(Operators::toNumber) }, + { "toString", Value(Operators::toString) }, + { "<", Value(Operators::lessThan) }, + { "<=", Value(Operators::lessOrEq) }, + { ">", Value(Operators::greaterThan) }, + { ">=", Value(Operators::greaterOrEq) }, + { "=", Value(Operators::eq) }, + { "!=", Value(Operators::notEq) }, + { "not", Value(Operators::not_) }, + { "len", Value(Operators::len) }, + { "empty?", Value(Operators::isEmpty) }, + { "nil?", Value(Operators::isNil) }, + { "tail", Value(Operators::tail) }, + { "head", Value(Operators::head) }, + { "@", Value(Operators::at) }, + { "@@", Value(Operators::atAt) }, + { "type", Value(Operators::type) } }; } diff --git a/src/arkreactor/Builtins/Operators.cpp b/src/arkreactor/Builtins/Operators.cpp new file mode 100644 index 00000000..e6ccee14 --- /dev/null +++ b/src/arkreactor/Builtins/Operators.cpp @@ -0,0 +1,377 @@ +#include + +#include +#include +#include +#include +#include +#include + +namespace Ark::internal::Builtins::Operators +{ + // cppcheck-suppress constParameterReference + Value add(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.empty()) + throw types::TypeCheckingError( + "+", + { { types::Contract { { types::Typedef("a", ValueType::Number, /* is_variadic= */ true) } }, + types::Contract { { types::Typedef("a", ValueType::String, /* is_variadic= */ true) } } } }, + n); + + if (n[0].valueType() == ValueType::Number) + { + double output = 0.0; + for (const Value& num : n) + { + if (num.valueType() != ValueType::Number) + throw types::TypeCheckingError( + "+", + { { types::Contract { { types::Typedef("a", ValueType::Number, /* is_variadic= */ true) } }, + types::Contract { { types::Typedef("a", ValueType::String, /* is_variadic= */ true) } } } }, + n); + output += num.number(); + } + return Value(output); + } + + std::string output; + for (const Value& str : n) + { + if (str.valueType() != ValueType::String) + throw types::TypeCheckingError( + "+", + { { types::Contract { { types::Typedef("a", ValueType::Number, /* is_variadic= */ true) } }, + types::Contract { { types::Typedef("a", ValueType::String, /* is_variadic= */ true) } } } }, + n); + output += str.string(); + } + return Value(output); + } + + // cppcheck-suppress constParameterReference + Value sub(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.empty() || n[0].valueType() != ValueType::Number) + throw types::TypeCheckingError( + "-", + { { types::Contract { { types::Typedef("a", ValueType::Number, /* is_variadic= */ true) } } } }, n); + + double output = n[0].number(); + for (const Value& num : n | std::ranges::views::drop(1)) + { + if (num.valueType() != ValueType::Number) + throw types::TypeCheckingError( + "-", + { { types::Contract { { types::Typedef("a", ValueType::Number, /* is_variadic= */ true) } } } }, n); + output -= num.number(); + } + + return Value(output); + } + + // cppcheck-suppress constParameterReference + Value mul(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() < 2 || n[0].valueType() != ValueType::Number || n[1].valueType() != ValueType::Number) + throw types::TypeCheckingError( + "*", + { { types::Contract { { types::Typedef("a", ValueType::Number, /* is_variadic= */ true) } } } }, n); + + double output = n[0].number(); + for (const Value& num : n | std::ranges::views::drop(1)) + { + if (num.valueType() != ValueType::Number) + throw types::TypeCheckingError( + "*", + { { types::Contract { { types::Typedef("a", ValueType::Number, /* is_variadic= */ true) } } } }, n); + output *= num.number(); + } + + return Value(output); + } + + // cppcheck-suppress constParameterReference + Value div(std::vector& n, VM* vm) + { + if (n.size() < 2 || n[0].valueType() != ValueType::Number || n[1].valueType() != ValueType::Number) + throw types::TypeCheckingError( + "/", + { { types::Contract { { types::Typedef("a", ValueType::Number, /* is_variadic= */ true) } } } }, n); + + double output = n[0].number(); + for (const Value& num : n | std::ranges::views::drop(1)) + { + if (num.valueType() != ValueType::Number) + throw types::TypeCheckingError( + "/", + { { types::Contract { { types::Typedef("a", ValueType::Number, /* is_variadic= */ true) } } } }, n); + + if (num.number() == 0) + VM::throwVMError(ErrorKind::DivisionByZero, fmt::format("Can not compute expression (/ {} {})", n[0].toString(*vm), num.toString(*vm))); + + output /= num.number(); + } + + return Value(output); + } + + // cppcheck-suppress constParameterReference + Value mod(std::vector& n, VM* vm [[maybe_unused]]) + { + if (!types::check(n, ValueType::Number, ValueType::Number)) + throw types::TypeCheckingError( + "mod", + { { types::Contract { { types::Typedef("a", ValueType::Number), types::Typedef("b", ValueType::Number) } } } }, + n); + + return Value(std::fmod(n[0].number(), n[1].number())); + } + + // cppcheck-suppress constParameterReference + Value toNumber(std::vector& n, VM* vm [[maybe_unused]]) + { + if (!types::check(n, ValueType::String)) + throw types::TypeCheckingError( + "toNumber", + { { types::Contract { { types::Typedef("val", ValueType::String) } } } }, + n); + + double val; + if (Utils::isDouble(n[0].string(), &val)) + return Value(val); + return Nil; + } + + // cppcheck-suppress constParameterReference + Value toString(std::vector& n, VM* vm) + { + if (n.size() != 1) + throw types::TypeCheckingError( + "toString", + { { types::Contract { { types::Typedef("val", ValueType::Any) } } } }, + n); + + return Value(n[0].toString(*vm)); + } + + // cppcheck-suppress constParameterReference + Value lessThan(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 2) + throw types::TypeCheckingError( + "<", + { { types::Contract { { types::Typedef("lhs", ValueType::Any), types::Typedef("rhs", ValueType::Any) } } } }, + n); + + return n[0] < n[1] ? True : False; + } + + // cppcheck-suppress constParameterReference + Value lessOrEq(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 2) + throw types::TypeCheckingError( + "<=", + { { types::Contract { { types::Typedef("lhs", ValueType::Any), types::Typedef("rhs", ValueType::Any) } } } }, + n); + + return (n[0] < n[1] || n[0] == n[1]) ? True : False; + } + + // cppcheck-suppress constParameterReference + Value greaterThan(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 2) + throw types::TypeCheckingError( + ">", + { { types::Contract { { types::Typedef("lhs", ValueType::Any), types::Typedef("rhs", ValueType::Any) } } } }, + n); + + return n[1] < n[0] ? True : False; + } + + // cppcheck-suppress constParameterReference + Value greaterOrEq(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 2) + throw types::TypeCheckingError( + ">=", + { { types::Contract { { types::Typedef("lhs", ValueType::Any), types::Typedef("rhs", ValueType::Any) } } } }, + n); + + return (n[1] < n[0] || n[0] == n[1]) ? True : False; + } + + // cppcheck-suppress constParameterReference + Value eq(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 2) + throw types::TypeCheckingError( + "=", + { { types::Contract { { types::Typedef("lhs", ValueType::Any), types::Typedef("rhs", ValueType::Any) } } } }, + n); + + return n[0] == n[1] ? True : False; + } + + // cppcheck-suppress constParameterReference + Value notEq(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 2) + throw types::TypeCheckingError( + "!=", + { { types::Contract { { types::Typedef("lhs", ValueType::Any), types::Typedef("rhs", ValueType::Any) } } } }, + n); + + return n[0] != n[1] ? True : False; + } + + // cppcheck-suppress constParameterReference + Value not_(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 1) + throw types::TypeCheckingError( + "not", + { { types::Contract { { types::Typedef("val", ValueType::Any) } } } }, + n); + + return !n[0] ? True : False; + } + + // cppcheck-suppress constParameterReference + Value len(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 1) + throw types::TypeCheckingError( + "len", + { { types::Contract { { types::Typedef("value", ValueType::List) } }, + types::Contract { { types::Typedef("value", ValueType::String) } }, + types::Contract { { types::Typedef("value", ValueType::Dict) } } } }, + n); + + if (n[0].valueType() == ValueType::List) + return Value(static_cast(n[0].constList().size())); + if (n[0].valueType() == ValueType::String) + return Value(static_cast(n[0].string().size())); + if (n[0].valueType() == ValueType::Dict) + return Value(static_cast(n[0].dict().size())); + + throw types::TypeCheckingError( + "len", + { { types::Contract { { types::Typedef("value", ValueType::List) } }, + types::Contract { { types::Typedef("value", ValueType::String) } }, + types::Contract { { types::Typedef("value", ValueType::Dict) } } } }, + n); + } + + // cppcheck-suppress constParameterReference + Value isEmpty(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 1) + throw types::TypeCheckingError( + "empty?", + { { types::Contract { { types::Typedef("value", ValueType::List) } }, + types::Contract { { types::Typedef("value", ValueType::Nil) } }, + types::Contract { { types::Typedef("value", ValueType::String) } }, + types::Contract { { types::Typedef("value", ValueType::Dict) } } } }, + n); + + if (n[0].valueType() == ValueType::List) + return n[0].constList().empty() ? True : False; + if (n[0].valueType() == ValueType::String) + return n[0].string().empty() ? True : False; + if (n[0].valueType() == ValueType::Dict) + return std::cmp_equal(n[0].dict().size(), 0) ? True : False; + if (n[0].valueType() == ValueType::Nil) + return True; + + throw types::TypeCheckingError( + "empty?", + { { types::Contract { { types::Typedef("value", ValueType::List) } }, + types::Contract { { types::Typedef("value", ValueType::Nil) } }, + types::Contract { { types::Typedef("value", ValueType::String) } }, + types::Contract { { types::Typedef("value", ValueType::Dict) } } } }, + n); + } + + // cppcheck-suppress constParameterReference + Value isNil(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 1) + throw types::TypeCheckingError( + "nil?", + { { types::Contract { { types::Typedef("value", ValueType::Any) } } } }, + n); + + if (n[0] == Nil) + return True; + return False; + } + + // cppcheck-suppress constParameterReference + Value tail(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 1) + throw types::TypeCheckingError( + "tail", + { { types::Contract { { types::Typedef("value", ValueType::List) } }, + types::Contract { { types::Typedef("value", ValueType::String) } } } }, + n); + + return helper::tail(&n[0]); + } + + // cppcheck-suppress constParameterReference + Value head(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 1) + throw types::TypeCheckingError( + "head", + { { types::Contract { { types::Typedef("value", ValueType::List) } }, + types::Contract { { types::Typedef("value", ValueType::String) } } } }, + n); + + return helper::head(&n[0]); + } + + // cppcheck-suppress constParameterReference + Value at(std::vector& n, VM* vm) + { + if (n.size() != 2) + throw types::TypeCheckingError( + "@", + { { types::Contract { { types::Typedef("src", ValueType::List), types::Typedef("idx", ValueType::Number) } }, + types::Contract { { types::Typedef("src", ValueType::String), types::Typedef("idx", ValueType::Number) } } } }, + n); + + return helper::at(n[0], n[1], *vm); + } + + // cppcheck-suppress constParameterReference + Value atAt(std::vector& n, VM* vm [[maybe_unused]]) + { + if (!types::check(n, ValueType::List, ValueType::Number, ValueType::Number)) + throw types::TypeCheckingError( + "@@", + { { types::Contract { + { types::Typedef("src", ValueType::List), + types::Typedef("y", ValueType::Number), + types::Typedef("x", ValueType::Number) } } } }, + n); + + return helper::atAt(&n[2], &n[1], n[0]); + } + + // cppcheck-suppress constParameterReference + Value type(std::vector& n, VM* vm [[maybe_unused]]) + { + if (n.size() != 1) + throw types::TypeCheckingError( + "type", + { { types::Contract { { types::Typedef("value", ValueType::Any) } } } }, + n); + + return Value(std::to_string(n[0].valueType())); + } +} diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 38817d53..91890103 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -17,7 +17,7 @@ namespace Ark::internal ASTLowerer::ASTLowerer(const unsigned debug) : m_logger("ASTLowerer", debug) - {} + { } void ASTLowerer::addToTables(const std::vector& symbols, const std::vector& constants) { @@ -143,6 +143,21 @@ namespace Ark::internal } } + bool ASTLowerer::isRepeatableOperation(const Instruction inst) noexcept + { + switch (inst) + { + case ADD: [[fallthrough]]; + case SUB: [[fallthrough]]; + case MUL: [[fallthrough]]; + case DIV: + return true; + + default: + return false; + } + } + void ASTLowerer::warning(const std::string& message, const Node& node) { fmt::println("{} {}", fmt::styled("Warning", fmt::fg(fmt::color::dark_orange)), Diagnostics::makeContextWithNode(message, node)); @@ -268,7 +283,7 @@ namespace Ark::internal if (const auto it_builtin = getBuiltin(name)) page(p).emplace_back(Instruction::BUILTIN, it_builtin.value()); else if (getOperator(name).has_value()) - buildAndThrowError(fmt::format("Found a free standing operator: `{}`", name), x); + buildAndThrowError(fmt::format("Found a freestanding operator: `{}`. It can not be used as value like `+', where (let add +) (add 1 2) would be valid", name), x); else { if (can_use_ref) @@ -759,7 +774,7 @@ namespace Ark::internal else buildAndThrowError(fmt::format("Invalid node inside call to operator `{}'", node.repr()), x.constList()[index]); - if (!is_breakpoint && ((index + 1 < size && x.constList()[index + 1].nodeType() != NodeType::Capture) || index + 1 == size)) + if (!is_breakpoint) exp_count++; // in order to be able to handle things like (op A B C D...) @@ -768,7 +783,6 @@ namespace Ark::internal page(p).emplace_back(op); } - // todo: allow using operators with 0 or 1 argument, but push their builtin counterpart (todo as well) if (isBreakpoint(x)) { if (exp_count > 1) @@ -790,26 +804,11 @@ namespace Ark::internal else if (exp_count <= 1) buildAndThrowError(fmt::format("`{}' expected two arguments, but was called with {}", op_name, exp_count), x.constList()[0]); - page(p).back().setSourceLocation(x.filename(), x.position().start.line); - // need to check we didn't push the (op A B C D...) things for operators not supporting it - if (exp_count > 2) - { - switch (op) - { - // authorized instructions - case ADD: [[fallthrough]]; - case SUB: [[fallthrough]]; - case MUL: [[fallthrough]]; - case DIV: [[fallthrough]]; - case MOD: [[fallthrough]]; - case AT_AT: - break; - - default: - buildAndThrowError(fmt::format("`{}' requires 2 arguments, but got {}.", op_name, exp_count), x); - } - } + if (exp_count > 2 && !isRepeatableOperation(op) && !isTernaryInst(op)) + buildAndThrowError(fmt::format("`{}' requires 2 arguments, but got {}.", op_name, exp_count), x); + + page(p).back().setSourceLocation(x.filename(), x.position().start.line); } if (is_result_unused) diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 7f818764..2b1dac53 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -13,135 +13,12 @@ #include #include #include +#include namespace Ark { using namespace internal; - namespace helper - { - inline Value tail(Value* a) - { - if (a->valueType() == ValueType::List) - { - if (a->constList().size() < 2) - return Value(ValueType::List); - - std::vector tmp(a->constList().size() - 1); - for (std::size_t i = 1, end = a->constList().size(); i < end; ++i) - tmp[i - 1] = a->constList()[i]; - return Value(std::move(tmp)); - } - if (a->valueType() == ValueType::String) - { - if (a->string().size() < 2) - return Value(ValueType::String); - - Value b { *a }; - b.stringRef().erase(b.stringRef().begin()); - return b; - } - - throw types::TypeCheckingError( - "tail", - { { types::Contract { { types::Typedef("value", ValueType::List) } }, - types::Contract { { types::Typedef("value", ValueType::String) } } } }, - { *a }); - } - - inline Value head(Value* a) - { - if (a->valueType() == ValueType::List) - { - if (a->constList().empty()) - return Builtins::nil; - return a->constList()[0]; - } - if (a->valueType() == ValueType::String) - { - if (a->string().empty()) - return Value(ValueType::String); - return Value(std::string(1, a->stringRef()[0])); - } - - throw types::TypeCheckingError( - "head", - { { types::Contract { { types::Typedef("value", ValueType::List) } }, - types::Contract { { types::Typedef("value", ValueType::String) } } } }, - { *a }); - } - - inline Value at(Value& container, Value& index, VM& vm) - { - if (index.valueType() != ValueType::Number) - throw types::TypeCheckingError( - "@", - { { types::Contract { { types::Typedef("src", ValueType::List), types::Typedef("idx", ValueType::Number) } }, - types::Contract { { types::Typedef("src", ValueType::String), types::Typedef("idx", ValueType::Number) } } } }, - { container, index }); - - const auto num = static_cast(index.number()); - - if (container.valueType() == ValueType::List) - { - const auto i = static_cast(num < 0 ? static_cast(container.list().size()) + num : num); - if (i < container.list().size()) - return container.list()[i]; - else - VM::throwVMError( - ErrorKind::Index, - fmt::format("{} out of range {} (length {})", num, container.toString(vm), container.list().size())); - } - else if (container.valueType() == ValueType::String) - { - const auto i = static_cast(num < 0 ? static_cast(container.string().size()) + num : num); - if (i < container.string().size()) - return Value(std::string(1, container.string()[i])); - else - VM::throwVMError( - ErrorKind::Index, - fmt::format("{} out of range \"{}\" (length {})", num, container.string(), container.string().size())); - } - else - throw types::TypeCheckingError( - "@", - { { types::Contract { { types::Typedef("src", ValueType::List), types::Typedef("idx", ValueType::Number) } }, - types::Contract { { types::Typedef("src", ValueType::String), types::Typedef("idx", ValueType::Number) } } } }, - { container, index }); - } - - inline double doMath(double a, double b, const Instruction op) - { - if (op == ADD) - a += b; - else if (op == SUB) - a -= b; - else if (op == MUL) - a *= b; - else if (op == DIV) - { - if (b == 0) - Ark::VM::throwVMError(ErrorKind::DivisionByZero, fmt::format("Can not compute expression (/ {} {})", a, b)); - a /= b; - } - - return a; - } - - inline std::string mathInstToStr(const Instruction op) - { - if (op == ADD) - return "+"; - if (op == SUB) - return "-"; - if (op == MUL) - return "*"; - if (op == DIV) - return "/"; - return "???"; - } - } - VM::VM(State& state) noexcept : m_state(state), m_exit_code(0), m_running(false) { @@ -1450,40 +1327,7 @@ namespace Ark const Value* y = popAndResolveAsPtr(context); Value& list = *popAndResolveAsPtr(context); - if (y->valueType() != ValueType::Number || x->valueType() != ValueType::Number || - list.valueType() != ValueType::List) - throw types::TypeCheckingError( - "@@", - { { types::Contract { - { types::Typedef("src", ValueType::List), - types::Typedef("y", ValueType::Number), - types::Typedef("x", ValueType::Number) } } } }, - { list, *y, *x }); - - long idx_y = static_cast(y->number()); - idx_y = idx_y < 0 ? static_cast(list.list().size()) + idx_y : idx_y; - if (std::cmp_greater_equal(idx_y, list.list().size()) || idx_y < 0) - throwVMError( - ErrorKind::Index, - fmt::format("@@ index ({}) out of range (list size: {})", idx_y, list.list().size())); - - const bool is_list = list.list()[static_cast(idx_y)].valueType() == ValueType::List; - const std::size_t size = - is_list - ? list.list()[static_cast(idx_y)].list().size() - : list.list()[static_cast(idx_y)].stringRef().size(); - - long idx_x = static_cast(x->number()); - idx_x = idx_x < 0 ? static_cast(size) + idx_x : idx_x; - if (std::cmp_greater_equal(idx_x, size) || idx_x < 0) - throwVMError( - ErrorKind::Index, - fmt::format("@@ index (x: {}) out of range (inner indexable size: {})", idx_x, size)); - - if (is_list) - push(list.list()[static_cast(idx_y)].list()[static_cast(idx_x)], context); - else - push(Value(std::string(1, list.list()[static_cast(idx_y)].stringRef()[static_cast(idx_x)])), context); + push(helper::atAt(x, y, list), context); } DISPATCH(); } diff --git a/src/arkscript/REPL/Utils.cpp b/src/arkscript/REPL/Utils.cpp index 97172f9d..1f22086c 100644 --- a/src/arkscript/REPL/Utils.cpp +++ b/src/arkscript/REPL/Utils.cpp @@ -24,9 +24,13 @@ namespace Ark::internal std::ranges::transform(Language::operators, std::back_inserter(output), [](const auto& string_view) { return std::string(string_view); }); - std::ranges::transform(std::ranges::views::keys(Builtins::builtins), std::back_inserter(output), [](const auto& string) { - return string; - }); + std::ranges::transform( + std::ranges::views::keys(Builtins::builtins) | std::ranges::views::filter([&output](const auto& val) -> bool { + return std::ranges::find(output, val) == output.end(); + }), + std::back_inserter(output), [](const auto& string) { + return string; + }); output.emplace_back(Language::And); output.emplace_back(Language::Or); @@ -56,9 +60,18 @@ namespace Ark::internal safe_op.insert(it, "\\"); return std::make_pair(safe_op, Replxx::Color::BRIGHTBLUE); }); - std::ranges::transform(std::ranges::views::keys(Builtins::builtins), std::back_inserter(output), [](const auto& string) { - return std::make_pair(string, Replxx::Color::GREEN); - }); + std::ranges::transform( + std::ranges::views::keys(Builtins::builtins) | std::ranges::views::filter([&output](const auto& val) -> bool { + return std::ranges::find_if(output, [&val](const std::pair& pair) -> bool { + return pair.first == val; + }) == output.end(); + }), + std::back_inserter(output), [](const auto& string) { + auto safe_op = string; + if (const auto it = safe_op.find_first_of(R"(-+=/*<>[]()?")"); it != std::string::npos) + safe_op.insert(it, "\\"); + return std::make_pair(safe_op, Replxx::Color::GREEN); + }); output.emplace_back(Language::And, Replxx::Color::BRIGHTBLUE); output.emplace_back(Language::Or, Replxx::Color::BRIGHTBLUE); diff --git a/tests/unittests/resources/CompilerSuite/ir/operators.ark b/tests/unittests/resources/CompilerSuite/ir/operators.ark new file mode 100644 index 00000000..cb70addc --- /dev/null +++ b/tests/unittests/resources/CompilerSuite/ir/operators.ark @@ -0,0 +1,26 @@ +# in this test, we want to ensure that operators are still being compiled to their corresponding instruction, +# and not using their corresponding builtin + +(print + (+ 1 2) + (- 1 2) + (* 1 2) + (/ 1 2) + (mod 1 2) + (toNumber "12") + (toString 12) + (< 1 2) + (<= 1 2) + (> 1 2) + (>= 1 2) + (= 1 2) + (!= 1 2) + (not 12) + (len []) + (empty? []) + (nil? []) + (tail []) + (head []) + (@ [1 2] 1) + (@@ [[1] [2] [3]] 0 0) + (type 12)) diff --git a/tests/unittests/resources/CompilerSuite/ir/operators.expected b/tests/unittests/resources/CompilerSuite/ir/operators.expected new file mode 100644 index 00000000..50a28651 --- /dev/null +++ b/tests/unittests/resources/CompilerSuite/ir/operators.expected @@ -0,0 +1,72 @@ +page_0 + PUSH_RETURN_ADDRESS L0 + LOAD_CONST 0 + TYPE 0 + LOAD_CONST 1 + LIST 1 + LOAD_CONST 2 + LIST 1 + LOAD_CONST 3 + LIST 1 + LIST 3 + LOAD_CONST 4 + LOAD_CONST 4 + AT_AT 0 + LOAD_CONST 2 + LOAD_CONST 3 + LIST 2 + LOAD_CONST 3 + AT 0 + LIST 0 + HEAD 0 + LIST 0 + TAIL 0 + LIST 0 + IS_NIL 0 + LIST 0 + IS_EMPTY 0 + LIST 0 + LEN 0 + LOAD_CONST 0 + NOT 0 + LOAD_CONST 3 + LOAD_CONST 2 + NEQ 0 + LOAD_CONST 3 + LOAD_CONST 2 + EQ 0 + LOAD_CONST 3 + LOAD_CONST 2 + GE 0 + LOAD_CONST 3 + LOAD_CONST 2 + GT 0 + LOAD_CONST 3 + LOAD_CONST 2 + LE 0 + LOAD_CONST 3 + LOAD_CONST 2 + LT 0 + LOAD_CONST 0 + TO_STR 0 + LOAD_CONST 5 + TO_NUM 0 + LOAD_CONST 3 + LOAD_CONST 2 + MOD 0 + LOAD_CONST 3 + LOAD_CONST 2 + DIV 0 + LOAD_CONST 3 + LOAD_CONST 2 + MUL 0 + LOAD_CONST 3 + LOAD_CONST 2 + SUB 0 + LOAD_CONST 3 + LOAD_CONST 2 + ADD 0 + BUILTIN 9 + CALL 22 +.L0: + HALT 0 \ No newline at end of file diff --git a/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.ark b/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.ark new file mode 100644 index 00000000..3e683477 --- /dev/null +++ b/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.ark @@ -0,0 +1 @@ +(print + - * / mod toNumber toString < <= > >= = != not len empty? nil? tail head @ @@ type) diff --git a/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected b/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected new file mode 100644 index 00000000..c5fc3d82 --- /dev/null +++ b/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected @@ -0,0 +1,28 @@ +page_0 + PUSH_RETURN_ADDRESS L0 + BUILTIN 89 + BUILTIN 88 + BUILTIN 87 + BUILTIN 86 + BUILTIN 85 + BUILTIN 84 + BUILTIN 83 + BUILTIN 82 + BUILTIN 81 + BUILTIN 80 + BUILTIN 79 + BUILTIN 78 + BUILTIN 77 + BUILTIN 76 + BUILTIN 75 + BUILTIN 74 + BUILTIN 73 + BUILTIN 72 + BUILTIN 71 + BUILTIN 70 + BUILTIN 69 + BUILTIN 68 + BUILTIN 9 + CALL 22 +.L0: + HALT 0 diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding.ark b/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding.ark deleted file mode 100644 index b28d16e6..00000000 --- a/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding.ark +++ /dev/null @@ -1,2 +0,0 @@ -(let ba0 (fun (a b) ())) -(ba0 0 0 nil?) diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding.expected b/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding.expected deleted file mode 100644 index c9125a06..00000000 --- a/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding.expected +++ /dev/null @@ -1,6 +0,0 @@ -In file tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding.ark:2 - 1 | (let ba0 (fun (a b) ())) - 2 | (ba0 0 0 nil?) - | ^~~~ - 3 | - Found a free standing operator: `nil?` diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_breakpoint.ark b/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_breakpoint.ark new file mode 100644 index 00000000..cf5b319a --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_breakpoint.ark @@ -0,0 +1 @@ +(print breakpoint) diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_breakpoint.expected b/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_breakpoint.expected new file mode 100644 index 00000000..812c2178 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_breakpoint.expected @@ -0,0 +1,5 @@ +In file tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_breakpoint.ark:1 + 1 | (print breakpoint) + | ^~~~~~~~~~ + 2 | + Found a freestanding operator: `breakpoint`. It can not be used as value like `+', where (let add +) (add 1 2) would be valid \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_hasfield.ark b/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_hasfield.ark new file mode 100644 index 00000000..b781b00a --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_hasfield.ark @@ -0,0 +1 @@ +(print hasField) diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_hasfield.expected b/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_hasfield.expected new file mode 100644 index 00000000..82e089ce --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_hasfield.expected @@ -0,0 +1,5 @@ +In file tests/unittests/resources/DiagnosticsSuite/compileTime/freestanding_hasfield.ark:1 + 1 | (print hasField) + | ^~~~~~~~ + 2 | + Found a freestanding operator: `hasField`. It can not be used as value like `+', where (let add +) (add 1 2) would be valid \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_nil.ark new file mode 100644 index 00000000..3fea8e7d --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_nil.ark @@ -0,0 +1 @@ +(print (apply + [nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_nil.expected new file mode 100644 index 00000000..b08744a3 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_nil.expected @@ -0,0 +1,20 @@ +Function + expected at least 1 argument and got 1 +Call + ↳ (+ nil) +Signature + ↳ (+ a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + nil (Nil) × + +Alternative 2: +Signature + ↳ (+ a) +Arguments + → variadic `a' (expected String): 1 argument do not match: + nil (Nil) × + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_nil.ark:1 + 1 | (print (apply + [nil])) + | ^~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_nil.ark new file mode 100644 index 00000000..1cf9a743 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_nil.ark @@ -0,0 +1 @@ +(print (apply + [1 nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_nil.expected new file mode 100644 index 00000000..2b3d1471 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_nil.expected @@ -0,0 +1,24 @@ +Function + expected at least 1 argument and got 2 +Call + ↳ (+ 1 nil) +Signature + ↳ (+ a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + 1 (Number) ✓ + nil (Nil) × + → unexpected additional args: nil (Nil) + +Alternative 2: +Signature + ↳ (+ a) +Arguments + → variadic `a' (expected String): 2 arguments do not match: + 1 (Number) × + nil (Nil) × + → unexpected additional args: nil (Nil) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_nil.ark:1 + 1 | (print (apply + [1 nil])) + | ^~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_num_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_num_nil.ark new file mode 100644 index 00000000..76d8ddc5 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_num_nil.ark @@ -0,0 +1 @@ +(print (apply + [1 2 nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_num_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_num_nil.expected new file mode 100644 index 00000000..029c441c --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_num_nil.expected @@ -0,0 +1,26 @@ +Function + expected at least 1 argument and got 3 +Call + ↳ (+ 1 2 nil) +Signature + ↳ (+ a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + 1 (Number) ✓ + 2 (Number) ✓ + nil (Nil) × + → unexpected additional args: 2 (Number), nil (Nil) + +Alternative 2: +Signature + ↳ (+ a) +Arguments + → variadic `a' (expected String): 3 arguments do not match: + 1 (Number) × + 2 (Number) × + nil (Nil) × + → unexpected additional args: 2 (Number), nil (Nil) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_add_num_num_nil.ark:1 + 1 | (print (apply + [1 2 nil])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_at_num_zero.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_at_num_zero.ark new file mode 100644 index 00000000..41c1c092 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_at_num_zero.ark @@ -0,0 +1 @@ +(print (apply @ [1 0])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_at_num_zero.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_at_num_zero.expected new file mode 100644 index 00000000..5888c42e --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_at_num_zero.expected @@ -0,0 +1,20 @@ +Function @ expected 2 arguments +Call + ↳ (@ 1 0) +Signature + ↳ (@ src idx) +Arguments + → `src' (expected List), got 1 (Number) + → `idx' (expected Number) ✓ + +Alternative 2: +Signature + ↳ (@ src idx) +Arguments + → `src' (expected String), got 1 (Number) + → `idx' (expected Number) ✓ + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_at_num_zero.ark:1 + 1 | (print (apply @ [1 0])) + | ^~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_atat_num_zero.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_atat_num_zero.ark new file mode 100644 index 00000000..66183650 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_atat_num_zero.ark @@ -0,0 +1 @@ +(print (apply @@ [1 0])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_atat_num_zero.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_atat_num_zero.expected new file mode 100644 index 00000000..c5bab784 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_atat_num_zero.expected @@ -0,0 +1,14 @@ +Function @@ expected 3 arguments but got 2 +Call + ↳ (@@ 1 0) +Signature + ↳ (@@ src y x) +Arguments + → `src' (expected List), got 1 (Number) + → `y' (expected Number) ✓ + → `x' (expected Number) was not provided + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_atat_num_zero.ark:1 + 1 | (print (apply @@ [1 0])) + | ^~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_nil.ark new file mode 100644 index 00000000..12eb42a3 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_nil.ark @@ -0,0 +1 @@ +(print (apply / [nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_nil.expected new file mode 100644 index 00000000..95d840e4 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_nil.expected @@ -0,0 +1,13 @@ +Function / expected at least 1 argument and got 1 +Call + ↳ (/ nil) +Signature + ↳ (/ a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + nil (Nil) × + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_nil.ark:1 + 1 | (print (apply / [nil])) + | ^~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_nil.ark new file mode 100644 index 00000000..99828fb1 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_nil.ark @@ -0,0 +1 @@ +(print (apply / [1 nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_nil.expected new file mode 100644 index 00000000..ed00e6cf --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_nil.expected @@ -0,0 +1,15 @@ +Function / expected at least 1 argument and got 2 +Call + ↳ (/ 1 nil) +Signature + ↳ (/ a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + 1 (Number) ✓ + nil (Nil) × + → unexpected additional args: nil (Nil) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_nil.ark:1 + 1 | (print (apply / [1 nil])) + | ^~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_num_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_num_nil.ark new file mode 100644 index 00000000..577a3271 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_num_nil.ark @@ -0,0 +1 @@ +(print (apply / [1 2 nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_num_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_num_nil.expected new file mode 100644 index 00000000..a06a9a17 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_num_nil.expected @@ -0,0 +1,16 @@ +Function / expected at least 1 argument and got 3 +Call + ↳ (/ 1 2 nil) +Signature + ↳ (/ a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + 1 (Number) ✓ + 2 (Number) ✓ + nil (Nil) × + → unexpected additional args: 2 (Number), nil (Nil) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_num_nil.ark:1 + 1 | (print (apply / [1 2 nil])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_zero.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_zero.ark new file mode 100644 index 00000000..12843226 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_zero.ark @@ -0,0 +1 @@ +(print (apply / [1 0])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_zero.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_zero.expected new file mode 100644 index 00000000..58696d09 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_zero.expected @@ -0,0 +1,6 @@ +DivisionByZero: Can not compute expression (/ 1 0) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_div_num_zero.ark:1 + 1 | (print (apply / [1 0])) + | ^~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_eq_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_eq_num.ark new file mode 100644 index 00000000..7cd33130 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_eq_num.ark @@ -0,0 +1 @@ +(print (apply = [1])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_eq_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_eq_num.expected new file mode 100644 index 00000000..bcdb095f --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_eq_num.expected @@ -0,0 +1,13 @@ +Function = expected 2 arguments but got 1 +Call + ↳ (= 1) +Signature + ↳ (= lhs rhs) +Arguments + → `lhs' (expected any) ✓ + → `rhs' (expected any) was not provided + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_eq_num.ark:1 + 1 | (print (apply = [1])) + | ^~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_ge_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_ge_num.ark new file mode 100644 index 00000000..4329fa50 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_ge_num.ark @@ -0,0 +1 @@ +(print (apply >= [1])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_ge_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_ge_num.expected new file mode 100644 index 00000000..b2fecae9 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_ge_num.expected @@ -0,0 +1,13 @@ +Function >= expected 2 arguments but got 1 +Call + ↳ (>= 1) +Signature + ↳ (>= lhs rhs) +Arguments + → `lhs' (expected any) ✓ + → `rhs' (expected any) was not provided + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_ge_num.ark:1 + 1 | (print (apply >= [1])) + | ^~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_gt_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_gt_num.ark new file mode 100644 index 00000000..d6b99182 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_gt_num.ark @@ -0,0 +1 @@ +(print (apply > [1])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_gt_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_gt_num.expected new file mode 100644 index 00000000..26bcb60f --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_gt_num.expected @@ -0,0 +1,13 @@ +Function > expected 2 arguments but got 1 +Call + ↳ (> 1) +Signature + ↳ (> lhs rhs) +Arguments + → `lhs' (expected any) ✓ + → `rhs' (expected any) was not provided + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_gt_num.ark:1 + 1 | (print (apply > [1])) + | ^~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num.ark new file mode 100644 index 00000000..b66f1448 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num.ark @@ -0,0 +1 @@ +(print (apply head [1])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num.expected new file mode 100644 index 00000000..eaf622c4 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num.expected @@ -0,0 +1,18 @@ +Function head expected 1 argument +Call + ↳ (head 1) +Signature + ↳ (head value) +Arguments + → `value' (expected List), got 1 (Number) + +Alternative 2: +Signature + ↳ (head value) +Arguments + → `value' (expected String), got 1 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num.ark:1 + 1 | (print (apply head [1])) + | ^~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num_num.ark new file mode 100644 index 00000000..8196fb93 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num_num.ark @@ -0,0 +1 @@ +(print (apply head [1 2])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num_num.expected new file mode 100644 index 00000000..9dd54efe --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num_num.expected @@ -0,0 +1,20 @@ +Function head expected 1 argument but got 2 +Call + ↳ (head 1 2) +Signature + ↳ (head value) +Arguments + → `value' (expected List), got 1 (Number) + → unexpected additional args: 2 (Number) + +Alternative 2: +Signature + ↳ (head value) +Arguments + → `value' (expected String), got 1 (Number) + → unexpected additional args: 2 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_head_num_num.ark:1 + 1 | (print (apply head [1 2])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num.ark new file mode 100644 index 00000000..2142d641 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num.ark @@ -0,0 +1 @@ +(print (apply empty? [1])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num.expected new file mode 100644 index 00000000..c11bd2af --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num.expected @@ -0,0 +1,30 @@ +Function empty? expected 1 argument +Call + ↳ (empty? 1) +Signature + ↳ (empty? value) +Arguments + → `value' (expected List), got 1 (Number) + +Alternative 2: +Signature + ↳ (empty? value) +Arguments + → `value' (expected Nil), got 1 (Number) + +Alternative 3: +Signature + ↳ (empty? value) +Arguments + → `value' (expected String), got 1 (Number) + +Alternative 4: +Signature + ↳ (empty? value) +Arguments + → `value' (expected Dict), got 1 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num.ark:1 + 1 | (print (apply empty? [1])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num_num.ark new file mode 100644 index 00000000..17287cb6 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num_num.ark @@ -0,0 +1 @@ +(print (apply empty? [1 2])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num_num.expected new file mode 100644 index 00000000..693fd9e1 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num_num.expected @@ -0,0 +1,34 @@ +Function empty? expected 1 argument but got 2 +Call + ↳ (empty? 1 2) +Signature + ↳ (empty? value) +Arguments + → `value' (expected List), got 1 (Number) + → unexpected additional args: 2 (Number) + +Alternative 2: +Signature + ↳ (empty? value) +Arguments + → `value' (expected Nil), got 1 (Number) + → unexpected additional args: 2 (Number) + +Alternative 3: +Signature + ↳ (empty? value) +Arguments + → `value' (expected String), got 1 (Number) + → unexpected additional args: 2 (Number) + +Alternative 4: +Signature + ↳ (empty? value) +Arguments + → `value' (expected Dict), got 1 (Number) + → unexpected additional args: 2 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isempty_num_num.ark:1 + 1 | (print (apply empty? [1 2])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isnil_num_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isnil_num_num.ark new file mode 100644 index 00000000..5e38c8bb --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isnil_num_num.ark @@ -0,0 +1 @@ +(print (apply nil? [1 2])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isnil_num_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isnil_num_num.expected new file mode 100644 index 00000000..8b48d6e1 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isnil_num_num.expected @@ -0,0 +1,13 @@ +Function nil? expected 1 argument but got 2 +Call + ↳ (nil? 1 2) +Signature + ↳ (nil? value) +Arguments + → `value' (expected any) ✓ + → unexpected additional args: 2 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_isnil_num_num.ark:1 + 1 | (print (apply nil? [1 2])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_le_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_le_num.ark new file mode 100644 index 00000000..f634055d --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_le_num.ark @@ -0,0 +1 @@ +(print (apply <= [1])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_le_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_le_num.expected new file mode 100644 index 00000000..a53ec0de --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_le_num.expected @@ -0,0 +1,13 @@ +Function <= expected 2 arguments but got 1 +Call + ↳ (<= 1) +Signature + ↳ (<= lhs rhs) +Arguments + → `lhs' (expected any) ✓ + → `rhs' (expected any) was not provided + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_le_num.ark:1 + 1 | (print (apply <= [1])) + | ^~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num.ark new file mode 100644 index 00000000..aed9fce0 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num.ark @@ -0,0 +1 @@ +(print (apply len [1])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num.expected new file mode 100644 index 00000000..e4e6f51f --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num.expected @@ -0,0 +1,24 @@ +Function len expected 1 argument +Call + ↳ (len 1) +Signature + ↳ (len value) +Arguments + → `value' (expected List), got 1 (Number) + +Alternative 2: +Signature + ↳ (len value) +Arguments + → `value' (expected String), got 1 (Number) + +Alternative 3: +Signature + ↳ (len value) +Arguments + → `value' (expected Dict), got 1 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num.ark:1 + 1 | (print (apply len [1])) + | ^~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num_num.ark new file mode 100644 index 00000000..8d1e7787 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num_num.ark @@ -0,0 +1 @@ +(print (apply len [1 2])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num_num.expected new file mode 100644 index 00000000..b4645e13 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num_num.expected @@ -0,0 +1,27 @@ +Function len expected 1 argument but got 2 +Call + ↳ (len 1 2) +Signature + ↳ (len value) +Arguments + → `value' (expected List), got 1 (Number) + → unexpected additional args: 2 (Number) + +Alternative 2: +Signature + ↳ (len value) +Arguments + → `value' (expected String), got 1 (Number) + → unexpected additional args: 2 (Number) + +Alternative 3: +Signature + ↳ (len value) +Arguments + → `value' (expected Dict), got 1 (Number) + → unexpected additional args: 2 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_len_num_num.ark:1 + 1 | (print (apply len [1 2])) + | ^~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_lt_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_lt_num.ark new file mode 100644 index 00000000..ac8bbf47 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_lt_num.ark @@ -0,0 +1 @@ +(print (apply < [1])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_lt_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_lt_num.expected new file mode 100644 index 00000000..5606ad22 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_lt_num.expected @@ -0,0 +1,13 @@ +Function < expected 2 arguments but got 1 +Call + ↳ (< 1) +Signature + ↳ (< lhs rhs) +Arguments + → `lhs' (expected any) ✓ + → `rhs' (expected any) was not provided + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_lt_num.ark:1 + 1 | (print (apply < [1])) + | ^~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mod_num_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mod_num_nil.ark new file mode 100644 index 00000000..eac86840 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mod_num_nil.ark @@ -0,0 +1 @@ +(print (apply mod [1 nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mod_num_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mod_num_nil.expected new file mode 100644 index 00000000..178afbef --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mod_num_nil.expected @@ -0,0 +1,13 @@ +Function mod expected 2 arguments +Call + ↳ (mod 1 nil) +Signature + ↳ (mod a b) +Arguments + → `a' (expected Number) ✓ + → `b' (expected Number), got nil (Nil) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mod_num_nil.ark:1 + 1 | (print (apply mod [1 nil])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_nil.ark new file mode 100644 index 00000000..ae398589 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_nil.ark @@ -0,0 +1 @@ +(print (apply * [nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_nil.expected new file mode 100644 index 00000000..2b09295e --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_nil.expected @@ -0,0 +1,13 @@ +Function * expected at least 1 argument and got 1 +Call + ↳ (* nil) +Signature + ↳ (* a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + nil (Nil) × + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_nil.ark:1 + 1 | (print (apply * [nil])) + | ^~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_nil.ark new file mode 100644 index 00000000..52adc3f1 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_nil.ark @@ -0,0 +1 @@ +(print (apply * [1 nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_nil.expected new file mode 100644 index 00000000..95ea8e3a --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_nil.expected @@ -0,0 +1,15 @@ +Function * expected at least 1 argument and got 2 +Call + ↳ (* 1 nil) +Signature + ↳ (* a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + 1 (Number) ✓ + nil (Nil) × + → unexpected additional args: nil (Nil) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_nil.ark:1 + 1 | (print (apply * [1 nil])) + | ^~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_num_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_num_nil.ark new file mode 100644 index 00000000..d13beb10 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_num_nil.ark @@ -0,0 +1 @@ +(print (apply * [1 2 nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_num_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_num_nil.expected new file mode 100644 index 00000000..529470bd --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_num_nil.expected @@ -0,0 +1,16 @@ +Function * expected at least 1 argument and got 3 +Call + ↳ (* 1 2 nil) +Signature + ↳ (* a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + 1 (Number) ✓ + 2 (Number) ✓ + nil (Nil) × + → unexpected additional args: 2 (Number), nil (Nil) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_mul_num_num_nil.ark:1 + 1 | (print (apply * [1 2 nil])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_neq_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_neq_num.ark new file mode 100644 index 00000000..be622843 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_neq_num.ark @@ -0,0 +1 @@ +(print (apply != [1])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_neq_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_neq_num.expected new file mode 100644 index 00000000..0359ca21 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_neq_num.expected @@ -0,0 +1,13 @@ +Function != expected 2 arguments but got 1 +Call + ↳ (!= 1) +Signature + ↳ (!= lhs rhs) +Arguments + → `lhs' (expected any) ✓ + → `rhs' (expected any) was not provided + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_neq_num.ark:1 + 1 | (print (apply != [1])) + | ^~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_not_num_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_not_num_num.ark new file mode 100644 index 00000000..c8f4e017 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_not_num_num.ark @@ -0,0 +1 @@ +(print (apply not [1 2])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_not_num_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_not_num_num.expected new file mode 100644 index 00000000..768c67d5 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_not_num_num.expected @@ -0,0 +1,13 @@ +Function not expected 1 argument but got 2 +Call + ↳ (not 1 2) +Signature + ↳ (not val) +Arguments + → `val' (expected any) ✓ + → unexpected additional args: 2 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_not_num_num.ark:1 + 1 | (print (apply not [1 2])) + | ^~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_nil.ark new file mode 100644 index 00000000..17d5059c --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_nil.ark @@ -0,0 +1 @@ +(print (apply - [nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_nil.expected new file mode 100644 index 00000000..174a495a --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_nil.expected @@ -0,0 +1,13 @@ +Function - expected at least 1 argument and got 1 +Call + ↳ (- nil) +Signature + ↳ (- a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + nil (Nil) × + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_nil.ark:1 + 1 | (print (apply - [nil])) + | ^~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_nil.ark new file mode 100644 index 00000000..3a284fa5 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_nil.ark @@ -0,0 +1 @@ +(print (apply - [1 nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_nil.expected new file mode 100644 index 00000000..99b87015 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_nil.expected @@ -0,0 +1,15 @@ +Function - expected at least 1 argument and got 2 +Call + ↳ (- 1 nil) +Signature + ↳ (- a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + 1 (Number) ✓ + nil (Nil) × + → unexpected additional args: nil (Nil) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_nil.ark:1 + 1 | (print (apply - [1 nil])) + | ^~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_num_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_num_nil.ark new file mode 100644 index 00000000..062537bd --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_num_nil.ark @@ -0,0 +1 @@ +(print (apply - [1 2 nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_num_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_num_nil.expected new file mode 100644 index 00000000..d253e7ec --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_num_nil.expected @@ -0,0 +1,16 @@ +Function - expected at least 1 argument and got 3 +Call + ↳ (- 1 2 nil) +Signature + ↳ (- a) +Arguments + → variadic `a' (expected Number): 1 argument do not match: + 1 (Number) ✓ + 2 (Number) ✓ + nil (Nil) × + → unexpected additional args: 2 (Number), nil (Nil) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_sub_num_num_nil.ark:1 + 1 | (print (apply - [1 2 nil])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num.ark new file mode 100644 index 00000000..08f9553d --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num.ark @@ -0,0 +1 @@ +(print (apply tail [1])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num.expected new file mode 100644 index 00000000..388cf3c6 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num.expected @@ -0,0 +1,18 @@ +Function tail expected 1 argument +Call + ↳ (tail 1) +Signature + ↳ (tail value) +Arguments + → `value' (expected List), got 1 (Number) + +Alternative 2: +Signature + ↳ (tail value) +Arguments + → `value' (expected String), got 1 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num.ark:1 + 1 | (print (apply tail [1])) + | ^~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num_num.ark new file mode 100644 index 00000000..0739bb21 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num_num.ark @@ -0,0 +1 @@ +(print (apply tail [1 2])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num_num.expected new file mode 100644 index 00000000..cba3e96f --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num_num.expected @@ -0,0 +1,20 @@ +Function tail expected 1 argument but got 2 +Call + ↳ (tail 1 2) +Signature + ↳ (tail value) +Arguments + → `value' (expected List), got 1 (Number) + → unexpected additional args: 2 (Number) + +Alternative 2: +Signature + ↳ (tail value) +Arguments + → `value' (expected String), got 1 (Number) + → unexpected additional args: 2 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tail_num_num.ark:1 + 1 | (print (apply tail [1 2])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_nil.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_nil.ark new file mode 100644 index 00000000..d27cd733 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_nil.ark @@ -0,0 +1 @@ +(print (apply toNumber [nil])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_nil.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_nil.expected new file mode 100644 index 00000000..5200cf18 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_nil.expected @@ -0,0 +1,12 @@ +Function toNumber expected 1 argument +Call + ↳ (toNumber nil) +Signature + ↳ (toNumber val) +Arguments + → `val' (expected String), got nil (Nil) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_nil.ark:1 + 1 | (print (apply toNumber [nil])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_num_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_num_num.ark new file mode 100644 index 00000000..dfb1564b --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_num_num.ark @@ -0,0 +1 @@ +(print (apply toNumber [1 2])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_num_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_num_num.expected new file mode 100644 index 00000000..36de0cf1 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_num_num.expected @@ -0,0 +1,13 @@ +Function toNumber expected 1 argument but got 2 +Call + ↳ (toNumber 1 2) +Signature + ↳ (toNumber val) +Arguments + → `val' (expected String), got 1 (Number) + → unexpected additional args: 2 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tonum_num_num.ark:1 + 1 | (print (apply toNumber [1 2])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tostr_num_num.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tostr_num_num.ark new file mode 100644 index 00000000..e6193b02 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tostr_num_num.ark @@ -0,0 +1 @@ +(print (apply toString [1 2])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tostr_num_num.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tostr_num_num.expected new file mode 100644 index 00000000..73f81f16 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tostr_num_num.expected @@ -0,0 +1,13 @@ +Function toString expected 1 argument but got 2 +Call + ↳ (toString 1 2) +Signature + ↳ (toString val) +Arguments + → `val' (expected any) ✓ + → unexpected additional args: 2 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_tostr_num_num.ark:1 + 1 | (print (apply toString [1 2])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_type_num_zero.ark b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_type_num_zero.ark new file mode 100644 index 00000000..da63d037 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_type_num_zero.ark @@ -0,0 +1 @@ +(print (apply type [1 0])) diff --git a/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_type_num_zero.expected b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_type_num_zero.expected new file mode 100644 index 00000000..0f45b3f5 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_type_num_zero.expected @@ -0,0 +1,13 @@ +Function type expected 1 argument but got 2 +Call + ↳ (type 1 0) +Signature + ↳ (type value) +Arguments + → `value' (expected any) ✓ + → unexpected additional args: 0 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/typeChecking/builtin_type_num_zero.ark:1 + 1 | (print (apply type [1 0])) + | ^~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | \ No newline at end of file diff --git a/tests/unittests/resources/LangSuite/operators-tests.ark b/tests/unittests/resources/LangSuite/operators-tests.ark new file mode 100644 index 00000000..504a5f6c --- /dev/null +++ b/tests/unittests/resources/LangSuite/operators-tests.ark @@ -0,0 +1,191 @@ +(import std.Testing) + +(let foo (fun () ())) + +(test:suite operators { + (test:case "+" { + (test:eq (apply + [1]) 1) + (test:eq (apply + [1 2]) (+ 1 2)) + (test:eq (apply + [1 2]) 3) + (test:eq (apply + [1 2 3]) (+ 1 2 3)) + (test:eq (apply + [1 2 3]) 6) + (test:eq (apply + ["a"]) "a") + (test:eq (apply + ["a" "b"]) (+ "a" "b")) + (test:eq (apply + ["a" "b"]) "ab") + (test:eq (apply + ["a" "b" "c"]) (+ "a" "b" "c")) + (test:eq (apply + ["a" "b" "c"]) "abc") }) + + (test:case "-" { + (test:eq (apply - [1 2]) (- 1 2)) + (test:eq (apply - [1 2]) -1) + (test:eq (apply - [1 2 3]) (- 1 2 3)) + (test:eq (apply - [1 2 3]) -4) }) + + (test:case "*" { + (test:eq (apply * [1 2]) (* 1 2)) + (test:eq (apply * [1 2]) 2) + (test:eq (apply * [1 2 3]) (* 1 2 3)) + (test:eq (apply * [1 2 3]) 6) }) + + (test:case "/" { + (test:eq (apply / [1 2]) (/ 1 2)) + (test:eq (apply / [1 2]) 0.5) + (test:eq (apply / [12 2 3]) (/ 12 2 3)) + (test:eq (apply / [12 2 3]) 2) }) + + (test:case "mod" { + (test:eq (apply mod [5 3]) (mod 5 3)) + (test:eq (apply mod [5 3]) 2) + (test:eq (apply mod [5.7 3.2]) (mod 5.7 3.2)) + (test:eq (apply mod [5.7 3.2]) 2.5) }) + + (test:case "toNumber" { + (test:eq (apply toNumber ["a"]) (toNumber "a")) + (test:eq (apply toNumber ["a"]) nil) + (test:eq (apply toNumber ["123"]) (toNumber "123")) + (test:eq (apply toNumber ["123"]) 123) }) + + (test:case "toString" { + (test:eq (apply toString [1]) (toString 1)) + (test:eq (apply toString [1]) "1") + (test:eq (apply toString ["1"]) (toString "1")) + (test:eq (apply toString ["1"]) "1") + (test:eq (apply toString [nil]) (toString nil)) + (test:eq (apply toString [nil]) "nil") + (test:eq (apply toString [true]) (toString true)) + (test:eq (apply toString [true]) "true") + (test:eq (apply toString [foo]) (toString foo)) }) + + (test:case "<" { + (test:eq (apply < [1 2]) (< 1 2)) + (test:eq (apply < [1 2]) true) + (test:eq (apply < [2 2]) (< 2 2)) + (test:eq (apply < [2 2]) false) + (test:eq (apply < [2 1]) (< 2 1)) + (test:eq (apply < [2 1]) false) }) + + (test:case "<=" { + (test:eq (apply <= [1 2]) (<= 1 2)) + (test:eq (apply <= [1 2]) true) + (test:eq (apply <= [2 2]) (<= 2 2)) + (test:eq (apply <= [2 2]) true) + (test:eq (apply <= [2 1]) (<= 2 1)) + (test:eq (apply <= [2 1]) false) }) + + (test:case ">" { + (test:eq (apply > [1 2]) (> 1 2)) + (test:eq (apply > [1 2]) false) + (test:eq (apply > [2 2]) (> 2 2)) + (test:eq (apply > [2 2]) false) + (test:eq (apply > [2 1]) (> 2 1)) + (test:eq (apply > [2 1]) true) }) + + (test:case ">=" { + (test:eq (apply >= [1 2]) (>= 1 2)) + (test:eq (apply >= [1 2]) false) + (test:eq (apply >= [2 2]) (>= 2 2)) + (test:eq (apply >= [2 2]) true) + (test:eq (apply >= [2 1]) (>= 2 1)) + (test:eq (apply >= [2 1]) true) }) + + (test:case "=" { + (test:eq (apply = [1 2]) (= 1 2)) + (test:eq (apply = [1 2]) false) + (test:eq (apply = [2 2]) (= 2 2)) + (test:eq (apply = [2 2]) true) + (test:eq (apply = [2 1]) (= 2 1)) + (test:eq (apply = [2 1]) false) }) + + (test:case "!=" { + (test:eq (apply != [1 2]) (!= 1 2)) + (test:eq (apply != [1 2]) true) + (test:eq (apply != [2 2]) (!= 2 2)) + (test:eq (apply != [2 2]) false) + (test:eq (apply != [2 1]) (!= 2 1)) + (test:eq (apply != [2 1]) true) }) + + (test:case "not" { + (test:eq (apply not [true]) (not true)) + (test:eq (apply not [true]) false) + (test:eq (apply not [false]) (not false)) + (test:eq (apply not [false]) true) }) + + (test:case "len" { + (test:eq (apply len [[1 2 3]]) (len [1 2 3])) + (test:eq (apply len [[1 2 3]]) 3) + (test:eq (apply len [[]]) (len [])) + (test:eq (apply len [[]]) 0) + (test:eq (apply len ["abc"]) (len "abc")) + (test:eq (apply len ["abc"]) 3) + (test:eq (apply len [""]) (len "")) + (test:eq (apply len [""]) 0) + (test:eq (apply len [(dict)]) ($as-is (len (dict)))) + (test:eq (apply len [(dict)]) 0) + (test:eq (apply len [(dict "a" 1)]) ($as-is (len (dict "a" 1)))) + (test:eq (apply len [(dict "a" 1)]) 1) }) + + (test:case "empty?" { + (test:eq (apply empty? [[1 2 3]]) (empty? [1 2 3])) + (test:eq (apply empty? [[1 2 3]]) false) + (test:eq (apply empty? [[]]) (empty? [])) + (test:eq (apply empty? [[]]) true) + (test:eq (apply empty? ["abc"]) (empty? "abc")) + (test:eq (apply empty? ["abc"]) false) + (test:eq (apply empty? [""]) (empty? "")) + (test:eq (apply empty? [""]) true) + (test:eq (apply empty? [(dict)]) ($as-is (empty? (dict)))) + (test:eq (apply empty? [(dict)]) true) + (test:eq (apply empty? [(dict "a" 1)]) ($as-is (empty? (dict "a" 1)))) + (test:eq (apply empty? [(dict "a" 1)]) false) }) + + (test:case "nil?" { + (test:eq (apply nil? [1]) (nil? 1)) + (test:eq (apply nil? [1]) false) + (test:eq (apply nil? [nil]) (nil? nil)) + (test:eq (apply nil? [nil]) true) }) + + (test:case "tail" { + (test:eq (apply tail [[]]) (tail [])) + (test:eq (apply tail [[]]) []) + (test:eq (apply tail [[1 2 3]]) (tail [1 2 3])) + (test:eq (apply tail [[1 2 3]]) [2 3]) + (test:eq (apply tail [""]) (tail "")) + (test:eq (apply tail [""]) "") + (test:eq (apply tail ["123"]) (tail "123")) + (test:eq (apply tail ["123"]) "23") }) + + (test:case "head" { + (test:eq (apply head [[]]) (head [])) + (test:eq (apply head [[]]) nil) + (test:eq (apply head [[1 2 3]]) (head [1 2 3])) + (test:eq (apply head [[1 2 3]]) 1) + (test:eq (apply head [""]) (head "")) + (test:eq (apply head [""]) "") + (test:eq (apply head ["123"]) (head "123")) + (test:eq (apply head ["123"]) "1") }) + + (test:case "@" { + (test:eq (apply @ [[1 2 3] 0]) (@ [1 2 3] 0)) + (test:eq (apply @ [[1 2 3] 0]) 1) + (test:eq (apply @ [[1 2 3] -1]) (@ [1 2 3] -1)) + (test:eq (apply @ [[1 2 3] -1]) 3) + (test:eq (apply @ ["123" 0]) (@ "123" 0)) + (test:eq (apply @ ["123" 0]) "1") + (test:eq (apply @ ["123" -1]) (@ "123" -1)) + (test:eq (apply @ ["123" -1]) "3") }) + + (test:case "@@" { + (test:eq (apply @@ [[[1 2 3] [4 5 6]] 0 0]) (@@ [[1 2 3] [4 5 6]] 0 0)) + (test:eq (apply @@ [[[1 2 3] [4 5 6]] 0 0]) 1) + (test:eq (apply @@ [[[1 2 3] [4 5 6]] -1 -1]) (@@ [[1 2 3] [4 5 6]] -1 -1)) + (test:eq (apply @@ [[[1 2 3] [4 5 6]] -1 -1]) 6) + (test:eq (apply @@ [["123" "456"] 0 0]) (@@ ["123" "456"] 0 0)) + (test:eq (apply @@ [["123" "456"] 0 0]) "1") + (test:eq (apply @@ [["123" "456"] -1 -1]) (@@ ["123" "456"] -1 -1)) + (test:eq (apply @@ [["123" "456"] -1 -1]) "6") }) + + (test:case "type" { + (test:eq (apply type [1]) (type 1)) + (test:eq (apply type [1]) "Number") + (test:eq (apply type [nil]) (type nil)) + (test:eq (apply type [nil]) "Nil") })}) diff --git a/tests/unittests/resources/LangSuite/unittests.ark b/tests/unittests/resources/LangSuite/unittests.ark index 07d34faf..0392dfef 100644 --- a/tests/unittests/resources/LangSuite/unittests.ark +++ b/tests/unittests/resources/LangSuite/unittests.ark @@ -1,5 +1,6 @@ (import vm-tests) (import builtins-tests) +(import operators-tests) (import module-tests) (import utf8-tests) (import macro-tests) @@ -12,6 +13,7 @@ (let outputs (list:unzip [ vm-tests:vm-output builtins-tests:builtin-output + operators-tests:operators-output module-tests:module-output utf8-tests:utf8-output macro-tests:macro-output From b34f75f322787187a3738a6b342097e089da3156 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Wed, 11 Feb 2026 17:55:21 +0100 Subject: [PATCH 4/7] refactor(ast lowerer): put all code using list of nodes in the same if statement --- .../Compiler/Lowerer/ASTLowerer.cpp | 132 +++++++++--------- 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 91890103..473060fc 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -17,7 +17,7 @@ namespace Ark::internal ASTLowerer::ASTLowerer(const unsigned debug) : m_logger("ASTLowerer", debug) - { } + {} void ASTLowerer::addToTables(const std::vector& symbols, const std::vector& constants) { @@ -195,80 +195,78 @@ namespace Ark::internal // namespace nodes else if (x.nodeType() == NodeType::Namespace) compileExpression(*x.constArkNamespace().ast, p, is_result_unused, is_terminal); - else if (x.nodeType() == NodeType::Unused) - { - // do nothing, explicitly - } - // empty code block should be nil - else if (x.constList().empty()) + else if (x.nodeType() == NodeType::List) { - if (!is_result_unused) + // empty code block should be nil + if (x.constList().empty()) { - static const std::optional nil = getBuiltin("nil"); - page(p).emplace_back(BUILTIN, nil.value()); + if (!is_result_unused) + { + static const std::optional nil = getBuiltin("nil"); + page(p).emplace_back(BUILTIN, nil.value()); + } } - } - // list instructions - else if (const auto head = x.constList()[0]; head.nodeType() == NodeType::Symbol && getListInstruction(head.string()).has_value()) - compileListInstruction(x, p, is_result_unused); - // todo: make a function "compile special form" for list and apply (and probably others in the future) - else if (const auto head2 = x.constList()[0]; head2.nodeType() == NodeType::Symbol && head2.string() == Language::Apply) - compileApplyInstruction(x, p, is_result_unused); - // registering structures - else if (x.constList()[0].nodeType() == NodeType::Keyword) - { - switch (const Keyword keyword = x.constList()[0].keyword()) + // list instructions + else if (const auto head = x.constList()[0]; head.nodeType() == NodeType::Symbol && getListInstruction(head.string()).has_value()) + compileListInstruction(x, p, is_result_unused); + else if (head.nodeType() == NodeType::Symbol && head.string() == Language::Apply) + compileApplyInstruction(x, p, is_result_unused); + // registering structures + else if (head.nodeType() == NodeType::Keyword) { - case Keyword::If: - compileIf(x, p, is_result_unused, is_terminal); - break; - - case Keyword::Set: - [[fallthrough]]; - case Keyword::Let: - [[fallthrough]]; - case Keyword::Mut: - compileLetMutSet(keyword, x, p); - break; - - case Keyword::Fun: - compileFunction(x, p, is_result_unused); - break; - - case Keyword::Begin: + switch (const Keyword keyword = head.keyword()) { - for (std::size_t i = 1, size = x.list().size(); i < size; ++i) - compileExpression( - x.list()[i], - p, - // All the nodes in a 'begin' (except for the last one) are producing a result that we want to drop. - (i != size - 1) || is_result_unused, - // If the 'begin' is a terminal node, only its last node is terminal. - is_terminal && (i == size - 1)); - break; + case Keyword::If: + compileIf(x, p, is_result_unused, is_terminal); + break; + + case Keyword::Set: + [[fallthrough]]; + case Keyword::Let: + [[fallthrough]]; + case Keyword::Mut: + compileLetMutSet(keyword, x, p); + break; + + case Keyword::Fun: + compileFunction(x, p, is_result_unused); + break; + + case Keyword::Begin: + { + for (std::size_t i = 1, size = x.list().size(); i < size; ++i) + compileExpression( + x.list()[i], + p, + // All the nodes in a 'begin' (except for the last one) are producing a result that we want to drop. + (i != size - 1) || is_result_unused, + // If the 'begin' is a terminal node, only its last node is terminal. + is_terminal && (i == size - 1)); + break; + } + + case Keyword::While: + compileWhile(x, p); + break; + + case Keyword::Import: + compilePluginImport(x, p); + break; + + case Keyword::Del: + page(p).emplace_back(DEL, addSymbol(x.constList()[1])); + page(p).back().setSourceLocation(x.filename(), x.position().start.line); + break; } - - case Keyword::While: - compileWhile(x, p); - break; - - case Keyword::Import: - compilePluginImport(x, p); - break; - - case Keyword::Del: - page(p).emplace_back(DEL, addSymbol(x.constList()[1])); - page(p).back().setSourceLocation(x.filename(), x.position().start.line); - break; + } + else + { + // If we are here, we should have a function name via the m_opened_vars. + // Push arguments first, then function name, then call it. + handleCalls(x, p, is_result_unused, is_terminal); } } - else if (x.nodeType() == NodeType::List) - { - // If we are here, we should have a function name via the m_opened_vars. - // Push arguments first, then function name, then call it. - handleCalls(x, p, is_result_unused, is_terminal); - } - else + else if (x.nodeType() != NodeType::Unused) buildAndThrowError( fmt::format( "NodeType `{}' not handled in ASTLowerer::compileExpression. Please fill an issue on GitHub: https://github.com/ArkScript-lang/Ark", From 8676823722f70460d5e0a847b6a57104ac402263 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Wed, 11 Feb 2026 18:26:19 +0100 Subject: [PATCH 5/7] refactor(ast lowerer): split the calls lowering in multiple methods, to separate shortcircuit, operators and function calls --- include/Ark/Compiler/Lowerer/ASTLowerer.hpp | 3 + .../Compiler/Lowerer/ASTLowerer.cpp | 310 +++++++++--------- 2 files changed, 167 insertions(+), 146 deletions(-) diff --git a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp index 9e9fddc4..d78ca766 100644 --- a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp +++ b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp @@ -246,6 +246,9 @@ namespace Ark::internal void compilePluginImport(const Node& x, Page p); void pushFunctionCallArguments(Node& call, Page p, bool is_tail_call); void handleCalls(Node& x, Page p, bool is_result_unused, bool is_terminal); + void handleShortcircuit(Node& x, Page p); + void handleOperator(Node& x, Page p, Instruction op); + bool handleFunctionCall(Node& x, Page p, bool is_terminal); /** * @brief Register a given node in the symbol table diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 473060fc..79f3e7fb 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -239,9 +239,9 @@ namespace Ark::internal x.list()[i], p, // All the nodes in a 'begin' (except for the last one) are producing a result that we want to drop. - (i != size - 1) || is_result_unused, + /* is_result_unused= */ (i != size - 1) || is_result_unused, // If the 'begin' is a terminal node, only its last node is terminal. - is_terminal && (i == size - 1)); + /* is_terminal= */ is_terminal && (i == size - 1)); break; } @@ -638,179 +638,197 @@ namespace Ark::internal void ASTLowerer::handleCalls(Node& x, const Page p, bool is_result_unused, const bool is_terminal) { - constexpr std::size_t start_index = 1; - - Node& node = x.list()[0]; - const std::optional maybe_operator = node.nodeType() == NodeType::Symbol ? getOperator(node.string()) : std::nullopt; + const Node& node = x.constList()[0]; + bool matched = false; - const std::optional maybe_shortcircuit = - node.nodeType() == NodeType::Symbol - ? (node.string() == Language::And - ? std::make_optional(Instruction::SHORTCIRCUIT_AND) - : (node.string() == Language::Or - ? std::make_optional(Instruction::SHORTCIRCUIT_OR) - : std::nullopt)) - : std::nullopt; + if (node.nodeType() == NodeType::Symbol) + { + if (node.string() == Language::And || node.string() == Language::Or) + { + matched = true; + handleShortcircuit(x, p); + } + if (const auto maybe_operator = getOperator(node.string()); maybe_operator.has_value()) + { + matched = true; + if (maybe_operator.value() == BREAKPOINT) + is_result_unused = false; + handleOperator(x, p, maybe_operator.value()); + } + } - if (maybe_shortcircuit.has_value()) + if (!matched) { - // short circuit implementation - if (x.constList().size() < 3) - buildAndThrowError( - fmt::format( - "Expected at least 2 arguments while compiling '{}', got {}", - node.string(), - x.constList().size() - 1), - x); - const auto name = node.string(); // and / or + // if nothing else matched, then compile a function call + if (handleFunctionCall(x, p, is_terminal)) + // if it returned true, we compiled a tail call, skip the POP at the end + return; + } - if (!nodeProducesOutput(x.list()[1])) + if (is_result_unused) + page(p).emplace_back(POP); + } + + void ASTLowerer::handleShortcircuit(Node& x, const Page p) + { + const Node& node = x.constList()[0]; + const auto name = node.string(); // and / or + const Instruction inst = name == Language::And ? SHORTCIRCUIT_AND : SHORTCIRCUIT_OR; + + // short circuit implementation + if (x.constList().size() < 3) + buildAndThrowError( + fmt::format( + "Expected at least 2 arguments while compiling '{}', got {}", + name, + x.constList().size() - 1), + x); + + if (!nodeProducesOutput(x.list()[1])) + buildAndThrowError( + fmt::format( + "Can not use `{}' inside a `{}' expression, as it doesn't return a value", + x.list()[1].repr(), name), + x.list()[1]); + compileExpression(x.list()[1], p, false, false); + + const auto label_shortcircuit = IR::Entity::Label(m_current_label++); + auto shortcircuit_entity = IR::Entity::Goto(label_shortcircuit, inst); + page(p).emplace_back(shortcircuit_entity); + + for (std::size_t i = 2, end = x.constList().size(); i < end; ++i) + { + if (!nodeProducesOutput(x.list()[i])) buildAndThrowError( fmt::format( "Can not use `{}' inside a `{}' expression, as it doesn't return a value", - x.list()[1].repr(), name), - x.list()[1]); - compileExpression(x.list()[1], p, false, false); + x.list()[i].repr(), name), + x.list()[i]); + compileExpression(x.list()[i], p, false, false); + if (i + 1 != end) + page(p).emplace_back(shortcircuit_entity); + } - const auto label_shortcircuit = IR::Entity::Label(m_current_label++); - auto shortcircuit_entity = IR::Entity::Goto(label_shortcircuit, maybe_shortcircuit.value()); - page(p).emplace_back(shortcircuit_entity); + page(p).emplace_back(label_shortcircuit); + } - for (std::size_t i = 2, end = x.constList().size(); i < end; ++i) - { - if (!nodeProducesOutput(x.list()[i])) - buildAndThrowError( - fmt::format( - "Can not use `{}' inside a `{}' expression, as it doesn't return a value", - x.list()[i].repr(), name), - x.list()[i]); - compileExpression(x.list()[i], p, false, false); - if (i + 1 != end) - page(p).emplace_back(shortcircuit_entity); - } + void ASTLowerer::handleOperator(Node& x, const Page p, const Instruction op) + { + constexpr std::size_t start_index = 1; + const Node& node = x.constList()[0]; + const auto op_name = Language::operators[static_cast(op - FIRST_OPERATOR)]; - page(p).emplace_back(label_shortcircuit); - } - else if (!maybe_operator.has_value()) - { - if (is_terminal && node.nodeType() == NodeType::Symbol && isFunctionCallingItself(node.string())) - { - pushFunctionCallArguments(x, p, /* is_tail_call= */ true); - // jump to the top of the function - page(p).emplace_back(JUMP, 0_u16); - page(p).back().setSourceLocation(node.filename(), node.position().start.line); - return; // skip the potential Instruction::POP at the end - } + // push arguments on current page + std::size_t exp_count = 0; + for (std::size_t index = start_index, size = x.constList().size(); index < size; ++index) + { + const bool is_breakpoint = isBreakpoint(x.constList()[index]); + if (nodeProducesOutput(x.constList()[index]) || is_breakpoint) + compileExpression(x.list()[index], p, false, false); else - { - if (!nodeProducesOutput(node)) - buildAndThrowError(fmt::format("Can not call `{}', as it doesn't return a value", node.repr()), node); + buildAndThrowError(fmt::format("Invalid node inside call to operator `{}'", node.repr()), x.constList()[index]); - const auto proc_page = createNewCodePage(/* temp= */ true); + if (!is_breakpoint) + exp_count++; - // compile the function resolution to a separate page - if (node.nodeType() == NodeType::Symbol && isFunctionCallingItself(node.string())) - { - // The function is trying to call itself, but this isn't a tail call. - // We can skip the LOAD_FAST function_name and directly push the current - // function page, which will be quicker than a local variable resolution. - // We set its argument to the symbol id of the function we are calling, - // so that the VM knows the name of the last called function. - page(proc_page).emplace_back(GET_CURRENT_PAGE_ADDR, addSymbol(node)); - } - else - { - // closure chains have been handled (eg: closure.field.field.function) - compileExpression(node, proc_page, false, false); // storing proc - } + // in order to be able to handle things like (op A B C D...) + // which should be transformed into A B op C op D op... + if (exp_count >= 2 && !isTernaryInst(op) && !is_breakpoint) + page(p).emplace_back(op); + } - if (m_temp_pages.back().empty()) - buildAndThrowError(fmt::format("Can not call {}", x.constList()[0].repr()), x); + if (isBreakpoint(x)) + { + if (exp_count > 1) + buildAndThrowError(fmt::format("`{}' expected at most one argument, but was called with {}", op_name, exp_count), x.constList()[0]); + page(p).emplace_back(op, exp_count); + } + else if (isUnaryInst(op)) + { + if (exp_count != 1) + buildAndThrowError(fmt::format("`{}' expected one argument, but was called with {}", op_name, exp_count), x.constList()[0]); + page(p).emplace_back(op); + } + else if (isTernaryInst(op)) + { + if (exp_count != 3) + buildAndThrowError(fmt::format("`{}' expected three arguments, but was called with {}", op_name, exp_count), x.constList()[0]); + page(p).emplace_back(op); + } + else if (exp_count <= 1) + buildAndThrowError(fmt::format("`{}' expected two arguments, but was called with {}", op_name, exp_count), x.constList()[0]); - const auto label_return = IR::Entity::Label(m_current_label++); - page(p).emplace_back(IR::Entity::Goto(label_return, PUSH_RETURN_ADDRESS)); - page(p).back().setSourceLocation(x.filename(), x.position().start.line); + // need to check we didn't push the (op A B C D...) things for operators not supporting it + if (exp_count > 2 && !isRepeatableOperation(op) && !isTernaryInst(op)) + buildAndThrowError(fmt::format("`{}' requires 2 arguments, but got {}.", op_name, exp_count), x); - pushFunctionCallArguments(x, p, /* is_tail_call= */ false); - // push proc from temp page - for (const auto& inst : m_temp_pages.back()) - page(p).push_back(inst); - m_temp_pages.pop_back(); + page(p).back().setSourceLocation(x.filename(), x.position().start.line); + } - // number of arguments - std::size_t args_count = 0; - for (auto it = x.constList().begin() + start_index, it_end = x.constList().end(); it != it_end; ++it) - { - if (it->nodeType() != NodeType::Capture && !isBreakpoint(*it)) - args_count++; - } - // call the procedure - page(p).emplace_back(CALL, args_count); - page(p).back().setSourceLocation(node.filename(), node.position().start.line); + bool ASTLowerer::handleFunctionCall(Node& x, const Page p, const bool is_terminal) + { + constexpr std::size_t start_index = 1; + Node& node = x.list()[0]; - // patch the PUSH_RETURN_ADDRESS instruction with the return location (IP=CALL instruction IP) - page(p).emplace_back(label_return); - } - } - else // operator + if (is_terminal && node.nodeType() == NodeType::Symbol && isFunctionCallingItself(node.string())) { - // retrieve operator - const auto op = maybe_operator.value(); - const auto op_name = Language::operators[static_cast(op - FIRST_OPERATOR)]; + pushFunctionCallArguments(x, p, /* is_tail_call= */ true); - if (op == BREAKPOINT) - is_result_unused = false; + // jump to the top of the function + page(p).emplace_back(JUMP, 0_u16); + page(p).back().setSourceLocation(node.filename(), node.position().start.line); + return true; // skip the potential Instruction::POP at the end + } - // push arguments on current page - std::size_t exp_count = 0; - for (std::size_t index = start_index, size = x.constList().size(); index < size; ++index) - { - const bool is_breakpoint = isBreakpoint(x.constList()[index]); - if (nodeProducesOutput(x.constList()[index]) || is_breakpoint) - compileExpression(x.list()[index], p, false, false); - else - buildAndThrowError(fmt::format("Invalid node inside call to operator `{}'", node.repr()), x.constList()[index]); + if (!nodeProducesOutput(node)) + buildAndThrowError(fmt::format("Can not call `{}', as it doesn't return a value", node.repr()), node); - if (!is_breakpoint) - exp_count++; + const auto proc_page = createNewCodePage(/* temp= */ true); - // in order to be able to handle things like (op A B C D...) - // which should be transformed into A B op C op D op... - if (exp_count >= 2 && !isTernaryInst(op) && !is_breakpoint) - page(p).emplace_back(op); - } + // compile the function resolution to a separate page + if (node.nodeType() == NodeType::Symbol && isFunctionCallingItself(node.string())) + { + // The function is trying to call itself, but this isn't a tail call. + // We can skip the LOAD_FAST function_name and directly push the current + // function page, which will be quicker than a local variable resolution. + // We set its argument to the symbol id of the function we are calling, + // so that the VM knows the name of the last called function. + page(proc_page).emplace_back(GET_CURRENT_PAGE_ADDR, addSymbol(node)); + } + else + { + // closure chains have been handled (eg: closure.field.field.function) + compileExpression(node, proc_page, false, false); // storing proc + } - if (isBreakpoint(x)) - { - if (exp_count > 1) - buildAndThrowError(fmt::format("`{}' expected at most one argument, but was called with {}", op_name, exp_count), x.constList()[0]); - page(p).emplace_back(op, exp_count); - } - else if (isUnaryInst(op)) - { - if (exp_count != 1) - buildAndThrowError(fmt::format("`{}' expected one argument, but was called with {}", op_name, exp_count), x.constList()[0]); - page(p).emplace_back(op); - } - else if (isTernaryInst(op)) - { - if (exp_count != 3) - buildAndThrowError(fmt::format("`{}' expected three arguments, but was called with {}", op_name, exp_count), x.constList()[0]); - page(p).emplace_back(op); - } - else if (exp_count <= 1) - buildAndThrowError(fmt::format("`{}' expected two arguments, but was called with {}", op_name, exp_count), x.constList()[0]); + if (m_temp_pages.back().empty()) + buildAndThrowError(fmt::format("Can not call {}", x.constList()[0].repr()), x); + + const auto label_return = IR::Entity::Label(m_current_label++); + page(p).emplace_back(IR::Entity::Goto(label_return, PUSH_RETURN_ADDRESS)); + page(p).back().setSourceLocation(x.filename(), x.position().start.line); - // need to check we didn't push the (op A B C D...) things for operators not supporting it - if (exp_count > 2 && !isRepeatableOperation(op) && !isTernaryInst(op)) - buildAndThrowError(fmt::format("`{}' requires 2 arguments, but got {}.", op_name, exp_count), x); + pushFunctionCallArguments(x, p, /* is_tail_call= */ false); + // push proc from temp page + for (const auto& inst : m_temp_pages.back()) + page(p).push_back(inst); + m_temp_pages.pop_back(); - page(p).back().setSourceLocation(x.filename(), x.position().start.line); + // number of arguments + std::size_t args_count = 0; + for (auto it = x.constList().begin() + start_index, it_end = x.constList().end(); it != it_end; ++it) + { + if (it->nodeType() != NodeType::Capture && !isBreakpoint(*it)) + args_count++; } + // call the procedure + page(p).emplace_back(CALL, args_count); + page(p).back().setSourceLocation(node.filename(), node.position().start.line); - if (is_result_unused) - page(p).emplace_back(POP); + // patch the PUSH_RETURN_ADDRESS instruction with the return location (IP=CALL instruction IP) + page(p).emplace_back(label_return); + return false; // we didn't compile a tail call } uint16_t ASTLowerer::addSymbol(const Node& sym) From e31d4d6e796bd87e04193c8680ee6a9f63de6483 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Wed, 11 Feb 2026 18:34:24 +0100 Subject: [PATCH 6/7] docs: move the builtins docs to docs/arkdoc/ --- {src/arkreactor/Builtins => docs/arkdoc}/Builtins.txt | 0 {src/arkreactor/Builtins => docs/arkdoc}/Dict.txt | 0 {src/arkreactor/Builtins => docs/arkdoc}/List.txt | 0 {src/arkreactor/Builtins => docs/arkdoc}/Math.txt | 0 {src/arkreactor/Builtins => docs/arkdoc}/String.txt | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {src/arkreactor/Builtins => docs/arkdoc}/Builtins.txt (100%) rename {src/arkreactor/Builtins => docs/arkdoc}/Dict.txt (100%) rename {src/arkreactor/Builtins => docs/arkdoc}/List.txt (100%) rename {src/arkreactor/Builtins => docs/arkdoc}/Math.txt (100%) rename {src/arkreactor/Builtins => docs/arkdoc}/String.txt (100%) diff --git a/src/arkreactor/Builtins/Builtins.txt b/docs/arkdoc/Builtins.txt similarity index 100% rename from src/arkreactor/Builtins/Builtins.txt rename to docs/arkdoc/Builtins.txt diff --git a/src/arkreactor/Builtins/Dict.txt b/docs/arkdoc/Dict.txt similarity index 100% rename from src/arkreactor/Builtins/Dict.txt rename to docs/arkdoc/Dict.txt diff --git a/src/arkreactor/Builtins/List.txt b/docs/arkdoc/List.txt similarity index 100% rename from src/arkreactor/Builtins/List.txt rename to docs/arkdoc/List.txt diff --git a/src/arkreactor/Builtins/Math.txt b/docs/arkdoc/Math.txt similarity index 100% rename from src/arkreactor/Builtins/Math.txt rename to docs/arkdoc/Math.txt diff --git a/src/arkreactor/Builtins/String.txt b/docs/arkdoc/String.txt similarity index 100% rename from src/arkreactor/Builtins/String.txt rename to docs/arkdoc/String.txt From 00c850ec3d3fe1af05576133f14685405e88b613 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Wed, 11 Feb 2026 18:55:04 +0100 Subject: [PATCH 7/7] docs: adding arkdoc docstrings for append and friends, as well as assert --- docs/arkdoc/Builtins.txt | 11 ++++++ docs/arkdoc/List.txt | 80 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/docs/arkdoc/Builtins.txt b/docs/arkdoc/Builtins.txt index f9415e0c..27f04681 100644 --- a/docs/arkdoc/Builtins.txt +++ b/docs/arkdoc/Builtins.txt @@ -34,6 +34,17 @@ * =end #-- +--# +* @name assert +* @brief Interrupt the execution if a given condition is false +* @param cond condition +* @param message string to display +* =begin +* (let a 5) +* (assert (>= a 4) "'a' must be at least 4") +* =end +#-- + --# * @name nil? * @brief Check if a value is nil diff --git a/docs/arkdoc/List.txt b/docs/arkdoc/List.txt index f0d58976..ef846fab 100644 --- a/docs/arkdoc/List.txt +++ b/docs/arkdoc/List.txt @@ -22,6 +22,86 @@ * =end #-- +--# +* @name append +* @brief Add an element to a list, returning a new list +* @param lst a list +* @param element +* =begin +* (print (append [] 1)) # [1] +* (print (append [1 2] [3])) # [1 2 [3]] +* =end +#-- + +--# +* @name append! +* @brief Add an element to a list, modifying it in place. It doesn't return anything +* @param lst a list. Must be mutable +* @param element +* =begin +* (mut lst [1 2]) +* (append! lst 1) +* (print lst) # [1 2 1] +* (append! lst [3]) +* (print lst) # [1 2 1 [3]] +* =end +#-- + +--# +* @name concat +* @brief Concatenate two lists, returning a new list +* @param lhs a list +* @param rhs a second list +* =begin +* (print (append [] 1)) # [1] +* (print (append [1 2] [3])) # [1 2 [3]] +* =end +#-- + +--# +* @name concat! +* @brief Concatenate two lists in place, modifying the first one it in place. It doesn't return anything +* @param lst a list. Must be mutable +* @param more another list +* =begin +* (mut lst [1 2]) +* (concat! lst [1]) +* (print lst) # [1 2 1] +* (concat! lst [3]) +* (print lst) # [1 2 1 3] +* =end +#-- + +--# +* @name pop +* @brief Return a new list without the element at index +* @details Supports negative indices, -1 being the end. +* @param lst a list +* @param index number +* =begin +* (print (pop [1] 0)) # [] +* (print (pop [1 2 3] -2)) # [1 3] +* (print (pop [1 2 3] 2)) # [1 2] +* =end +#-- + +--# +* @name pop! +* @brief Remove an element from a list in place, given its index. It doesn't return anything +* @details Supports negative indices, -1 being the end. +* @param lst a list. Must be mutable +* @param index number +* =begin +* (mut lst [1 2 3 4 5]) +* (pop! lst 0) +* (print lst) # [2 3 4 5] +* (pop! lst -2) +* (print lst) # [2 3 5] +* (pop! lst 2) +* (print lst) # [2 3] +* =end +#-- + --# * @name head * @brief Return the first element of a list, or nil if empty