diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3118a88da..9504a3e32 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -12,3 +12,4 @@ Closes # (issue) - [ ] I have updated the documentation if needed (on https://github.com/ArkScript-lang/website, content/docs/) - [ ] I have added tests that prove my fix/feature is working - [ ] New and existing tests pass locally with my changes +- [ ] AI wasn't involved in order to develop this feature diff --git a/.gitignore b/.gitignore index 3c202a89d..f97660afb 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,6 @@ __arkscript__/ *.arkm /*.ark *.ark.ir -!tests/unittests/resources/BytecodeReaderSuite/*.arkc tools/*_hash.py # Generated files diff --git a/.run/a.ark.run.xml b/.run/a.ark.run.xml new file mode 100644 index 000000000..2b60e9a3b --- /dev/null +++ b/.run/a.ark.run.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/.run/ast a.ark.run.xml b/.run/ast a.ark.run.xml new file mode 100644 index 000000000..959a15b35 --- /dev/null +++ b/.run/ast a.ark.run.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/.run/bcr a.ark.run.xml b/.run/bcr a.ark.run.xml new file mode 100644 index 000000000..69548eab8 --- /dev/null +++ b/.run/bcr a.ark.run.xml @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/.run/benchmarks.run.xml b/.run/benchmarks.run.xml new file mode 100644 index 000000000..6cf632758 --- /dev/null +++ b/.run/benchmarks.run.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/.run/build arkscript.run.xml b/.run/build arkscript.run.xml new file mode 100644 index 000000000..96e5de93b --- /dev/null +++ b/.run/build arkscript.run.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/.run/cpp unittests.run.xml b/.run/cpp unittests.run.xml new file mode 100644 index 000000000..900a033d4 --- /dev/null +++ b/.run/cpp unittests.run.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/.run/dbg a.ark.run.xml b/.run/dbg a.ark.run.xml new file mode 100644 index 000000000..c2967a0da --- /dev/null +++ b/.run/dbg a.ark.run.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/.run/format a.ark.run.xml b/.run/format a.ark.run.xml new file mode 100644 index 000000000..707dfa329 --- /dev/null +++ b/.run/format a.ark.run.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/.run/format b.ark.run.xml b/.run/format b.ark.run.xml new file mode 100644 index 000000000..b66bed6cd --- /dev/null +++ b/.run/format b.ark.run.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/.run/repl.run.xml b/.run/repl.run.xml new file mode 100644 index 000000000..0b0c6772a --- /dev/null +++ b/.run/repl.run.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/.run/update expected tests.run.xml b/.run/update expected tests.run.xml new file mode 100644 index 000000000..4f3baa4d8 --- /dev/null +++ b/.run/update expected tests.run.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 147f1ad5d..c6e8d7d12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ ### Added - added new macro `$gensym`, to generate a unique symbol identifier to use in macros -- `append`, `concat`, and `pop` can be use as values +- `append`, `concat`, and `pop` can be used as values +- new `ptr` command for the debugger, printing the VM pointers (ip, pp, sp) ### Changed - all paths inside `if` should return a value, when used as an expression. If an `else` branch is missing, `nil` will be returned diff --git a/README.md b/README.md index 82e162463..48cc77493 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,23 @@ - # ArkScript ![Latest version](https://img.shields.io/github/v/release/arkscript-lang/ark?style=for-the-badge&include_prereleases) +# ArkScript ![Latest version](https://img.shields.io/github/v/release/arkscript-lang/ark?style=for-the-badge&include_prereleases) + + ![Code size](https://img.shields.io/github/languages/code-size/arkscript-lang/ark?style=for-the-badge&logo=github) ![Downloads](https://img.shields.io/github/downloads/arkscript-lang/ark/total?color=%2324cc24&style=for-the-badge&logo=github) @@ -173,22 +192,22 @@ DESCRIPTION ArkScript programming language SYNOPSIS - arkscript -h - arkscript -v - arkscript --dev-info - arkscript -e + arkscript -h + arkscript -v + arkscript --dev-info + arkscript -e arkscript [-d] [-L ] [-f(importsolver|no-importsolver)] [-f(macroprocessor|no-macroprocessor)] [-f(optimizer|no-optimizer)] [-f(iroptimizer|no-iroptimizer)] [-fdebugger] [-fdump-ir] [-fno-cache] ((-c - ) | ) - - arkscript -f [--(dry-run|check)] - arkscript [-d] [-L ] --ast - arkscript -bcr -on - arkscript -bcr -a [-s ] - arkscript -bcr -st [-s ] - arkscript -bcr -vt [-s ] - arkscript -bcr [-cs] [-p ] [-s ] + ) | ) + + arkscript -f [--(dry-run|check)] + arkscript [-d] [-L ] --ast + arkscript -bcr -on + arkscript -bcr -a [-s ] + arkscript -bcr -st [-s ] + arkscript -bcr -vt [-s ] + arkscript -bcr [-cs] [-p ] [-s ] OPTIONS -h, --help Display this message diff --git a/include/Ark/VM/Debugger.hpp b/include/Ark/VM/Debugger.hpp index eb5d76f3e..6f1b2189c 100644 --- a/include/Ark/VM/Debugger.hpp +++ b/include/Ark/VM/Debugger.hpp @@ -11,10 +11,12 @@ #ifndef ARK_VM_DEBUGGER_HPP #define ARK_VM_DEBUGGER_HPP +#include #include #include #include #include +#include #include #include @@ -103,6 +105,41 @@ namespace Ark::internal } private: + struct CommandArgs + { + VM* vm_ptr; + ExecutionContext* ctx_ptr; + std::size_t ip, pp; + }; + + struct StartsWith_t + { + } StartsWith; + + struct Command + { + using Action_t = std::function; + + bool is_exact; + std::vector names; + std::string description; + Action_t action; + + Command(std::string name, std::string desc, Action_t&& do_this) : + is_exact(true), names({ std::move(name) }), description(std::move(desc)), action(do_this) + {} + + Command(const std::initializer_list list_of_names, std::string desc, Action_t&& do_this) : + is_exact(true), names(list_of_names), description(std::move(desc)), action(do_this) + {} + + Command(StartsWith_t, std::string start, std::string desc, Action_t&& do_this) : + is_exact(false), names({ std::move(start) }), description(std::move(desc)), action(std::move(do_this)) + {} + }; + + std::vector m_commands; + std::vector> m_states; std::vector m_libenv; std::vector m_symbols; @@ -116,6 +153,9 @@ namespace Ark::internal std::string m_code; ///< Code added while inside the debugger std::size_t m_line_count { 0 }; + void initCommands(); + std::optional matchCommand(const std::string& line) const; + void showContext(const VM& vm, const ExecutionContext& context) const; void showStack(VM& vm, const ExecutionContext& context, std::size_t count) const; void showLocals(VM& vm, ExecutionContext& context, std::size_t count) const; diff --git a/src/arkreactor/Builtins/String.cpp b/src/arkreactor/Builtins/String.cpp index 16a880bd0..352b2ac3a 100644 --- a/src/arkreactor/Builtins/String.cpp +++ b/src/arkreactor/Builtins/String.cpp @@ -226,16 +226,7 @@ namespace Ark::internal::Builtins::String else if (it->valueType() == ValueType::False) store.push_back("false"); else if (it->valueType() == ValueType::List) - { - // std::vector r; - // std::ranges::transform( - // it->list(), - // std::back_inserter(r), - // [&vm](const Value& val) -> value_wrapper { - // return value_wrapper { val, vm }; - // }); store.push_back(value_wrapper { *it, vm }); - } else store.push_back(it->toString(*vm)); } diff --git a/src/arkreactor/VM/Debugger.cpp b/src/arkreactor/VM/Debugger.cpp index 5b1194179..d97eb41ec 100644 --- a/src/arkreactor/VM/Debugger.cpp +++ b/src/arkreactor/VM/Debugger.cpp @@ -23,6 +23,7 @@ namespace Ark::internal m_os(std::cout), m_colorize(true) { + initCommands(); saveState(context); } @@ -33,7 +34,9 @@ namespace Ark::internal m_os(os), m_colorize(false), m_prompt_stream(std::make_unique(path_to_prompt_file)) - {} + { + initCommands(); + } void Debugger::saveState(const ExecutionContext& context) { @@ -128,6 +131,93 @@ namespace Ark::internal m_running = false; } + void Debugger::initCommands() + { + m_commands = { + Command( + "help", + "display this message", + [this](const std::string&, const CommandArgs&) { + fmt::println(m_os, "Available commands:"); + for (const Command& cmd : m_commands) + { + if (cmd.is_exact) + fmt::println(m_os, " {} -- {}", fmt::join(cmd.names, ", "), cmd.description); + else + // todo: make arguments description configurable + fmt::println(m_os, " {} -- {}", fmt::join(cmd.names, ", "), cmd.description); + } + return false; + }), + Command( + { "c", "continue" }, + "resume execution", + [this](const std::string&, const CommandArgs&) { + fmt::println(m_os, "dbg: continue"); + return true; + }), + Command( + { "q", "quit" }, + "quit the debugger, stopping the script execution", + [this](const std::string&, const CommandArgs&) { + fmt::println(m_os, "dbg: stop"); + m_quit_vm = true; + return true; + }), + Command( + StartsWith, + "stack", + "show the last n values on the stack", + [this](const std::string& line, const CommandArgs& args) { + if (const auto arg = getArgAndParseOrError("stack", line, /* default_value= */ 5)) + showStack(*args.vm_ptr, *args.ctx_ptr, arg.value()); + return false; + }), + Command( + StartsWith, + "locals", + "show the last n values on the locals' stack", + [this](const std::string& line, const CommandArgs& args) { + if (const auto arg = getArgAndParseOrError("locals", line, /* default_value= */ 5)) + showLocals(*args.vm_ptr, *args.ctx_ptr, arg.value()); + return false; + }), + Command( + "ptr", + "show the values of the VM pointers", + [this](const std::string&, const CommandArgs& args) { + fmt::println( + m_os, + "IP: {} - PP: {} - SP: {}", + fmt::styled(args.ip / 4, m_colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()), + fmt::styled(args.pp, m_colorize ? fmt::fg(fmt::color::green) : fmt::text_style()), + fmt::styled(args.ctx_ptr->sp, m_colorize ? fmt::fg(fmt::color::yellow) : fmt::text_style())); + return false; + }), + }; + } + + std::optional Debugger::matchCommand(const std::string& line) const + { + for (const Command& c : m_commands) + { + if (c.is_exact) + { + if (std::ranges::find(c.names, line) != c.names.end()) + return c; + } + else + { + if (std::ranges::find_if(c.names, [&line](const std::string& name) -> bool { + return line.starts_with(name); + }) != c.names.end()) + return c; + } + } + + return std::nullopt; + } + void Debugger::showContext(const VM& vm, const ExecutionContext& context) const { // show the line where the breakpoint hit @@ -276,43 +366,20 @@ namespace Ark::internal Utils::trimWhitespace(line); - if (line == "c" || line == "continue" || line.empty()) + if (line.empty() && !unfinished_block) { fmt::println(m_os, "dbg: continue"); return std::nullopt; } - else if (line == "q" || line == "quit") - { - fmt::println(m_os, "dbg: stop"); - m_quit_vm = true; - return std::nullopt; - } - else if (line.starts_with("stack")) - { - if (auto arg = getArgAndParseOrError("stack", line, /* default_value= */ 5)) - showStack(vm, context, arg.value()); - else - return std::nullopt; - } - else if (line.starts_with("locals")) + + if (const auto& maybe_cmd = matchCommand(line)) { - if (auto arg = getArgAndParseOrError("locals", line, /* default_value= */ 5)) - showLocals(vm, context, arg.value()); - else + if (maybe_cmd->action(line, CommandArgs { .vm_ptr = &vm, .ctx_ptr = &context, .ip = ip, .pp = pp })) return std::nullopt; } - else if (line == "help") - { - fmt::println(m_os, "Available commands:"); - fmt::println(m_os, " help -- display this message"); - fmt::println(m_os, " c, continue -- resume execution"); - fmt::println(m_os, " q, quit -- quit the debugger, stopping the script execution"); - fmt::println(m_os, " stack -- show the last n values on the stack"); - fmt::println(m_os, " locals -- show the last n values on the locals' stack"); - } else { - code += line; + code += line + "\n"; open_parens += Utils::countOpenEnclosures(line, '(', ')'); open_braces += Utils::countOpenEnclosures(line, '{', '}'); diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 27765f2dc..aa03026cf 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -106,7 +106,7 @@ namespace Ark } } - Value VM::createList(const std::size_t count, internal::ExecutionContext& context) + Value VM::createList(const std::size_t count, ExecutionContext& context) { Value l(ValueType::List); if (count != 0) diff --git a/tests/unittests/TestsHelper.cpp b/tests/unittests/TestsHelper.cpp index 826f482ac..89c01a17b 100644 --- a/tests/unittests/TestsHelper.cpp +++ b/tests/unittests/TestsHelper.cpp @@ -94,9 +94,14 @@ std::string sanitizeOutput(const std::string& output) // remove the directory prefix so that we are environment agnostic 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)); + + auto lines = Ark::Utils::splitString(diag, '\n'); + diag = ""; + for (std::string& line : lines) + diag += Ark::Utils::rtrim(line) + "\n"; + // we most likely have a blank line at the end now - Ark::Utils::rtrim(diag); + Ark::Utils::ltrim(Ark::Utils::rtrim(diag)); return diag; } diff --git a/tests/unittests/resources/DebuggerSuite/basic.expected b/tests/unittests/resources/DebuggerSuite/basic.expected index cc189307c..0f01725f8 100644 --- a/tests/unittests/resources/DebuggerSuite/basic.expected +++ b/tests/unittests/resources/DebuggerSuite/basic.expected @@ -6,10 +6,14 @@ In file tests/unittests/resources/DebuggerSuite/basic.ark:3 4 | (prn (format "ark: after first breakpoint, a={}, b={}" a b)) 5 | -dbg[pp:0,ip:3]:000> (let c (+ a b)) -dbg[pp:0,ip:3]:001> (prn c) +dbg[pp:0,ip:3]:000> (let c +dbg[pp:0,ip:3]:001: (+ +dbg[pp:0,ip:3]:002: a +dbg[pp:0,ip:3]:003: +dbg[pp:0,ip:3]:004: b)) +dbg[pp:0,ip:3]:005> (prn c) 11 -dbg[pp:0,ip:3]:002> c +dbg[pp:0,ip:3]:006> c dbg: continue ark: after first breakpoint, a=5, b=6 @@ -31,6 +35,7 @@ Available commands: q, quit -- quit the debugger, stopping the script execution stack -- show the last n values on the stack locals -- show the last n values on the locals' stack + ptr -- show the values of the VM pointers dbg[pp:1,ip:5]:001> continue dbg: continue ark: in (foo x y z), after second breakpoint diff --git a/tests/unittests/resources/DebuggerSuite/basic.prompt b/tests/unittests/resources/DebuggerSuite/basic.prompt index 0f5648781..42369549e 100644 --- a/tests/unittests/resources/DebuggerSuite/basic.prompt +++ b/tests/unittests/resources/DebuggerSuite/basic.prompt @@ -1,4 +1,8 @@ -(let c (+ a b)) +(let c +(+ +a + +b)) (prn c) c (prn x y z) diff --git a/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected b/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected index 7638509e4..deb72821a 100644 --- a/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected +++ b/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected @@ -7,6 +7,8 @@ In file tests/unittests/resources/DebuggerSuite/stack_and_locals.ark:8 9 | (prn (f 1 2)) 10 | +dbg[pp:0,ip:2]:000> ptr +IP: 2 - PP: 0 - SP: 0 dbg[pp:0,ip:2]:000> stack Stack is empty diff --git a/tests/unittests/resources/DebuggerSuite/stack_and_locals.prompt b/tests/unittests/resources/DebuggerSuite/stack_and_locals.prompt index c25e5fe48..8f6fe06c4 100644 --- a/tests/unittests/resources/DebuggerSuite/stack_and_locals.prompt +++ b/tests/unittests/resources/DebuggerSuite/stack_and_locals.prompt @@ -1,3 +1,4 @@ +ptr stack locals c