From 32134cfd2ff0085ffb90f64a72f967d80064d223 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 5 Jan 2026 18:21:21 +0100 Subject: [PATCH 1/8] feat(instructions, vm, compiler)!: adding BREAKPOINT instruction and turning ASSERT into a builtin --- CHANGELOG.md | 7 ++++ CMakeLists.txt | 3 +- include/Ark/Builtins/Builtins.hpp | 1 + include/Ark/Compiler/Common.hpp | 5 +-- include/Ark/Compiler/Instructions.hpp | 40 +++++++++---------- src/arkreactor/Builtins/Builtins.cpp | 1 + src/arkreactor/Builtins/System.cpp | 15 +++++++ .../Compiler/Lowerer/ASTLowerer.cpp | 6 +-- src/arkreactor/VM/VM.cpp | 28 +++++-------- tests/unittests/Suites/CompilerSuite.cpp | 4 ++ .../CompilerSuite/ir/99bottles.expected | 4 +- .../CompilerSuite/ir/plugin.expected | 11 +++-- .../optimized_ir/99bottles.expected | 4 +- .../optimized_ir/builtins.expected | 2 +- .../compileTime/assert_too_many_args.expected | 5 --- .../assert_too_many_args.ark | 0 .../runtime/assert_too_many_args.expected | 14 +++++++ 17 files changed, 91 insertions(+), 59 deletions(-) delete mode 100644 tests/unittests/resources/DiagnosticsSuite/compileTime/assert_too_many_args.expected rename tests/unittests/resources/DiagnosticsSuite/{compileTime => runtime}/assert_too_many_args.ark (100%) create mode 100644 tests/unittests/resources/DiagnosticsSuite/runtime/assert_too_many_args.expected diff --git a/CHANGELOG.md b/CHANGELOG.md index b26c27c6f..9899b5615 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## [Unreleased changes] - 20XX-XX-XX +### Breaking changes +- `assert` is no longer an instruction but a builtin + +### Added +- added `BREAKPOINT` instruction + ## [4.1.2] - 2026-01-09 ### Added - the repl prints the output of the last expression it ran diff --git a/CMakeLists.txt b/CMakeLists.txt index bf55d4219..4e5c121c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ if (NOT ARK_EMSCRIPTEN) endif () include(cmake/CPM.cmake) +message(STATUS "ArkScript version ${ARK_VERSION_MAJOR}.${ARK_VERSION_MINOR}.${ARK_VERSION_PATCH}-${ARK_COMMIT}") ##################################################### # Configure files @@ -55,9 +56,7 @@ include(cmake/CPM.cmake) configure_file( ${ark_SOURCE_DIR}/Installer.iss.in ${ark_SOURCE_DIR}/Installer.iss) - ## Ark/Constants.hpp -message(STATUS "ArkScript version ${ARK_VERSION_MAJOR}.${ARK_VERSION_MINOR}.${ARK_VERSION_PATCH}-${ARK_COMMIT}") configure_file( ${ark_SOURCE_DIR}/include/Ark/Constants.hpp.in ${ark_SOURCE_DIR}/include/Ark/Constants.hpp) diff --git a/include/Ark/Builtins/Builtins.hpp b/include/Ark/Builtins/Builtins.hpp index c2fa961dd..ba49bf270 100644 --- a/include/Ark/Builtins/Builtins.hpp +++ b/include/Ark/Builtins/Builtins.hpp @@ -70,6 +70,7 @@ namespace Ark::internal::Builtins ARK_BUILTIN(system_); ARK_BUILTIN(sleep); ARK_BUILTIN(exit_); + ARK_BUILTIN(assert_); } namespace String diff --git a/include/Ark/Compiler/Common.hpp b/include/Ark/Compiler/Common.hpp index c7cb98ae5..7a7c8eddc 100644 --- a/include/Ark/Compiler/Common.hpp +++ b/include/Ark/Compiler/Common.hpp @@ -153,11 +153,10 @@ namespace Ark::internal // This list is related to include/Ark/Compiler/Instructions.hpp // from FIRST_OPERATOR, to LAST_OPERATOR // The order is very important - constexpr std::array operators = { + constexpr std::array operators = { "+", "-", "*", "/", ">", "<", "<=", ">=", "!=", "=", - "len", "empty?", "tail", "head", - "nil?", "assert", + "len", "empty?", "tail", "head", "nil?", "toNumber", "toString", "@", "@@", "mod", "type", "hasField", diff --git a/include/Ark/Compiler/Instructions.hpp b/include/Ark/Compiler/Instructions.hpp index 94c460b7c..8028ee1f3 100644 --- a/include/Ark/Compiler/Instructions.hpp +++ b/include/Ark/Compiler/Instructions.hpp @@ -171,55 +171,55 @@ namespace Ark::internal // @role Push the current page address as a value on the stack GET_CURRENT_PAGE_ADDR = 0x25, - FIRST_OPERATOR = 0x26, + // @role Pop the top of the stack, if it's true, trigger the debugger + BREAKPOINT = 0x26, + + FIRST_OPERATOR = 0x27, // @role Push `TS1 + TS` - ADD = 0x26, + ADD = 0x27, // @role Push `TS1 - TS` - SUB = 0x27, + SUB = 0x28, // @role Push `TS1 * TS` - MUL = 0x28, + MUL = 0x29, // @role Push `TS1 / TS` - DIV = 0x29, + DIV = 0x2a, // @role Push `TS1 > TS` - GT = 0x2a, + GT = 0x2b, // @role Push `TS1 < TS` - LT = 0x2b, + LT = 0x2c, // @role Push `TS1 <= TS` - LE = 0x2c, + LE = 0x2d, // @role Push `TS1 >= TS` - GE = 0x2d, + GE = 0x2e, // @role Push `TS1 != TS` - NEQ = 0x2e, + NEQ = 0x2f, // @role Push `TS1 == TS` - EQ = 0x2f, + EQ = 0x30, // @role Push `len(TS)`, TS must be a list - LEN = 0x30, + LEN = 0x31, // @role Push `empty?(TS)`, TS must be a list or string - IS_EMPTY = 0x31, + IS_EMPTY = 0x32, // @role Push `tail(TS)`, all the elements of TS except the first one. TS must be a list or string - TAIL = 0x32, + TAIL = 0x33, // @role Push `head(TS)`, the first element of TS or nil if empty. TS must be a list or string - HEAD = 0x33, + HEAD = 0x34, // @role Push true if TS is nil, false otherwise - IS_NIL = 0x34, - - // @role Throw an exception if TS1 is false, and display TS (must be a string). Do not push anything on the stack - ASSERT = 0x35, + IS_NIL = 0x35, // @role Convert TS to number (must be a string) TO_NUM = 0x36, @@ -491,6 +491,7 @@ namespace Ark::internal "RESET_SCOPE_JUMP", "POP_SCOPE", "GET_CURRENT_PAGE_ADDR", + "BREAKPOINT", // operators "ADD", "SUB", @@ -507,7 +508,6 @@ namespace Ark::internal "TAIL", "HEAD", "IS_NIL", - "ASSERT", "TO_NUM", "TO_STR", "AT", diff --git a/src/arkreactor/Builtins/Builtins.cpp b/src/arkreactor/Builtins/Builtins.cpp index d689f83e6..4738f8960 100644 --- a/src/arkreactor/Builtins/Builtins.cpp +++ b/src/arkreactor/Builtins/Builtins.cpp @@ -57,6 +57,7 @@ namespace Ark::internal::Builtins { "builtin__sys:exec", Value(System::system_) }, { "builtin__sys:sleep", Value(System::sleep) }, { "builtin__sys:exit", Value(System::exit_) }, + { "assert", Value(System::assert_) }, // String { "format", Value(String::format) }, diff --git a/src/arkreactor/Builtins/System.cpp b/src/arkreactor/Builtins/System.cpp index 6580ee83f..37446ab40 100644 --- a/src/arkreactor/Builtins/System.cpp +++ b/src/arkreactor/Builtins/System.cpp @@ -80,4 +80,19 @@ namespace Ark::internal::Builtins::System vm->exit(static_cast(n[0].number())); return nil; } + + // cppcheck-suppress constParameterReference + Value assert_(std::vector& n, VM* vm [[maybe_unused]]) + { + if (!types::check(n, ValueType::Any, ValueType::String)) + throw types::TypeCheckingError( + "assert", + { { types::Contract { { types::Typedef("expr", ValueType::Any), types::Typedef("message", ValueType::String) } } } }, + n); + + if (n[0] == Builtins::falseSym) + throw AssertionFailed(n[1].stringRef()); + + return nil; + } } diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 6df66aa4d..057a15e0d 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -86,10 +86,10 @@ namespace Ark::internal (node.constList()[0].keyword() == Keyword::If && nodeProducesOutput(node.constList()[2]) && (node.constList().size() == 3 || nodeProducesOutput(node.constList()[3]))); - // in place list instruction, as well as assert, do not produce values + // in place list instruction, as well as breakpoint, do not produce values if (node.nodeType() == NodeType::List && !node.constList().empty() && node.constList()[0].nodeType() == NodeType::Symbol) return std::ranges::find(Language::UpdateRef, node.constList().front().string()) == Language::UpdateRef.end() && - node.constList().front().string() != "assert"; + node.constList().front().string() != "breakpoint"; return true; // any other node, function call, symbol, number... } @@ -686,7 +686,7 @@ namespace Ark::internal // retrieve operator auto op = maybe_operator.value(); - if (op == ASSERT) + if (op == BREAKPOINT) is_result_unused = false; // push arguments on current page diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index d79958542..430fbfe80 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -606,6 +606,7 @@ namespace Ark &&TARGET_RESET_SCOPE_JUMP, &&TARGET_POP_SCOPE, &&TARGET_GET_CURRENT_PAGE_ADDR, + &&TARGET_BREAKPOINT, &&TARGET_ADD, &&TARGET_SUB, &&TARGET_MUL, @@ -621,7 +622,6 @@ namespace Ark &&TARGET_TAIL, &&TARGET_HEAD, &&TARGET_IS_NIL, - &&TARGET_ASSERT, &&TARGET_TO_NUM, &&TARGET_TO_STR, &&TARGET_AT, @@ -1180,6 +1180,16 @@ namespace Ark DISPATCH(); } + TARGET(BREAKPOINT) + { + { + const Value cond = *popAndResolveAsPtr(context); + if (cond == Builtins::trueSym) // todo: trigger debugger + {} + } + DISPATCH(); + } + #pragma endregion #pragma region "Operators" @@ -1344,22 +1354,6 @@ namespace Ark DISPATCH(); } - TARGET(ASSERT) - { - Value* const b = popAndResolveAsPtr(context); - Value* const a = popAndResolveAsPtr(context); - - if (b->valueType() != ValueType::String) - throw types::TypeCheckingError( - "assert", - { { types::Contract { { types::Typedef("expr", ValueType::Any), types::Typedef("message", ValueType::String) } } } }, - { *a, *b }); - - if (*a == Builtins::falseSym) - throw AssertionFailed(b->stringRef()); - DISPATCH(); - } - TARGET(TO_NUM) { const Value* a = popAndResolveAsPtr(context); diff --git a/tests/unittests/Suites/CompilerSuite.cpp b/tests/unittests/Suites/CompilerSuite.cpp index 464e79492..e167fc778 100644 --- a/tests/unittests/Suites/CompilerSuite.cpp +++ b/tests/unittests/Suites/CompilerSuite.cpp @@ -94,6 +94,8 @@ ut::suite<"Compiler"> compiler_suite = [] { Ark::Utils::ltrim(Ark::Utils::rtrim(ir)); expectOrDiff(data.expected, ir); + if (shouldWriteNewDiffsTofile() && data.expected != ir) + updateExpectedFile(data, ir); }; }); }; @@ -116,6 +118,8 @@ ut::suite<"Compiler"> compiler_suite = [] { Ark::Utils::ltrim(Ark::Utils::rtrim(ir)); expectOrDiff(data.expected, ir); + if (shouldWriteNewDiffsTofile() && data.expected != ir) + updateExpectedFile(data, ir); }; }); }; diff --git a/tests/unittests/resources/CompilerSuite/ir/99bottles.expected b/tests/unittests/resources/CompilerSuite/ir/99bottles.expected index 1ff09c922..e5132d226 100644 --- a/tests/unittests/resources/CompilerSuite/ir/99bottles.expected +++ b/tests/unittests/resources/CompilerSuite/ir/99bottles.expected @@ -37,7 +37,7 @@ page_0 LOAD_FAST 4 LOAD_FAST 4 LOAD_CONST 3 - BUILTIN 26 + BUILTIN 27 CALL 3 .L7: BUILTIN 9 @@ -52,7 +52,7 @@ page_0 PUSH_RETURN_ADDRESS L9 LOAD_FAST 4 LOAD_CONST 4 - BUILTIN 26 + BUILTIN 27 CALL 2 .L9: BUILTIN 9 diff --git a/tests/unittests/resources/CompilerSuite/ir/plugin.expected b/tests/unittests/resources/CompilerSuite/ir/plugin.expected index abfa2f787..4932688a2 100644 --- a/tests/unittests/resources/CompilerSuite/ir/plugin.expected +++ b/tests/unittests/resources/CompilerSuite/ir/plugin.expected @@ -1,12 +1,15 @@ page_0 PLUGIN 0 - LOAD_CONST 1 PUSH_RETURN_ADDRESS L0 + LOAD_CONST 1 LOAD_CONST 2 + PUSH_RETURN_ADDRESS L1 + LOAD_CONST 3 LOAD_FAST 0 CALL 1 -.L0: +.L1: EQ 0 - LOAD_CONST 3 - ASSERT 0 + BUILTIN 26 + CALL 2 +.L0: HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected index 485b5b7b1..4386e4110 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected @@ -37,7 +37,7 @@ page_0 LOAD_FAST 13 LOAD_FAST 13 LOAD_CONST 6 - CALL_BUILTIN 26, 3 + CALL_BUILTIN 27, 3 .L10: CALL_BUILTIN 9, 1 .L9: @@ -47,7 +47,7 @@ page_0 PUSH_RETURN_ADDRESS L12 LOAD_FAST 13 LOAD_CONST 7 - CALL_BUILTIN 26, 2 + CALL_BUILTIN 27, 2 .L12: CALL_BUILTIN 9, 1 .L11: diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/builtins.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/builtins.expected index 8453e41f4..a8c4a2277 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/builtins.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/builtins.expected @@ -5,7 +5,7 @@ page_0 HALT 0 page_1 - CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 54, 1 + CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 55, 1 .L0: RET 0 HALT 0 diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/assert_too_many_args.expected b/tests/unittests/resources/DiagnosticsSuite/compileTime/assert_too_many_args.expected deleted file mode 100644 index 80d04e7b2..000000000 --- a/tests/unittests/resources/DiagnosticsSuite/compileTime/assert_too_many_args.expected +++ /dev/null @@ -1,5 +0,0 @@ -In file tests/unittests/resources/DiagnosticsSuite/compileTime/assert_too_many_args.ark:1 - 1 | (assert true 1 2 3) - | ^~~~~~~~~~~~~~~~~~ - 2 | - `assert' requires 2 arguments, but got 4. diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/assert_too_many_args.ark b/tests/unittests/resources/DiagnosticsSuite/runtime/assert_too_many_args.ark similarity index 100% rename from tests/unittests/resources/DiagnosticsSuite/compileTime/assert_too_many_args.ark rename to tests/unittests/resources/DiagnosticsSuite/runtime/assert_too_many_args.ark diff --git a/tests/unittests/resources/DiagnosticsSuite/runtime/assert_too_many_args.expected b/tests/unittests/resources/DiagnosticsSuite/runtime/assert_too_many_args.expected new file mode 100644 index 000000000..504cfae39 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/runtime/assert_too_many_args.expected @@ -0,0 +1,14 @@ +Function assert expected 2 arguments but got 4 +Call + ↳ (assert true 1 2 3) +Signature + ↳ (assert expr message) +Arguments + → `expr' (expected any) ✓ + → `message' (expected String), got 1 (Number) + → unexpected additional args: 2 (Number), 3 (Number) + +In file tests/unittests/resources/DiagnosticsSuite/runtime/assert_too_many_args.ark:1 + 1 | (assert true 1 2 3) + | ^~~~~~~~~~~~~~~~~~ + 2 | From 236239e9cd3132a5dead7fc6383eddddb9de907e Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 5 Jan 2026 18:40:00 +0100 Subject: [PATCH 2/8] feat(error messages): enhance operator arity error messages by adding the operator name in the message --- include/Ark/Compiler/Common.hpp | 3 ++- include/Ark/Compiler/Instructions.hpp | 6 +++--- src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp | 17 +++++++---------- .../compileTime/at_at_not_enough_args.expected | 2 +- .../compileTime/no_args.expected | 2 +- .../compileTime/ope_not_enough_args.expected | 2 +- .../compileTime/type_no_args.expected | 2 +- 7 files changed, 16 insertions(+), 18 deletions(-) diff --git a/include/Ark/Compiler/Common.hpp b/include/Ark/Compiler/Common.hpp index 7a7c8eddc..1cfa0a72c 100644 --- a/include/Ark/Compiler/Common.hpp +++ b/include/Ark/Compiler/Common.hpp @@ -153,7 +153,8 @@ namespace Ark::internal // This list is related to include/Ark/Compiler/Instructions.hpp // from FIRST_OPERATOR, to LAST_OPERATOR // The order is very important - constexpr std::array operators = { + constexpr std::array operators = { + "breakpoint", "+", "-", "*", "/", ">", "<", "<=", ">=", "!=", "=", "len", "empty?", "tail", "head", "nil?", diff --git a/include/Ark/Compiler/Instructions.hpp b/include/Ark/Compiler/Instructions.hpp index 8028ee1f3..99e636091 100644 --- a/include/Ark/Compiler/Instructions.hpp +++ b/include/Ark/Compiler/Instructions.hpp @@ -171,11 +171,11 @@ namespace Ark::internal // @role Push the current page address as a value on the stack GET_CURRENT_PAGE_ADDR = 0x25, + FIRST_OPERATOR = 0x26, + // @role Pop the top of the stack, if it's true, trigger the debugger BREAKPOINT = 0x26, - FIRST_OPERATOR = 0x27, - // @role Push `TS1 + TS` ADD = 0x27, @@ -491,8 +491,8 @@ namespace Ark::internal "RESET_SCOPE_JUMP", "POP_SCOPE", "GET_CURRENT_PAGE_ADDR", - "BREAKPOINT", // operators + "BREAKPOINT", "ADD", "SUB", "MUL", diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 057a15e0d..d5d81ed80 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -97,6 +97,7 @@ namespace Ark::internal { switch (inst) { + case BREAKPOINT: [[fallthrough]]; case NOT: [[fallthrough]]; case LEN: [[fallthrough]]; case IS_EMPTY: [[fallthrough]]; @@ -684,7 +685,8 @@ namespace Ark::internal else // operator { // retrieve operator - auto op = maybe_operator.value(); + const auto op = maybe_operator.value(); + const auto op_name = Language::operators[static_cast(op - FIRST_OPERATOR)]; if (op == BREAKPOINT) is_result_unused = false; @@ -710,17 +712,17 @@ namespace Ark::internal if (isUnaryInst(op)) { if (exp_count != 1) - buildAndThrowError(fmt::format("Operator needs one argument, but was called with {}", exp_count), x.constList()[0]); + 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("Operator needs three arguments, but was called with {}", exp_count), x.constList()[0]); + 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("Operator needs two arguments, but was called with {}", exp_count), x.constList()[0]); + 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); @@ -739,12 +741,7 @@ namespace Ark::internal break; default: - buildAndThrowError( - fmt::format( - "`{}' requires 2 arguments, but got {}.", - Language::operators[static_cast(op - FIRST_OPERATOR)], - exp_count), - x); + buildAndThrowError(fmt::format("`{}' requires 2 arguments, but got {}.", op_name, exp_count), x); } } } diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/at_at_not_enough_args.expected b/tests/unittests/resources/DiagnosticsSuite/compileTime/at_at_not_enough_args.expected index 6abcfcced..ddfdc6ef8 100644 --- a/tests/unittests/resources/DiagnosticsSuite/compileTime/at_at_not_enough_args.expected +++ b/tests/unittests/resources/DiagnosticsSuite/compileTime/at_at_not_enough_args.expected @@ -3,4 +3,4 @@ In file tests/unittests/resources/DiagnosticsSuite/compileTime/at_at_not_enough_ 2 | (print (@@ lst 1)) | ^~ 3 | - Operator needs three arguments, but was called with 2 + `@@' expected three arguments, but was called with 2 diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/no_args.expected b/tests/unittests/resources/DiagnosticsSuite/compileTime/no_args.expected index 24b790486..e588657f8 100644 --- a/tests/unittests/resources/DiagnosticsSuite/compileTime/no_args.expected +++ b/tests/unittests/resources/DiagnosticsSuite/compileTime/no_args.expected @@ -2,4 +2,4 @@ In file tests/unittests/resources/DiagnosticsSuite/compileTime/no_args.ark:1 1 | ((+)) | ^ 2 | - Operator needs two arguments, but was called with 0 + `+' expected two arguments, but was called with 0 diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/ope_not_enough_args.expected b/tests/unittests/resources/DiagnosticsSuite/compileTime/ope_not_enough_args.expected index 3d6b3c8f3..0b0483f49 100644 --- a/tests/unittests/resources/DiagnosticsSuite/compileTime/ope_not_enough_args.expected +++ b/tests/unittests/resources/DiagnosticsSuite/compileTime/ope_not_enough_args.expected @@ -2,4 +2,4 @@ In file tests/unittests/resources/DiagnosticsSuite/compileTime/ope_not_enough_ar 1 | (print (!= 1)) | ^~ 2 | - Operator needs two arguments, but was called with 1 + `!=' expected two arguments, but was called with 1 diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/type_no_args.expected b/tests/unittests/resources/DiagnosticsSuite/compileTime/type_no_args.expected index b8f7607ea..b5e865ba4 100644 --- a/tests/unittests/resources/DiagnosticsSuite/compileTime/type_no_args.expected +++ b/tests/unittests/resources/DiagnosticsSuite/compileTime/type_no_args.expected @@ -2,4 +2,4 @@ In file tests/unittests/resources/DiagnosticsSuite/compileTime/type_no_args.ark: 1 | (print (type)) | ^~~~ 2 | - Operator needs one argument, but was called with 0 + `type' expected one argument, but was called with 0 From 1ff32eec47e79d0833ef588021923317aadcb760 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 5 Jan 2026 19:05:40 +0100 Subject: [PATCH 3/8] feat(ast lowerer): breakpoints can be placed anywhere without disturbing the argument counts for functions and operators, as they produce nothing --- include/Ark/Compiler/Lowerer/ASTLowerer.hpp | 8 ++ .../Compiler/Lowerer/ASTLowerer.cpp | 19 +++- .../CompilerSuite/ir/breakpoints.ark | 19 ++++ .../CompilerSuite/ir/breakpoints.expected | 105 ++++++++++++++++++ 4 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 tests/unittests/resources/CompilerSuite/ir/breakpoints.ark create mode 100644 tests/unittests/resources/CompilerSuite/ir/breakpoints.expected diff --git a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp index de9971fb2..36ece2193 100644 --- a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp +++ b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp @@ -128,6 +128,14 @@ namespace Ark::internal */ static std::optional getListInstruction(const std::string& name) noexcept; + /** + * Checks if a node is a list and is a call to 'breakpoint' + * @param node node to check + * @return true if the node is a 'breakpoint' call: (breakpoint ) + * @return false otherwise + */ + static bool isBreakpoint(const Node& node); + /** * Checks if a node is a list and has a keyboard as its first node, indicating if it's producing a value on the stack or not * @param node node to check diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index d5d81ed80..e47c39e15 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -75,6 +75,13 @@ namespace Ark::internal return std::nullopt; } + bool ASTLowerer::isBreakpoint(const Node& node) + { + if (node.nodeType() == NodeType::List && !node.constList().empty() && node.constList()[0].nodeType() == NodeType::Symbol) + return node.constList().front().string() == "breakpoint"; + return false; + } + bool ASTLowerer::nodeProducesOutput(const Node& node) { if (node.nodeType() == NodeType::List && !node.constList().empty() && node.constList()[0].nodeType() == NodeType::Keyword) @@ -557,7 +564,8 @@ namespace Ark::internal // value for argument b, but loaded it as a reference. for (Node& value : std::ranges::drop_view(call.list(), 1) | std::views::reverse) { - if (nodeProducesOutput(value)) + // FIXME: in (foo a b (breakpoint (< c 0)) c), we will push c before the breakpoint + if (nodeProducesOutput(value) || isBreakpoint(value)) { // we have to disallow usage of references in tail calls, because if we shuffle arguments around while using refs, they will end up with the same value if (value.nodeType() == NodeType::Symbol && is_tail_call) @@ -671,7 +679,7 @@ namespace Ark::internal 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) + if (it->nodeType() != NodeType::Capture && !isBreakpoint(*it)) args_count++; } // call the procedure @@ -695,17 +703,18 @@ namespace Ark::internal std::size_t exp_count = 0; for (std::size_t index = start_index, size = x.constList().size(); index < size; ++index) { - if (nodeProducesOutput(x.constList()[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 ((index + 1 < size && x.constList()[index + 1].nodeType() != NodeType::Capture) || index + 1 == size) + if (!is_breakpoint && ((index + 1 < size && x.constList()[index + 1].nodeType() != NodeType::Capture) || index + 1 == size)) exp_count++; // 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)) + if (exp_count >= 2 && !isTernaryInst(op) && !is_breakpoint) page(p).emplace_back(op); } diff --git a/tests/unittests/resources/CompilerSuite/ir/breakpoints.ark b/tests/unittests/resources/CompilerSuite/ir/breakpoints.ark new file mode 100644 index 000000000..c19d793f3 --- /dev/null +++ b/tests/unittests/resources/CompilerSuite/ir/breakpoints.ark @@ -0,0 +1,19 @@ +(breakpoint true) + +(let foo (fun (a b c) { + (breakpoint (> a 5)) + (print (format "a={}, b={}, c={}" a b c)) + (let res (* a b (breakpoint (< c 0)) c 5)) + (print (breakpoint true) res (breakpoint false)) + res })) + +(foo 1 2 3) + +(if (> (breakpoint true) 2 1) + { + (print "before call") + (foo 1 (breakpoint false) 2 3) + (print "after call") }) + +(let bar (fun () ())) +(bar (breakpoint true)) diff --git a/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected new file mode 100644 index 000000000..01f448939 --- /dev/null +++ b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected @@ -0,0 +1,105 @@ +page_0 + BUILTIN 1 + BREAKPOINT 0 + LOAD_CONST 0 + STORE 0 + PUSH_RETURN_ADDRESS L3 + LOAD_CONST 4 + LOAD_CONST 5 + LOAD_CONST 6 + LOAD_SYMBOL_BY_INDEX 0 + CALL 3 +.L3: + POP 0 + BUILTIN 1 + BREAKPOINT 0 + LOAD_CONST 5 + LOAD_CONST 6 + GT 0 + POP_JUMP_IF_TRUE L4 + JUMP L5 +.L4: + PUSH_RETURN_ADDRESS L6 + LOAD_CONST 7 + BUILTIN 9 + CALL 1 +.L6: + POP 0 + PUSH_RETURN_ADDRESS L7 + LOAD_CONST 4 + LOAD_CONST 5 + BUILTIN 0 + BREAKPOINT 0 + LOAD_CONST 6 + LOAD_SYMBOL_BY_INDEX 0 + CALL 3 +.L7: + POP 0 + PUSH_RETURN_ADDRESS L8 + LOAD_CONST 8 + BUILTIN 9 + CALL 1 +.L8: + POP 0 +.L5: + LOAD_CONST 9 + STORE 5 + PUSH_RETURN_ADDRESS L9 + BUILTIN 1 + BREAKPOINT 0 + LOAD_SYMBOL_BY_INDEX 0 + CALL 0 +.L9: + HALT 0 + +page_1 + STORE 1 + STORE 2 + STORE 3 + LOAD_SYMBOL_BY_INDEX 2 + LOAD_CONST 1 + GT 0 + BREAKPOINT 0 + PUSH_RETURN_ADDRESS L0 + PUSH_RETURN_ADDRESS L1 + LOAD_SYMBOL_BY_INDEX 0 + LOAD_SYMBOL_BY_INDEX 1 + LOAD_SYMBOL_BY_INDEX 2 + LOAD_CONST 2 + BUILTIN 27 + CALL 4 +.L1: + BUILTIN 9 + CALL 1 +.L0: + POP 0 + LOAD_SYMBOL_BY_INDEX 2 + LOAD_SYMBOL_BY_INDEX 1 + MUL 0 + LOAD_SYMBOL_BY_INDEX 0 + LOAD_CONST 3 + LT 0 + BREAKPOINT 0 + LOAD_SYMBOL_BY_INDEX 0 + MUL 0 + LOAD_CONST 1 + MUL 0 + STORE 4 + PUSH_RETURN_ADDRESS L2 + BUILTIN 0 + BREAKPOINT 0 + LOAD_SYMBOL_BY_INDEX 0 + BUILTIN 1 + BREAKPOINT 0 + BUILTIN 9 + CALL 1 +.L2: + POP 0 + LOAD_SYMBOL_BY_INDEX 0 + RET 0 + HALT 0 + +page_2 + BUILTIN 2 + RET 0 + HALT 0 \ No newline at end of file From 8cdae3e64a4684601aaf06136f44746caee1069c Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 5 Jan 2026 19:43:23 +0100 Subject: [PATCH 4/8] feat(cli): add -fdebugger to toggle the debugger in the VM when an error is caught --- include/Ark/Constants.hpp.in | 1 + include/Ark/VM/State.hpp | 6 +- src/arkreactor/VM/State.cpp | 35 ++-- src/arkreactor/VM/VM.cpp | 15 +- src/arkscript/main.cpp | 5 +- .../CompilerSuite/ir/breakpoints.expected | 26 +-- .../optimized_ir/fused_math_ops.expected | 162 +++++++++--------- 7 files changed, 132 insertions(+), 118 deletions(-) diff --git a/include/Ark/Constants.hpp.in b/include/Ark/Constants.hpp.in index 30a15d915..4b7a626b5 100644 --- a/include/Ark/Constants.hpp.in +++ b/include/Ark/Constants.hpp.in @@ -56,6 +56,7 @@ namespace Ark constexpr uint16_t FeatureASTOptimizer = 1 << 2; ///< This is disabled so that embedding ArkScript does not prune nodes from the AST ; it is active in the `arkscript` executable constexpr uint16_t FeatureIROptimizer = 1 << 3; constexpr uint16_t FeatureNameResolver = 1 << 4; + constexpr uint16_t FeatureVMDebugger = 1 << 5; ///< This is disabled so that embedding ArkScript does not launch the debugger on every error when running code constexpr uint16_t FeatureDumpIR = 1 << 14; /// This feature should only be used in tests, to disable diagnostics generation and enable exceptions to be thrown diff --git a/include/Ark/VM/State.hpp b/include/Ark/VM/State.hpp index 7c11c696e..c80543448 100644 --- a/include/Ark/VM/State.hpp +++ b/include/Ark/VM/State.hpp @@ -131,11 +131,10 @@ namespace Ark * * @param file the path of file code to compile * @param output set path of .arkc file - * @param features compiler features to enable/disable * @return true on success * @return false on failure and raise an exception */ - [[nodiscard]] bool compile(const std::string& file, const std::string& output, uint16_t features) const; + [[nodiscard]] bool compile(const std::string& file, const std::string& output) const; static void throwStateError(const std::string& message) { @@ -143,6 +142,7 @@ namespace Ark } unsigned m_debug_level; + uint16_t m_features; bytecode_t m_bytecode; std::vector m_libenv; @@ -157,7 +157,7 @@ namespace Ark bytecode_t m_code; // related to the execution - std::unordered_map m_binded; ///< Values binded to the State, to be used by the VM + std::unordered_map m_bound; ///< Values bound to the State, to be used by the VM /** * @brief Get an instruction in a given page, with a given instruction pointer diff --git a/src/arkreactor/VM/State.cpp b/src/arkreactor/VM/State.cpp index 6782b1e0a..eef26f57d 100644 --- a/src/arkreactor/VM/State.cpp +++ b/src/arkreactor/VM/State.cpp @@ -18,15 +18,16 @@ namespace Ark { State::State(const std::vector& libenv) noexcept : m_debug_level(0), + m_features(0), m_libenv(libenv), m_filename(ARK_NO_NAME_FILE), m_max_page_size(0) { // default value for builtin__sys:args is empty list const Value val(ValueType::List); - m_binded[std::string(internal::Language::SysArgs)] = val; + m_bound[std::string(internal::Language::SysArgs)] = val; - m_binded[std::string(internal::Language::SysProgramName)] = Value(""); + m_bound[std::string(internal::Language::SysProgramName)] = Value(""); } bool State::feed(const std::string& bytecode_filename, const bool fail_with_exception) @@ -61,11 +62,11 @@ namespace Ark } } - bool State::compile(const std::string& file, const std::string& output, const uint16_t features) const + bool State::compile(const std::string& file, const std::string& output) const { - Welder welder(m_debug_level, m_libenv, features); - for (const auto& p : m_binded) - welder.registerSymbol(p.first); + Welder welder(m_debug_level, m_libenv, m_features); + for (const auto& key : m_bound | std::views::keys) + welder.registerSymbol(key); if (!welder.computeASTFromFile(file)) return false; @@ -81,13 +82,15 @@ namespace Ark bool State::doFile(const std::string& file_path, const uint16_t features) { + m_features = features; + if (!Utils::fileExists(file_path)) { fmt::print(fmt::fg(fmt::color::red), "Can not find file '{}'\n", file_path); return false; } m_filename = file_path; - m_binded[std::string(internal::Language::SysProgramName)] = Value(std::filesystem::path(m_filename).filename().string()); + m_bound[std::string(internal::Language::SysProgramName)] = Value(std::filesystem::path(m_filename).filename().string()); const bytecode_t bytecode = Utils::readFileAsBytes(file_path); BytecodeReader bcr; @@ -102,7 +105,7 @@ namespace Ark if (!exists(cache_directory)) create_directory(cache_directory); - if (compile(file_path, bytecode_path, features) && feed(bytecode_path)) + if (compile(file_path, bytecode_path) && feed(bytecode_path)) return true; } else if (feed(bytecode)) // it's a bytecode file @@ -112,8 +115,10 @@ namespace Ark bool State::doString(const std::string& code, const uint16_t features) { - Welder welder(m_debug_level, m_libenv, features); - for (const auto& p : m_binded) + m_features = features; + + Welder welder(m_debug_level, m_libenv, m_features); + for (const auto& p : m_bound) welder.registerSymbol(p.first); if (!welder.computeASTFromString(code)) @@ -125,7 +130,7 @@ namespace Ark void State::loadFunction(const std::string& name, Procedure::CallbackType&& function) noexcept { - m_binded[name] = Value(std::move(function)); + m_bound[name] = Value(std::move(function)); } void State::setArgs(const std::vector& args) noexcept @@ -135,7 +140,7 @@ namespace Ark return Value(arg); }); - m_binded[std::string(internal::Language::SysArgs)] = val; + m_bound[std::string(internal::Language::SysArgs)] = val; } void State::setDebug(const unsigned level) noexcept @@ -210,13 +215,13 @@ namespace Ark m_inst_locations.clear(); m_max_page_size = 0; m_code.clear(); - m_binded.clear(); + m_bound.clear(); // default value for builtin__sys:args is empty list const Value val(ValueType::List); - m_binded[std::string(internal::Language::SysArgs)] = val; + m_bound[std::string(internal::Language::SysArgs)] = val; - m_binded[std::string(internal::Language::SysProgramName)] = Value(""); + m_bound[std::string(internal::Language::SysProgramName)] = Value(""); } } diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 430fbfe80..89e811cbb 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -173,7 +173,7 @@ namespace Ark // loading bound stuff // put them in the global frame if we can, aka the first one - for (const auto& [sym_id, value] : m_state.m_binded) + for (const auto& [sym_id, value] : m_state.m_bound) { auto it = std::ranges::find(m_state.m_symbols, sym_id); if (it != m_state.m_symbols.end()) @@ -1180,6 +1180,10 @@ namespace Ark DISPATCH(); } +#pragma endregion + +#pragma region "Operators" + TARGET(BREAKPOINT) { { @@ -1190,10 +1194,6 @@ namespace Ark DISPATCH(); } -#pragma endregion - -#pragma region "Operators" - TARGET(ADD) { Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context); @@ -2260,6 +2260,11 @@ namespace Ark text += '\n'; fmt::println("{}", text); backtrace(context); + + if (m_state.m_features & FeatureVMDebugger) + { + // TODO: launch debugger + } #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // don't report a "failed" exit code so that the fuzzers can more accurately triage crashes m_exit_code = 0; diff --git a/src/arkscript/main.cpp b/src/arkscript/main.cpp index 4cf124543..16ae4e84b 100644 --- a/src/arkscript/main.cpp +++ b/src/arkscript/main.cpp @@ -80,12 +80,15 @@ int main(int argc, char** argv) option("-firoptimizer").call([&] { passes |= Ark::FeatureIROptimizer; }) | option("-fno-iroptimizer").call([&] { passes &= ~Ark::FeatureIROptimizer; }) ).doc("Toggle on and off the IR optimizer pass"); + auto vm_debugger_flag = ( + option("-fdebugger").call([&] { passes |= Ark::FeatureVMDebugger; }) + ).doc("Turn on the debugger"); auto ir_dump = option("-fdump-ir").call([&] { passes |= Ark::FeatureDumpIR; }) .doc("Dump IR to file.ark.ir"); const auto run_flags = ( // cppcheck-suppress constStatement - debug_flag, lib_dir_flag, import_solver_pass_flag, macro_proc_pass_flag, optimizer_pass_flag, ir_optimizer_pass_flag, ir_dump + debug_flag, lib_dir_flag, import_solver_pass_flag, macro_proc_pass_flag, optimizer_pass_flag, ir_optimizer_pass_flag, vm_debugger_flag, ir_dump ); auto cli = ( diff --git a/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected index 01f448939..d4b95686c 100644 --- a/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected +++ b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected @@ -7,7 +7,7 @@ page_0 LOAD_CONST 4 LOAD_CONST 5 LOAD_CONST 6 - LOAD_SYMBOL_BY_INDEX 0 + LOAD_FAST_BY_INDEX 0 CALL 3 .L3: POP 0 @@ -31,7 +31,7 @@ page_0 BUILTIN 0 BREAKPOINT 0 LOAD_CONST 6 - LOAD_SYMBOL_BY_INDEX 0 + LOAD_FAST_BY_INDEX 0 CALL 3 .L7: POP 0 @@ -47,7 +47,7 @@ page_0 PUSH_RETURN_ADDRESS L9 BUILTIN 1 BREAKPOINT 0 - LOAD_SYMBOL_BY_INDEX 0 + LOAD_FAST_BY_INDEX 0 CALL 0 .L9: HALT 0 @@ -56,15 +56,15 @@ page_1 STORE 1 STORE 2 STORE 3 - LOAD_SYMBOL_BY_INDEX 2 + LOAD_FAST_BY_INDEX 2 LOAD_CONST 1 GT 0 BREAKPOINT 0 PUSH_RETURN_ADDRESS L0 PUSH_RETURN_ADDRESS L1 - LOAD_SYMBOL_BY_INDEX 0 - LOAD_SYMBOL_BY_INDEX 1 - LOAD_SYMBOL_BY_INDEX 2 + LOAD_FAST_BY_INDEX 0 + LOAD_FAST_BY_INDEX 1 + LOAD_FAST_BY_INDEX 2 LOAD_CONST 2 BUILTIN 27 CALL 4 @@ -73,14 +73,14 @@ page_1 CALL 1 .L0: POP 0 - LOAD_SYMBOL_BY_INDEX 2 - LOAD_SYMBOL_BY_INDEX 1 + LOAD_FAST_BY_INDEX 2 + LOAD_FAST_BY_INDEX 1 MUL 0 - LOAD_SYMBOL_BY_INDEX 0 + LOAD_FAST_BY_INDEX 0 LOAD_CONST 3 LT 0 BREAKPOINT 0 - LOAD_SYMBOL_BY_INDEX 0 + LOAD_FAST_BY_INDEX 0 MUL 0 LOAD_CONST 1 MUL 0 @@ -88,14 +88,14 @@ page_1 PUSH_RETURN_ADDRESS L2 BUILTIN 0 BREAKPOINT 0 - LOAD_SYMBOL_BY_INDEX 0 + LOAD_FAST_BY_INDEX 0 BUILTIN 1 BREAKPOINT 0 BUILTIN 9 CALL 1 .L2: POP 0 - LOAD_SYMBOL_BY_INDEX 0 + LOAD_FAST_BY_INDEX 0 RET 0 HALT 0 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 7d911eb0f..d1926012f 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 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, 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, 38, 41 + FUSED_MATH 42, 39, 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, 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, 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, 38, 40 + FUSED_MATH 42, 39, 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, 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 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, 38, 39 + FUSED_MATH 42, 39, 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, 38 + FUSED_MATH 42, 42, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 39, 38 + FUSED_MATH 42, 40, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 40, 38 + FUSED_MATH 42, 41, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 38, 38 + FUSED_MATH 42, 39, 39 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, 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, 38, 41 + FUSED_MATH 41, 39, 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, 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, 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, 38, 40 + FUSED_MATH 41, 39, 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, 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 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, 38, 39 + FUSED_MATH 41, 39, 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, 38 + FUSED_MATH 41, 42, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 39, 38 + FUSED_MATH 41, 40, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 40, 38 + FUSED_MATH 41, 41, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 40, 38, 38 + FUSED_MATH 41, 39, 39 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, 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, 38, 41 + FUSED_MATH 40, 39, 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, 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, 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, 38, 40 + FUSED_MATH 40, 39, 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, 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 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, 38, 39 + FUSED_MATH 40, 39, 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, 38 + FUSED_MATH 40, 42, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 39, 38 + FUSED_MATH 40, 40, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 40, 38 + FUSED_MATH 40, 41, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 39, 38, 38 + FUSED_MATH 40, 39, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 41, 41 + FUSED_MATH 39, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 39, 41 + FUSED_MATH 39, 40, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 40, 41 + FUSED_MATH 39, 41, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 38, 41 + FUSED_MATH 39, 39, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 41, 40 + FUSED_MATH 39, 42, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 39, 40 + FUSED_MATH 39, 40, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 40, 40 + FUSED_MATH 39, 41, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 38, 40 + FUSED_MATH 39, 39, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 41, 39 + FUSED_MATH 39, 42, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 39, 39 + FUSED_MATH 39, 40, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 40, 39 + FUSED_MATH 39, 41, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 38, 39 + FUSED_MATH 39, 39, 40 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 41, 38 + FUSED_MATH 39, 42, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 39, 38 + FUSED_MATH 39, 40, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 40, 38 + FUSED_MATH 39, 41, 39 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 38, 38, 38 + FUSED_MATH 39, 39, 39 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 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 38, 41, 0 + FUSED_MATH 39, 42, 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 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 38, 40, 0 + FUSED_MATH 39, 41, 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 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 38, 39, 0 + FUSED_MATH 39, 40, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 41, 38, 0 + FUSED_MATH 42, 39, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 39, 38, 0 + FUSED_MATH 40, 39, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 40, 38, 0 + FUSED_MATH 41, 39, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 38, 38, 0 + FUSED_MATH 39, 39, 0 LIST 80 CALL_BUILTIN 9, 1 .L0: - HALT 0 + HALT 0 \ No newline at end of file From cf0ba80c43c3b2c3cd3b468de8909a1925481d59 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 12 Jan 2026 18:06:09 +0100 Subject: [PATCH 5/8] refactor: create a dedicated Ark/VM/Value/ folder for better organization --- include/Ark/Ark.hpp | 3 ++- include/Ark/Builtins/Builtins.hpp | 2 +- include/Ark/Compiler/BytecodeReader.hpp | 2 +- include/Ark/{VM => }/State.hpp | 2 +- include/Ark/TypeChecker.hpp | 2 +- include/Ark/VM/DefaultValues.hpp | 2 +- include/Ark/VM/ExecutionContext.hpp | 2 +- include/Ark/VM/ScopeView.hpp | 4 ++-- include/Ark/VM/VM.hpp | 6 +++--- include/Ark/VM/Value/ClosureScope.hpp | 2 +- include/Ark/VM/Value/Dict.hpp | 2 +- include/Ark/VM/{ => Value}/Future.hpp | 2 +- include/Ark/VM/{ => Value}/Value.hpp | 0 include/CLI/REPL/Repl.hpp | 2 +- src/arkreactor/{VM => }/State.cpp | 2 +- src/arkreactor/VM/{ => Value}/Future.cpp | 2 +- src/arkreactor/VM/Value/Procedure.cpp | 2 +- src/arkreactor/VM/{ => Value}/Value.cpp | 2 +- tests/benchmarks/main.cpp | 2 +- tests/unittests/Suites/TypeCheckerSuite.cpp | 4 ++-- 20 files changed, 24 insertions(+), 23 deletions(-) rename include/Ark/{VM => }/State.hpp (99%) rename include/Ark/VM/{ => Value}/Future.hpp (97%) rename include/Ark/VM/{ => Value}/Value.hpp (100%) rename src/arkreactor/{VM => }/State.cpp (99%) rename src/arkreactor/VM/{ => Value}/Future.cpp (97%) rename src/arkreactor/VM/{ => Value}/Value.cpp (99%) diff --git a/include/Ark/Ark.hpp b/include/Ark/Ark.hpp index 30cb11c32..421b27902 100644 --- a/include/Ark/Ark.hpp +++ b/include/Ark/Ark.hpp @@ -14,9 +14,10 @@ #include #include #include +#include +#include #include #include -#include #include #endif // ARK_ARK_HPP diff --git a/include/Ark/Builtins/Builtins.hpp b/include/Ark/Builtins/Builtins.hpp index ba49bf270..d87b6d1ca 100644 --- a/include/Ark/Builtins/Builtins.hpp +++ b/include/Ark/Builtins/Builtins.hpp @@ -13,7 +13,7 @@ #include -#include +#include namespace Ark { diff --git a/include/Ark/Compiler/BytecodeReader.hpp b/include/Ark/Compiler/BytecodeReader.hpp index d5f3c1c11..ccfe50af0 100644 --- a/include/Ark/Compiler/BytecodeReader.hpp +++ b/include/Ark/Compiler/BytecodeReader.hpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include namespace Ark diff --git a/include/Ark/VM/State.hpp b/include/Ark/State.hpp similarity index 99% rename from include/Ark/VM/State.hpp rename to include/Ark/State.hpp index c80543448..7f22db921 100644 --- a/include/Ark/VM/State.hpp +++ b/include/Ark/State.hpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include diff --git a/include/Ark/TypeChecker.hpp b/include/Ark/TypeChecker.hpp index e5c7ac5e6..baa73baa5 100644 --- a/include/Ark/TypeChecker.hpp +++ b/include/Ark/TypeChecker.hpp @@ -17,7 +17,7 @@ #include #include -#include +#include namespace Ark { diff --git a/include/Ark/VM/DefaultValues.hpp b/include/Ark/VM/DefaultValues.hpp index 9709537d4..a9eb6eaae 100644 --- a/include/Ark/VM/DefaultValues.hpp +++ b/include/Ark/VM/DefaultValues.hpp @@ -11,7 +11,7 @@ #ifndef ARK_VM_DEFAULTVALUES_HPP #define ARK_VM_DEFAULTVALUES_HPP -#include +#include namespace Ark { diff --git a/include/Ark/VM/ExecutionContext.hpp b/include/Ark/VM/ExecutionContext.hpp index 0e0bbbeb7..afdc15db7 100644 --- a/include/Ark/VM/ExecutionContext.hpp +++ b/include/Ark/VM/ExecutionContext.hpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include diff --git a/include/Ark/VM/ScopeView.hpp b/include/Ark/VM/ScopeView.hpp index db9857c59..11fec5315 100644 --- a/include/Ark/VM/ScopeView.hpp +++ b/include/Ark/VM/ScopeView.hpp @@ -1,5 +1,5 @@ /** - * @file Scope.hpp + * @file ScopeView.hpp * @author Lexy Plateau (lexplt.dev@gmail.com) * @brief The virtual machine scope system * @date 2020-10-27 @@ -15,7 +15,7 @@ #include #include -#include +#include namespace Ark::internal { diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp index 63360be93..dda53e460 100644 --- a/include/Ark/VM/VM.hpp +++ b/include/Ark/VM/VM.hpp @@ -24,8 +24,8 @@ #include #include -#include -#include +#include +#include #include #include #include @@ -33,7 +33,7 @@ #include #include #include -#include +#include namespace Ark { diff --git a/include/Ark/VM/Value/ClosureScope.hpp b/include/Ark/VM/Value/ClosureScope.hpp index 5474eadbe..cdc13bb33 100644 --- a/include/Ark/VM/Value/ClosureScope.hpp +++ b/include/Ark/VM/Value/ClosureScope.hpp @@ -16,7 +16,7 @@ #include #include -#include +#include namespace Ark::internal { diff --git a/include/Ark/VM/Value/Dict.hpp b/include/Ark/VM/Value/Dict.hpp index d37260df8..c4038d457 100644 --- a/include/Ark/VM/Value/Dict.hpp +++ b/include/Ark/VM/Value/Dict.hpp @@ -11,7 +11,7 @@ #ifndef ARK_VM_VALUE_DICT_HPP #define ARK_VM_VALUE_DICT_HPP -#include +#include #include #include diff --git a/include/Ark/VM/Future.hpp b/include/Ark/VM/Value/Future.hpp similarity index 97% rename from include/Ark/VM/Future.hpp rename to include/Ark/VM/Value/Future.hpp index 6a8d0f2bb..23d0f35cc 100644 --- a/include/Ark/VM/Future.hpp +++ b/include/Ark/VM/Value/Future.hpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include namespace Ark diff --git a/include/Ark/VM/Value.hpp b/include/Ark/VM/Value/Value.hpp similarity index 100% rename from include/Ark/VM/Value.hpp rename to include/Ark/VM/Value/Value.hpp diff --git a/include/CLI/REPL/Repl.hpp b/include/CLI/REPL/Repl.hpp index cb59e4bc7..798efe9e3 100644 --- a/include/CLI/REPL/Repl.hpp +++ b/include/CLI/REPL/Repl.hpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include diff --git a/src/arkreactor/VM/State.cpp b/src/arkreactor/State.cpp similarity index 99% rename from src/arkreactor/VM/State.cpp rename to src/arkreactor/State.cpp index eef26f57d..77caaff2b 100644 --- a/src/arkreactor/VM/State.cpp +++ b/src/arkreactor/State.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/src/arkreactor/VM/Future.cpp b/src/arkreactor/VM/Value/Future.cpp similarity index 97% rename from src/arkreactor/VM/Future.cpp rename to src/arkreactor/VM/Value/Future.cpp index 5f050155e..cd4b039c6 100644 --- a/src/arkreactor/VM/Future.cpp +++ b/src/arkreactor/VM/Value/Future.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/src/arkreactor/VM/Value/Procedure.cpp b/src/arkreactor/VM/Value/Procedure.cpp index 6c66853a2..f4b23d2e9 100644 --- a/src/arkreactor/VM/Value/Procedure.cpp +++ b/src/arkreactor/VM/Value/Procedure.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/src/arkreactor/VM/Value.cpp b/src/arkreactor/VM/Value/Value.cpp similarity index 99% rename from src/arkreactor/VM/Value.cpp rename to src/arkreactor/VM/Value/Value.cpp index ded447e44..90f84a38b 100644 --- a/src/arkreactor/VM/Value.cpp +++ b/src/arkreactor/VM/Value/Value.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/tests/benchmarks/main.cpp b/tests/benchmarks/main.cpp index b518a7d6e..5f15b36b3 100644 --- a/tests/benchmarks/main.cpp +++ b/tests/benchmarks/main.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #define ARK_CREATE_RUNTIME_BENCH(name) \ diff --git a/tests/unittests/Suites/TypeCheckerSuite.cpp b/tests/unittests/Suites/TypeCheckerSuite.cpp index 96330e850..d1296a286 100644 --- a/tests/unittests/Suites/TypeCheckerSuite.cpp +++ b/tests/unittests/Suites/TypeCheckerSuite.cpp @@ -5,9 +5,9 @@ #include #include -#include +#include #include -#include +#include #include #include #include From d43ba706710f92c847a2ab42ecd58e03e3083f77 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 12 Jan 2026 18:25:34 +0100 Subject: [PATCH 6/8] refactor: print vm pointers in VM::showBacktraceWithException instead of VM::backtrace, so that we have access to the pointers when creating the debugger --- src/arkreactor/VM/VM.cpp | 24 +++++++++++++----------- tests/unittests/TestsHelper.cpp | 2 -- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 89e811cbb..864bf7ae5 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -2259,12 +2259,25 @@ namespace Ark if (!text.empty() && text.back() != '\n') text += '\n'; fmt::println("{}", text); + + const std::size_t saved_ip = context.ip; + const std::size_t saved_pp = context.pp; + const uint16_t saved_sp = context.sp; + backtrace(context); + fmt::println( + "At IP: {}, PP: {}, SP: {}", + // dividing by 4 because the instructions are actually on 4 bytes + fmt::styled(saved_ip / 4, fmt::fg(fmt::color::cyan)), + fmt::styled(saved_pp, fmt::fg(fmt::color::green)), + fmt::styled(saved_sp, fmt::fg(fmt::color::yellow))); + if (m_state.m_features & FeatureVMDebugger) { // TODO: launch debugger } + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // don't report a "failed" exit code so that the fuzzers can more accurately triage crashes m_exit_code = 0; @@ -2310,9 +2323,6 @@ namespace Ark void VM::backtrace(ExecutionContext& context, std::ostream& os, const bool colorize) { - const std::size_t saved_ip = context.ip; - const std::size_t saved_pp = context.pp; - const uint16_t saved_sp = context.sp; constexpr std::size_t max_consecutive_traces = 7; const auto maybe_location = findSourceLocation(context.ip, context.pp); @@ -2404,13 +2414,5 @@ namespace Ark old_scope.atPos(i).second.toString(*this)); } } - - fmt::println( - os, - "At IP: {}, PP: {}, SP: {}", - // dividing by 4 because the instructions are actually on 4 bytes - fmt::styled(saved_ip / 4, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()), - fmt::styled(saved_pp, colorize ? fmt::fg(fmt::color::green) : fmt::text_style()), - fmt::styled(saved_sp, colorize ? fmt::fg(fmt::color::yellow) : fmt::text_style())); } } diff --git a/tests/unittests/TestsHelper.cpp b/tests/unittests/TestsHelper.cpp index 156d029df..c33c8ebc3 100644 --- a/tests/unittests/TestsHelper.cpp +++ b/tests/unittests/TestsHelper.cpp @@ -95,8 +95,6 @@ std::string sanitizeRuntimeError(const std::exception& e) while (diag.find(ARK_TESTS_ROOT) != std::string::npos) diag.erase(diag.find(ARK_TESTS_ROOT), std::size(ARK_TESTS_ROOT) - 1); Ark::Utils::ltrim(Ark::Utils::rtrim(diag)); - // remove last line, At IP:.., PP:.., SP:.. - diag.erase(diag.find_last_of('\n'), diag.size() - 1); // we most likely have a blank line at the end now Ark::Utils::rtrim(diag); From f4650b0f46fb8cb3b77d5a0e55d7297c11702571 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 12 Jan 2026 19:29:06 +0100 Subject: [PATCH 7/8] feat(vm): enhancing the arity error handling and adding a test for when we have 0 argument --- src/arkreactor/VM/VM.cpp | 5 +++-- .../DiagnosticsSuite/runtime/arity_error_fun_no_arg.ark | 2 ++ .../runtime/arity_error_fun_no_arg.expected | 7 +++++++ 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 tests/unittests/resources/DiagnosticsSuite/runtime/arity_error_fun_no_arg.ark create mode 100644 tests/unittests/resources/DiagnosticsSuite/runtime/arity_error_fun_no_arg.expected diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 864bf7ae5..569addf42 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -2195,7 +2195,7 @@ namespace Ark return MaxValue16Bits; } - void VM::throwArityError(std::size_t passed_arg_count, std::size_t expected_arg_count, internal::ExecutionContext& context) + void VM::throwArityError(std::size_t passed_arg_count, std::size_t expected_arg_count, ExecutionContext& context) { std::vector arg_names; arg_names.reserve(expected_arg_count + 1); @@ -2215,7 +2215,7 @@ namespace Ark { assert(m_state.inst(context.pp, 0) == CALL_BUILTIN_WITHOUT_RETURN_ADDRESS && "expected a CALL_BUILTIN_WITHOUT_RETURN_ADDRESS instruction or STORE instructions"); for (std::size_t i = 0; i < expected_arg_count; ++i) - arg_names.push_back(std::string(1, static_cast('a' + i))); + arg_names.emplace_back(1, static_cast('a' + i)); } std::vector arg_vals; @@ -2233,6 +2233,7 @@ namespace Ark { context.ip = context.stack[context.sp - 1 - passed_arg_count].pageAddr(); context.pp = context.stack[context.sp - 2 - passed_arg_count].pageAddr(); + context.sp -= 2; returnFromFuncCall(context); } diff --git a/tests/unittests/resources/DiagnosticsSuite/runtime/arity_error_fun_no_arg.ark b/tests/unittests/resources/DiagnosticsSuite/runtime/arity_error_fun_no_arg.ark new file mode 100644 index 000000000..4d4cd4a2a --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/runtime/arity_error_fun_no_arg.ark @@ -0,0 +1,2 @@ +(let foo (fun () ())) +(print (foo 1)) diff --git a/tests/unittests/resources/DiagnosticsSuite/runtime/arity_error_fun_no_arg.expected b/tests/unittests/resources/DiagnosticsSuite/runtime/arity_error_fun_no_arg.expected new file mode 100644 index 000000000..b90afbb4a --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/runtime/arity_error_fun_no_arg.expected @@ -0,0 +1,7 @@ +ArityError: When calling `(foo 1)', received 1 argument, but expected 0: `(foo)' + +In file tests/unittests/resources/DiagnosticsSuite/runtime/arity_error_fun_no_arg.ark:2 + 1 | (let foo (fun () ())) + 2 | (print (foo 1)) + | ^~~~~~~~~~~~~~ + 3 | From 59ad4b9eb7951895da3c0abfd977dc9b9d6d37be Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 12 Jan 2026 19:30:05 +0100 Subject: [PATCH 8/8] feat(debugger): creating a very basic debugger, started by VM::showBacktraceWithException --- include/Ark/VM/Debugger.hpp | 52 ++++++++++++++++++++++++++++++++++ include/Ark/VM/VM.hpp | 8 ++++-- src/arkreactor/VM/Debugger.cpp | 45 +++++++++++++++++++++++++++++ src/arkreactor/VM/VM.cpp | 27 ++++++++++++++---- 4 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 include/Ark/VM/Debugger.hpp create mode 100644 src/arkreactor/VM/Debugger.cpp diff --git a/include/Ark/VM/Debugger.hpp b/include/Ark/VM/Debugger.hpp new file mode 100644 index 000000000..dcf1ee002 --- /dev/null +++ b/include/Ark/VM/Debugger.hpp @@ -0,0 +1,52 @@ +/** + * @file Debugger.hpp + * @author Lexy Plateau (lexplt.dev@gmail.com) + * @brief Debugger used by the VM when an error or a breakpoint is reached + * @date 2026-01-12 + * + * @copyright Copyright (c) 2026-01-12 + * + */ + +#ifndef ARK_VM_DEBUGGER_HPP +#define ARK_VM_DEBUGGER_HPP + +#include + +#include +#include + +namespace Ark::internal +{ + struct SavedState + { + std::size_t ip; + std::size_t pp; + uint16_t sp; + uint16_t fc; + std::vector locals; + std::vector> closure_scopes; + }; + + class Debugger + { + public: + /** + * @brief Create a new Debugger object + * + * @param context context from the VM before displaying a backtrace + */ + explicit Debugger(const ExecutionContext& context); + + void saveState(const ExecutionContext& context); + + void resetContextToErrorState(ExecutionContext& context); + + void run(); + + private: + std::vector> m_states; + }; +} + +#endif // ARK_VM_DEBUGGER_HPP diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp index dda53e460..e2f4a1959 100644 --- a/include/Ark/VM/VM.hpp +++ b/include/Ark/VM/VM.hpp @@ -34,6 +34,7 @@ #include #include #include +#include namespace Ark { @@ -175,6 +176,7 @@ namespace Ark std::mutex m_mutex, m_mutex_futures; std::vector> m_shared_lib_objects; std::vector> m_futures; ///< Storing the promises while we are resolving them + std::unique_ptr m_debugger { nullptr }; // a little trick for operator[] and for pop Value m_no_value = internal::Builtins::nil; @@ -352,6 +354,8 @@ namespace Ark [[noreturn]] void throwArityError(std::size_t passed_arg_count, std::size_t expected_arg_count, internal::ExecutionContext& context); + void initDebugger(internal::ExecutionContext& context); + void showBacktraceWithException(const std::exception& e, internal::ExecutionContext& context); /** @@ -361,9 +365,9 @@ namespace Ark * @param pp * @return std::optional */ - std::optional findSourceLocation(std::size_t ip, std::size_t pp) const; + [[nodiscard]] std::optional findSourceLocation(std::size_t ip, std::size_t pp) const; - std::string debugShowSource() const; + [[nodiscard]] std::string debugShowSource() const; /** * @brief Display a backtrace when the VM encounter an exception diff --git a/src/arkreactor/VM/Debugger.cpp b/src/arkreactor/VM/Debugger.cpp new file mode 100644 index 000000000..638207630 --- /dev/null +++ b/src/arkreactor/VM/Debugger.cpp @@ -0,0 +1,45 @@ +#include + +#include + +namespace Ark::internal +{ + // TODO: Create a new context? + // Creating a new context meaning modifying where the scope views points to, + // because the underlying storage won't have the same address. + Debugger::Debugger(const ExecutionContext& context) + { + saveState(context); + } + + void Debugger::saveState(const ExecutionContext& context) + { + m_states.emplace_back( + std::make_unique( + context.ip, + context.pp, + context.sp, + context.fc, + context.locals, + context.stacked_closure_scopes)); + } + + void Debugger::resetContextToErrorState(ExecutionContext& context) + { + const auto& [ip, pp, sp, fc, locals, closure_scopes] = *m_states.back(); + context.locals = locals; + context.stacked_closure_scopes = closure_scopes; + context.ip = ip; + context.pp = pp; + context.sp = sp; + context.fc = fc; + + m_states.pop_back(); + } + + void Debugger::run() + { + // TODO: create a shell + fmt::println("debugger running"); + } +} diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 569addf42..60528424b 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -918,7 +918,7 @@ namespace Ark args.push_back(*popAndResolveAsPtr(context)); throw types::TypeCheckingError( "append", - { { types::Contract { { types::Typedef("list", ValueType::List), types::Typedef("value", ValueType::Any, /* variadic= */ true) } } } }, + { { types::Contract { { types::Typedef("list", ValueType::List), types::Typedef("value", ValueType::Any, /* is_variadic= */ true) } } } }, args); } @@ -1188,8 +1188,11 @@ namespace Ark { { const Value cond = *popAndResolveAsPtr(context); - if (cond == Builtins::trueSym) // todo: trigger debugger - {} + if (cond == Builtins::trueSym) + { + initDebugger(context); + m_debugger->run(); + } } DISPATCH(); } @@ -2254,13 +2257,24 @@ namespace Ark fmt::join(arg_names, " "))); } - void VM::showBacktraceWithException(const std::exception& e, internal::ExecutionContext& context) + void VM::initDebugger(internal::ExecutionContext& context) + { + if (!m_debugger) + m_debugger = std::make_unique(context); + else + m_debugger->saveState(context); + } + + void VM::showBacktraceWithException(const std::exception& e, ExecutionContext& context) { std::string text = e.what(); if (!text.empty() && text.back() != '\n') text += '\n'; fmt::println("{}", text); + if (m_state.m_features & FeatureVMDebugger) + initDebugger(context); + const std::size_t saved_ip = context.ip; const std::size_t saved_pp = context.pp; const uint16_t saved_sp = context.sp; @@ -2274,9 +2288,10 @@ namespace Ark fmt::styled(saved_pp, fmt::fg(fmt::color::green)), fmt::styled(saved_sp, fmt::fg(fmt::color::yellow))); - if (m_state.m_features & FeatureVMDebugger) + if (m_debugger) { - // TODO: launch debugger + m_debugger->resetContextToErrorState(context); + m_debugger->run(); } #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION