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/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 c2fa961dd..d87b6d1ca 100644 --- a/include/Ark/Builtins/Builtins.hpp +++ b/include/Ark/Builtins/Builtins.hpp @@ -13,7 +13,7 @@ #include -#include +#include namespace Ark { @@ -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/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/Compiler/Common.hpp b/include/Ark/Compiler/Common.hpp index c7cb98ae5..1cfa0a72c 100644 --- a/include/Ark/Compiler/Common.hpp +++ b/include/Ark/Compiler/Common.hpp @@ -154,10 +154,10 @@ namespace Ark::internal // from FIRST_OPERATOR, to LAST_OPERATOR // The order is very important constexpr std::array operators = { + "breakpoint", "+", "-", "*", "/", ">", "<", "<=", ">=", "!=", "=", - "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..99e636091 100644 --- a/include/Ark/Compiler/Instructions.hpp +++ b/include/Ark/Compiler/Instructions.hpp @@ -173,53 +173,53 @@ namespace Ark::internal FIRST_OPERATOR = 0x26, + // @role Pop the top of the stack, if it's true, trigger the debugger + BREAKPOINT = 0x26, + // @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, @@ -492,6 +492,7 @@ namespace Ark::internal "POP_SCOPE", "GET_CURRENT_PAGE_ADDR", // operators + "BREAKPOINT", "ADD", "SUB", "MUL", @@ -507,7 +508,6 @@ namespace Ark::internal "TAIL", "HEAD", "IS_NIL", - "ASSERT", "TO_NUM", "TO_STR", "AT", 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/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/State.hpp similarity index 95% rename from include/Ark/VM/State.hpp rename to include/Ark/State.hpp index 7c11c696e..7f22db921 100644 --- a/include/Ark/VM/State.hpp +++ b/include/Ark/State.hpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include @@ -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/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/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/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..e2f4a1959 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,8 @@ #include #include #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/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/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..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) @@ -86,10 +93,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... } @@ -97,6 +104,7 @@ namespace Ark::internal { switch (inst) { + case BREAKPOINT: [[fallthrough]]; case NOT: [[fallthrough]]; case LEN: [[fallthrough]]; case IS_EMPTY: [[fallthrough]]; @@ -556,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) @@ -670,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 @@ -684,43 +693,45 @@ 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 == ASSERT) + if (op == BREAKPOINT) is_result_unused = false; // 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) { - 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); } 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 +750,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/src/arkreactor/VM/State.cpp b/src/arkreactor/State.cpp similarity index 86% rename from src/arkreactor/VM/State.cpp rename to src/arkreactor/State.cpp index 6782b1e0a..77caaff2b 100644 --- a/src/arkreactor/VM/State.cpp +++ b/src/arkreactor/State.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -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/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 d79958542..60528424b 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()) @@ -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, @@ -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); } @@ -1184,6 +1184,19 @@ namespace Ark #pragma region "Operators" + TARGET(BREAKPOINT) + { + { + const Value cond = *popAndResolveAsPtr(context); + if (cond == Builtins::trueSym) + { + initDebugger(context); + m_debugger->run(); + } + } + DISPATCH(); + } + TARGET(ADD) { Value *b = popAndResolveAsPtr(context), *a = popAndResolveAsPtr(context); @@ -1344,22 +1357,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); @@ -2201,7 +2198,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); @@ -2221,7 +2218,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; @@ -2239,6 +2236,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); } @@ -2259,13 +2257,43 @@ 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; + 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_debugger) + { + m_debugger->resetContextToErrorState(context); + m_debugger->run(); + } + #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; @@ -2311,9 +2339,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); @@ -2405,13 +2430,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/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/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/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/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/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 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); 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/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..d4b95686c --- /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_FAST_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_FAST_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_FAST_BY_INDEX 0 + CALL 0 +.L9: + HALT 0 + +page_1 + STORE 1 + STORE 2 + STORE 3 + LOAD_FAST_BY_INDEX 2 + LOAD_CONST 1 + GT 0 + BREAKPOINT 0 + PUSH_RETURN_ADDRESS L0 + PUSH_RETURN_ADDRESS L1 + LOAD_FAST_BY_INDEX 0 + LOAD_FAST_BY_INDEX 1 + LOAD_FAST_BY_INDEX 2 + LOAD_CONST 2 + BUILTIN 27 + CALL 4 +.L1: + BUILTIN 9 + CALL 1 +.L0: + POP 0 + LOAD_FAST_BY_INDEX 2 + LOAD_FAST_BY_INDEX 1 + MUL 0 + LOAD_FAST_BY_INDEX 0 + LOAD_CONST 3 + LT 0 + BREAKPOINT 0 + LOAD_FAST_BY_INDEX 0 + MUL 0 + LOAD_CONST 1 + MUL 0 + STORE 4 + PUSH_RETURN_ADDRESS L2 + BUILTIN 0 + BREAKPOINT 0 + LOAD_FAST_BY_INDEX 0 + BUILTIN 1 + BREAKPOINT 0 + BUILTIN 9 + CALL 1 +.L2: + POP 0 + LOAD_FAST_BY_INDEX 0 + RET 0 + HALT 0 + +page_2 + BUILTIN 2 + RET 0 + HALT 0 \ No newline at end of file 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/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 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/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 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 | 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 |