From 8de2d80e3bc960d04769d233bdd261f85b8d02b9 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sat, 11 May 2024 17:57:04 +0200 Subject: [PATCH 1/6] refactor(repl): use modern C++ instead of C, tidy up the code and make it more maintainable --- include/CLI/REPL/ConsoleStyle.hpp | 173 -------------------------- include/CLI/REPL/Repl.hpp | 28 +++-- include/CLI/REPL/Utils.hpp | 194 ++++++++++++++++++++++++++++++ include/CLI/REPL/replxx/Util.hpp | 29 ----- include/utf8.hpp | 3 - src/arkscript/REPL/Repl.cpp | 175 ++++++++++----------------- src/arkscript/REPL/Utils.cpp | 177 +++++++++++++-------------- 7 files changed, 364 insertions(+), 415 deletions(-) delete mode 100644 include/CLI/REPL/ConsoleStyle.hpp create mode 100644 include/CLI/REPL/Utils.hpp delete mode 100644 include/CLI/REPL/replxx/Util.hpp diff --git a/include/CLI/REPL/ConsoleStyle.hpp b/include/CLI/REPL/ConsoleStyle.hpp deleted file mode 100644 index a71c27895..000000000 --- a/include/CLI/REPL/ConsoleStyle.hpp +++ /dev/null @@ -1,173 +0,0 @@ -/** - * @file ConsoleStyle.hpp - * @author Alexandre Plateau (lexplt.dev@gmail.com) - * @brief Colors per token used by replxx - * @version 0.2 - * @date 2020-10-27 - * - * @copyright Copyright (c) 2020-2022 - * - */ - -#ifndef ARK_REPL_CONSOLESTYLE_HPP -#define ARK_REPL_CONSOLESTYLE_HPP - -#include - -using Replxx = replxx::Replxx; - -namespace Ark -{ - const std::vector KeywordsDict { - // Keywords - "if", "let", "mut", "set", - "fun", "while", "begin", "import", - "quote", "del", - // Operators - "len", "empty?", "tail", "head", - "nil?", "assert", "toNumber", - "toString", "and", "or", "mod", - "type", "hasField", "not", - // Constants - "true", "false", "nil", "math:pi", - "math:e", "math:tau", "math:Inf", "math:NaN", - // Functions - // List - "append", "concat", "list", "list:reverse", - "list:find", "list:slice", "list:sort", - "list:fill", "list:setAt", - // IO - "print", "puts", "input", "io:writeFile", - "io:readFile", "io:fileExists?", "io:listFiles", "io:dir?", - "io:makeDir", "io:removeFiles", - // Times - "time", - // System - "sys:exec", "sys:sleep", - // String - "str:format", "str:find", "str:removeAt", - // Mathematics - "math:exp", "math:ln", "math:ceil", "math:floor", - "math:round", "math:NaN?", "Inf?", "math:cos", - "math:sin", "math:tan", "math:arccos", "math:arcsin", - "math:arctan", - // Commands - "quit" - }; - - const std::vector> ColorsRegexDict { - // Keywords - { "if", Replxx::Color::BRIGHTRED }, - { "let", Replxx::Color::BRIGHTRED }, - { "mut", Replxx::Color::BRIGHTRED }, - { "set", Replxx::Color::BRIGHTRED }, - { "fun", Replxx::Color::BRIGHTRED }, - { "while", Replxx::Color::BRIGHTRED }, - { "begin", Replxx::Color::BRIGHTRED }, - { "import", Replxx::Color::BRIGHTRED }, - { "quote", Replxx::Color::BRIGHTRED }, - { "del", Replxx::Color::BRIGHTRED }, - // Operators - { "\\\"", Replxx::Color::BRIGHTBLUE }, - { "\\-", Replxx::Color::BRIGHTBLUE }, - { "\\+", Replxx::Color::BRIGHTBLUE }, - { "\\=", Replxx::Color::BRIGHTBLUE }, - { "\\/", Replxx::Color::BRIGHTBLUE }, - { "\\*", Replxx::Color::BRIGHTBLUE }, - { "\\<", Replxx::Color::BRIGHTBLUE }, - { "\\>", Replxx::Color::BRIGHTBLUE }, - { "\\!", Replxx::Color::BRIGHTBLUE }, - { "\\[", Replxx::Color::BRIGHTBLUE }, - { "\\]", Replxx::Color::BRIGHTBLUE }, - { "@", Replxx::Color::BRIGHTBLUE }, - { "len", Replxx::Color::BRIGHTBLUE }, - { "empty?", Replxx::Color::BRIGHTBLUE }, - { "tail", Replxx::Color::BRIGHTBLUE }, - { "head", Replxx::Color::BRIGHTBLUE }, - { "nil?", Replxx::Color::BRIGHTBLUE }, - { "assert", Replxx::Color::BRIGHTBLUE }, - { "toNumber", Replxx::Color::BRIGHTBLUE }, - { "toString", Replxx::Color::BRIGHTBLUE }, - { "and", Replxx::Color::BRIGHTBLUE }, - { "or ", Replxx::Color::BRIGHTBLUE }, - { "mod", Replxx::Color::BRIGHTBLUE }, - { "type", Replxx::Color::BRIGHTBLUE }, - { "hasField", Replxx::Color::BRIGHTBLUE }, - { "not", Replxx::Color::BRIGHTBLUE }, - // Constants - { "true", Replxx::Color::RED }, - { "false", Replxx::Color::RED }, - { "nil", Replxx::Color::RED }, - { "math:pi", Replxx::Color::BLUE }, - { "math:e", Replxx::Color::BLUE }, - { "math:tau", Replxx::Color::BLUE }, - { "math:Inf", Replxx::Color::BLUE }, - { "math:NaN", Replxx::Color::BLUE }, - // Functions - // List - { "append", Replxx::Color::BRIGHTGREEN }, - { "concat", Replxx::Color::BRIGHTGREEN }, - { "pop", Replxx::Color::BRIGHTGREEN }, - { "append!", Replxx::Color::BRIGHTGREEN }, - { "concat!", Replxx::Color::BRIGHTGREEN }, - { "pop!", Replxx::Color::BRIGHTGREEN }, - { "list", Replxx::Color::BRIGHTGREEN }, - { "list:reverse", Replxx::Color::BRIGHTGREEN }, - { "list:find", Replxx::Color::BRIGHTGREEN }, - { "list:slice", Replxx::Color::BRIGHTGREEN }, - { "list:sort", Replxx::Color::BRIGHTGREEN }, - { "list:fill", Replxx::Color::BRIGHTGREEN }, - { "list:setAt", Replxx::Color::BRIGHTGREEN }, - // IO - { "print", Replxx::Color::GREEN }, - { "puts", Replxx::Color::GREEN }, - { "input", Replxx::Color::GREEN }, - { "io:writeFile", Replxx::Color::GREEN }, - { "io:readFile", Replxx::Color::GREEN }, - { "io:fileExists?", Replxx::Color::GREEN }, - { "io:listFiles", Replxx::Color::GREEN }, - { "io:dir?", Replxx::Color::GREEN }, - { "io:makeDir", Replxx::Color::GREEN }, - { "io:removeFiles", Replxx::Color::GREEN }, - // Times - { "time", Replxx::Color::GREEN }, - // System - { "sys:exec", Replxx::Color::GREEN }, - { "sys:sleep", Replxx::Color::GREEN }, - { "sys:exit", Replxx::Color::GREEN }, - // String - { "str:format", Replxx::Color::BRIGHTGREEN }, - { "str:find", Replxx::Color::BRIGHTGREEN }, - { "str:removeAt", Replxx::Color::BRIGHTGREEN }, - { "str:ord", Replxx::Color::BRIGHTGREEN }, - { "str:chr", Replxx::Color::BRIGHTGREEN }, - // Mathematics - { "math:exp", Replxx::Color::BRIGHTCYAN }, - { "math:ln", Replxx::Color::BRIGHTCYAN }, - { "math:ceil", Replxx::Color::BRIGHTCYAN }, - { "math:floor", Replxx::Color::BRIGHTCYAN }, - { "math:round", Replxx::Color::BRIGHTCYAN }, - { "math:NaN?", Replxx::Color::BRIGHTCYAN }, - { "math:Inf?", Replxx::Color::BRIGHTCYAN }, - { "math:cos", Replxx::Color::BRIGHTCYAN }, - { "math:sin", Replxx::Color::BRIGHTCYAN }, - { "math:tan", Replxx::Color::BRIGHTCYAN }, - { "math:arccos", Replxx::Color::BRIGHTCYAN }, - { "math:arcsin", Replxx::Color::BRIGHTCYAN }, - { "math:arctan", Replxx::Color::BRIGHTCYAN }, - { "math:cosh", Replxx::Color::BRIGHTCYAN }, - { "math:sinh", Replxx::Color::BRIGHTCYAN }, - { "math:tanh", Replxx::Color::BRIGHTCYAN }, - { "math:acosh", Replxx::Color::BRIGHTCYAN }, - { "math:asinh", Replxx::Color::BRIGHTCYAN }, - { "math:atanh", Replxx::Color::BRIGHTCYAN }, - // Numbers - { "[\\-|+]{0,1}[0-9]+(\\.[0-9]+)?", Replxx::Color::YELLOW }, - // Strings - { "\".*?\"", Replxx::Color::BRIGHTGREEN }, - // Commands - { "quit", Replxx::Color::BRIGHTMAGENTA } - }; -} - -#endif diff --git a/include/CLI/REPL/Repl.hpp b/include/CLI/REPL/Repl.hpp index 80072876f..50765549f 100644 --- a/include/CLI/REPL/Repl.hpp +++ b/include/CLI/REPL/Repl.hpp @@ -2,10 +2,10 @@ * @file Repl.hpp * @author Alexandre Plateau (lexplt.dev@gmail.com) * @brief ArkScript REPL - Read Eval Print Loop - * @version 0.2 + * @version 1.0 * @date 2020-10-27 * - * @copyright Copyright (c) 2020-2021 + * @copyright Copyright (c) 2020-2024 * */ @@ -14,12 +14,14 @@ #include #include +#include #include #include #include #include -#include + +#include namespace Ark { @@ -29,9 +31,9 @@ namespace Ark /** * @brief Construct a new Repl object * - * @param libenv search path for the std library + * @param lib_env search path for the std library */ - explicit Repl(const std::vector& libenv); + explicit Repl(const std::vector& lib_env); /** * @brief Start the REPL @@ -40,16 +42,16 @@ namespace Ark int run(); private: - Replxx m_repl; - unsigned m_lines; + replxx::Replxx m_repl; int m_old_ip; - std::vector m_libenv; + std::vector m_lib_env; + unsigned m_line_count; + bool m_running; + + void cuiSetup(); - static inline void print_repl_header(); - static int count_open_parentheses(const std::string& line); - static int count_open_braces(const std::string& line); - static void trim_whitespace(std::string& line); - void cui_setup(); + std::optional getLine(); + std::optional getCodeBlock(); }; } diff --git a/include/CLI/REPL/Utils.hpp b/include/CLI/REPL/Utils.hpp new file mode 100644 index 000000000..fbc356ada --- /dev/null +++ b/include/CLI/REPL/Utils.hpp @@ -0,0 +1,194 @@ +/** + * @file Utils.hpp + * @author Alexandre Plateau (lexplt.dev@gmail.com) + * @brief replxx utilities + * @version 1.0 + * @date 2020-10-27 + * + * @copyright Copyright (c) 2020-2024 + * + */ + +#ifndef REPL_REPLXX_UTIL_HPP +#define REPL_REPLXX_UTIL_HPP + +#include +#include +#include + +#include + +namespace Ark::internal +{ + const std::vector KeywordsDict { + // Keywords + "if", "let", "mut", "set", "fun", "while", "begin", "import", "del", + // Operators + "len", "empty?", "tail", "head", + "nil?", "assert", "toNumber", + "toString", "and", "or", "mod", + "type", "hasField", "not", + // Constants + "true", "false", "nil", "math:pi", + "math:e", "math:tau", "math:Inf", "math:NaN", + // Functions + // List + "append", "concat", "list", "append!", "concat!", "pop", "pop!", + "list:reverse", "list:find", "list:slice", "list:sort", "list:fill", "list:setAt", + // IO + "print", "puts", "input", "io:writeFile", + "io:readFile", "io:fileExists?", "io:listFiles", "io:dir?", + "io:makeDir", "io:removeFiles", + // Times + "time", + // System + "sys:exec", "sys:sleep", + // String + "str:format", "str:find", "str:removeAt", + // Mathematics + "math:exp", "math:ln", "math:ceil", "math:floor", + "math:round", "math:NaN?", "math:Inf?", "math:cos", + "math:sin", "math:tan", "math:arccos", "math:arcsin", + "math:arctan", + // Commands + "quit" + }; + + const std::vector> ColorsRegexDict { + // Keywords + { "if", replxx::Replxx::Color::BRIGHTRED }, + { "let", replxx::Replxx::Color::BRIGHTRED }, + { "mut", replxx::Replxx::Color::BRIGHTRED }, + { "set", replxx::Replxx::Color::BRIGHTRED }, + { "fun", replxx::Replxx::Color::BRIGHTRED }, + { "while", replxx::Replxx::Color::BRIGHTRED }, + { "begin", replxx::Replxx::Color::BRIGHTRED }, + { "import", replxx::Replxx::Color::BRIGHTRED }, + { "quote", replxx::Replxx::Color::BRIGHTRED }, + { "del", replxx::Replxx::Color::BRIGHTRED }, + // Operators + { "\\\"", replxx::Replxx::Color::BRIGHTBLUE }, + { "\\-", replxx::Replxx::Color::BRIGHTBLUE }, + { "\\+", replxx::Replxx::Color::BRIGHTBLUE }, + { "\\=", replxx::Replxx::Color::BRIGHTBLUE }, + { "\\/", replxx::Replxx::Color::BRIGHTBLUE }, + { "\\*", replxx::Replxx::Color::BRIGHTBLUE }, + { "\\<", replxx::Replxx::Color::BRIGHTBLUE }, + { "\\>", replxx::Replxx::Color::BRIGHTBLUE }, + { "\\!", replxx::Replxx::Color::BRIGHTBLUE }, + { "\\[", replxx::Replxx::Color::BRIGHTBLUE }, + { "\\]", replxx::Replxx::Color::BRIGHTBLUE }, + { "@", replxx::Replxx::Color::BRIGHTBLUE }, + { "len", replxx::Replxx::Color::BRIGHTBLUE }, + { "empty\\?", replxx::Replxx::Color::BRIGHTBLUE }, + { "tail", replxx::Replxx::Color::BRIGHTBLUE }, + { "head", replxx::Replxx::Color::BRIGHTBLUE }, + { "nil\\?", replxx::Replxx::Color::BRIGHTBLUE }, + { "assert", replxx::Replxx::Color::BRIGHTBLUE }, + { "toNumber", replxx::Replxx::Color::BRIGHTBLUE }, + { "toString", replxx::Replxx::Color::BRIGHTBLUE }, + { "and", replxx::Replxx::Color::BRIGHTBLUE }, + { "or ", replxx::Replxx::Color::BRIGHTBLUE }, + { "mod", replxx::Replxx::Color::BRIGHTBLUE }, + { "type", replxx::Replxx::Color::BRIGHTBLUE }, + { "hasField", replxx::Replxx::Color::BRIGHTBLUE }, + { "not", replxx::Replxx::Color::BRIGHTBLUE }, + // Constants + { "true", replxx::Replxx::Color::RED }, + { "false", replxx::Replxx::Color::RED }, + { "nil", replxx::Replxx::Color::RED }, + { "math:pi", replxx::Replxx::Color::BLUE }, + { "math:e", replxx::Replxx::Color::BLUE }, + { "math:tau", replxx::Replxx::Color::BLUE }, + { "math:Inf", replxx::Replxx::Color::BLUE }, + { "math:NaN", replxx::Replxx::Color::BLUE }, + // Functions + // List + { "append", replxx::Replxx::Color::BRIGHTGREEN }, + { "concat", replxx::Replxx::Color::BRIGHTGREEN }, + { "pop", replxx::Replxx::Color::BRIGHTGREEN }, + { "append!", replxx::Replxx::Color::BRIGHTGREEN }, + { "concat!", replxx::Replxx::Color::BRIGHTGREEN }, + { "pop!", replxx::Replxx::Color::BRIGHTGREEN }, + { "list", replxx::Replxx::Color::BRIGHTGREEN }, + { "list:reverse", replxx::Replxx::Color::BRIGHTGREEN }, + { "list:find", replxx::Replxx::Color::BRIGHTGREEN }, + { "list:slice", replxx::Replxx::Color::BRIGHTGREEN }, + { "list:sort", replxx::Replxx::Color::BRIGHTGREEN }, + { "list:fill", replxx::Replxx::Color::BRIGHTGREEN }, + { "list:setAt", replxx::Replxx::Color::BRIGHTGREEN }, + // IO + { "print", replxx::Replxx::Color::GREEN }, + { "puts", replxx::Replxx::Color::GREEN }, + { "input", replxx::Replxx::Color::GREEN }, + { "io:writeFile", replxx::Replxx::Color::GREEN }, + { "io:readFile", replxx::Replxx::Color::GREEN }, + { "io:fileExists\\?", replxx::Replxx::Color::GREEN }, + { "io:listFiles", replxx::Replxx::Color::GREEN }, + { "io:dir\\?", replxx::Replxx::Color::GREEN }, + { "io:makeDir", replxx::Replxx::Color::GREEN }, + { "io:removeFiles", replxx::Replxx::Color::GREEN }, + // Times + { "time", replxx::Replxx::Color::GREEN }, + // System + { "sys:exec", replxx::Replxx::Color::GREEN }, + { "sys:sleep", replxx::Replxx::Color::GREEN }, + { "sys:exit", replxx::Replxx::Color::GREEN }, + // String + { "str:format", replxx::Replxx::Color::BRIGHTGREEN }, + { "str:find", replxx::Replxx::Color::BRIGHTGREEN }, + { "str:removeAt", replxx::Replxx::Color::BRIGHTGREEN }, + { "str:ord", replxx::Replxx::Color::BRIGHTGREEN }, + { "str:chr", replxx::Replxx::Color::BRIGHTGREEN }, + // Mathematics + { "math:exp", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:ln", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:ceil", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:floor", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:round", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:NaN\\?", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:Inf\\?", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:cos", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:sin", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:tan", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:arccos", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:arcsin", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:arctan", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:cosh", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:sinh", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:tanh", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:acosh", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:asinh", replxx::Replxx::Color::BRIGHTCYAN }, + { "math:atanh", replxx::Replxx::Color::BRIGHTCYAN }, + // Numbers + { "[\\-|+]{0,1}[0-9]+(\\.[0-9]+)?", replxx::Replxx::Color::YELLOW }, + // Strings + { "\".*?\"", replxx::Replxx::Color::BRIGHTGREEN }, + // Commands + { "quit", replxx::Replxx::Color::BRIGHTMAGENTA } + }; + + + /** + * @brief Count the open enclosure and its counterpart: (), {}, [] + * @param line data to operate on + * @param open the open char: (, { or [ + * @param close the closing char: ), } or ] + * @return positive if there are more open enclosures than closed. 0 when both are equal, negative otherwise + */ + long countOpenEnclosures(const std::string& line, char open, char close); + + /** + * @brief Remove whitespaces at the start and end of a string + * @param line string modified in place + */ + void trimWhitespace(std::string& line); + + replxx::Replxx::completions_t hookCompletion(const std::string& context, int& length); + + void hookColor(const std::string& str, replxx::Replxx::colors_t& colors); + + replxx::Replxx::hints_t hookHint(const std::string& context, int& length, replxx::Replxx::Color& color); +} + +#endif diff --git a/include/CLI/REPL/replxx/Util.hpp b/include/CLI/REPL/replxx/Util.hpp deleted file mode 100644 index 6f9de31f9..000000000 --- a/include/CLI/REPL/replxx/Util.hpp +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @file Util.hpp - * @author Alexandre Plateau (lexplt.dev@gmail.com) - * @brief replxx utilities - * @version 0.1 - * @date 2020-10-27 - * - * @copyright Copyright (c) 2020-2021 - * - */ - -#ifndef REPL_REPLXX_UTIL_HPP -#define REPL_REPLXX_UTIL_HPP - -#include -#include -#include - -#include - -using Replxx = replxx::Replxx; - -int utf8str_codepoint_len(char const* s, int utf8len); -int context_len(char const* prefix); -Replxx::completions_t hook_completion(std::string const& context, int& contextLen, std::vector const& user_data); -void hook_color(std::string const& str, Replxx::colors_t& colors, std::vector> const& user_data); -Replxx::hints_t hook_hint(std::string const& context, int& contextLen, Replxx::Color& color, std::vector const& examples); - -#endif diff --git a/include/utf8.hpp b/include/utf8.hpp index faa644a64..1a1b4b913 100644 --- a/include/utf8.hpp +++ b/include/utf8.hpp @@ -194,9 +194,6 @@ namespace utf8 if (isValid(str)) { - if (str == nullptr) - return -1; - while (*s != 0) { if (0xf0 == (0xf8 & *s)) diff --git a/src/arkscript/REPL/Repl.cpp b/src/arkscript/REPL/Repl.cpp index acc091108..61abb5ab2 100644 --- a/src/arkscript/REPL/Repl.cpp +++ b/src/arkscript/REPL/Repl.cpp @@ -1,80 +1,37 @@ -#include -#include -#include +#include #include -#include +#include namespace Ark { - Repl::Repl(const std::vector& libenv) : - m_lines(1), m_old_ip(0), m_libenv(libenv) + using namespace internal; + + Repl::Repl(const std::vector& lib_env) : + m_old_ip(0), m_lib_env(lib_env), m_line_count(1), m_running(true) {} int Repl::run() { - Ark::State state(m_libenv); + Ark::State state(m_lib_env); Ark::VM vm(state); state.setDebug(0); - std::string code; bool init = false; - print_repl_header(); - cui_setup(); + fmt::print("ArkScript REPL -- Version {} [LICENSE: Mozilla Public License 2.0]\nType \"(quit)\" to quit.\n", ARK_FULL_VERSION); + cuiSetup(); - while (true) + std::string code; + while (m_running) { - std::stringstream tmp_code; - unsigned open_parentheses = 0; - unsigned open_braces = 0; - - tmp_code << code; - while (true) - { - std::string str_lines = "000"; - if (std::to_string(m_lines).size() < 3) - { - std::size_t size = std::to_string(m_lines).size(); - str_lines.replace((str_lines.size() - size), size, std::to_string(m_lines)); - } - else - str_lines = std::to_string(m_lines); - - std::string prompt = "main:" + str_lines + "> "; - char const* buf { nullptr }; - do - { - buf = m_repl.input(prompt); - } while ((buf == nullptr) && (errno == EAGAIN)); - std::string line = (buf != nullptr) ? std::string(buf) : ""; - - // line history - m_repl.history_add(line); - trim_whitespace(line); - - // specific commands handling - if (line == "(quit)" || buf == nullptr) - { - std::cout << "\nExiting REPL\n"; - return 1; - } - - if (!line.empty()) - tmp_code << line << "\n"; - open_parentheses += count_open_parentheses(line); - open_braces += count_open_braces(line); - - // lines number incrementation - ++m_lines; - if (open_parentheses == 0 && open_braces == 0) - break; - } + auto maybe_block = getCodeBlock(); // save a valid ip if execution failed m_old_ip = vm.m_execution_contexts[0]->ip; - if (!tmp_code.str().empty()) + if (maybe_block.has_value() && !maybe_block.value().empty()) { - if (state.doString(tmp_code.str())) + std::string new_code = code + maybe_block.value(); + if (state.doString(new_code)) { // for only one vm init if (!init) @@ -82,10 +39,11 @@ namespace Ark vm.init(); init = true; } + if (vm.safeRun(*vm.m_execution_contexts[0]) == 0) { // save good code - code = tmp_code.str(); + code = new_code; // place ip to end of bytecode instruction (HALT) vm.m_execution_contexts[0]->ip -= 4; } @@ -98,80 +56,77 @@ namespace Ark state.reset(); } else - std::cout << "Ark::State::doString failed\n"; + std::cout << "Couldn't run code\n"; } } return 0; } - inline void Repl::print_repl_header() + void Repl::cuiSetup() { - std::printf( - "ArkScript REPL -- Version %i.%i.%i [LICENSE: Mozilla Public License 2.0]\n" - "Type \"(quit)\" to quit.\n", - ARK_VERSION_MAJOR, - ARK_VERSION_MINOR, - ARK_VERSION_PATCH); + m_repl.set_completion_callback(hookCompletion); + m_repl.set_highlighter_callback(hookColor); + m_repl.set_hint_callback(hookHint); + + m_repl.set_word_break_characters(" \t.,-%!;:=*~^'\"/?<>|[](){}"); + m_repl.set_completion_count_cutoff(128); + m_repl.set_double_tab_completion(false); + m_repl.set_complete_on_empty(true); + m_repl.set_beep_on_ambiguous_completion(false); + m_repl.set_no_color(false); } - int Repl::count_open_parentheses(const std::string& line) + std::optional Repl::getLine() { - int open_parentheses = 0; + std::string prompt = fmt::format("main:{:0>3}> ", m_line_count); - for (const char& c : line) + const char* buf { nullptr }; + do { - switch (c) - { - case '(': ++open_parentheses; break; - case ')': --open_parentheses; break; - default: break; - } + buf = m_repl.input(prompt); + } while ((buf == nullptr) && (errno == EAGAIN)); + std::string line = (buf != nullptr) ? std::string(buf) : ""; + + // line history + m_repl.history_add(line); + trimWhitespace(line); + + // specific commands handling + if (line == "(quit)" || buf == nullptr) + { + std::cout << "\nExiting REPL\n"; + m_running = false; + return std::nullopt; } - return open_parentheses; + return line; } - int Repl::count_open_braces(const std::string& line) + std::optional Repl::getCodeBlock() { - int open_braces = 0; + std::string code_block; + long open_parentheses = 0; + long open_braces = 0; - for (const char& c : line) + while (m_running) { - switch (c) + auto maybe_line = getLine(); + if (!maybe_line.has_value()) + return std::nullopt; + else if (!maybe_line.value().empty()) { - case '{': ++open_braces; break; - case '}': --open_braces; break; - default: break; + code_block += maybe_line.value() + "\n"; + open_parentheses += countOpenEnclosures(maybe_line.value(), '(', ')'); + open_braces += countOpenEnclosures(maybe_line.value(), '{', '}'); } - } - return open_braces; - } - - void Repl::trim_whitespace(std::string& line) - { - size_t string_begin = line.find_first_not_of(" \t"); - if (std::string::npos != string_begin) - { - size_t string_end = line.find_last_not_of(" \t"); - line = line.substr(string_begin, string_end - string_begin + 1); + // lines number incrementation + ++m_line_count; + if (open_parentheses == 0 && open_braces == 0) + break; } - } - - void Repl::cui_setup() - { - using namespace std::placeholders; - - m_repl.set_completion_callback(std::bind(&hook_completion, _1, _2, std::cref(KeywordsDict))); - m_repl.set_highlighter_callback(std::bind(&hook_color, _1, _2, std::cref(ColorsRegexDict))); - m_repl.set_hint_callback(std::bind(&hook_hint, _1, _2, _3, std::cref(KeywordsDict))); - m_repl.set_word_break_characters(" \t.,-%!;:=*~^'\"/?<>|[](){}"); - m_repl.set_completion_count_cutoff(128); - m_repl.set_double_tab_completion(false); - m_repl.set_complete_on_empty(true); - m_repl.set_beep_on_ambiguous_completion(false); - m_repl.set_no_color(false); + return code_block; } } diff --git a/src/arkscript/REPL/Utils.cpp b/src/arkscript/REPL/Utils.cpp index 25f97a6c2..732e36f85 100644 --- a/src/arkscript/REPL/Utils.cpp +++ b/src/arkscript/REPL/Utils.cpp @@ -1,118 +1,121 @@ -#include +#include -#include #include -int utf8str_codepoint_len(char const* s, int utf8len) +namespace Ark::internal { - int codepointLen = 0; - unsigned char m4 = 128 + 64 + 32 + 16; - unsigned char m3 = 128 + 64 + 32; - unsigned char m2 = 128 + 64; - - for (int i = 0; i < utf8len; ++i, ++codepointLen) + long countOpenEnclosures(const std::string& line, char open, char close) { - char c = s[i]; - - if ((c & m4) == m4) - i += 3; - else if ((c & m3) == m3) - i += 2; - else if ((c & m2) == m2) - i += 1; + return static_cast(std::count(line.begin(), line.end(), open)) - static_cast(std::count(line.begin(), line.end(), close)); } - return codepointLen; -} - -int context_len(char const* prefix) -{ - char const wb[] = " \t\n\r\v\f-=+*&^%$#@!,./?<>;:`~'\"[]{}()\\|"; - int i = std::strlen(prefix) - 1; - int cl = 0; - - while (i >= 0) + void trimWhitespace(std::string& line) { - if (std::strchr(wb, prefix[i]) != nullptr) - break; - - ++cl; - --i; + size_t string_begin = line.find_first_not_of(" \t"); + if (std::string::npos != string_begin) + { + size_t string_end = line.find_last_not_of(" \t"); + line = line.substr(string_begin, string_end - string_begin + 1); + } } - return cl; -} - -Replxx::completions_t hook_completion(std::string const& context, int& contextLen, std::vector const& examples) -{ - Replxx::completions_t completions; - int utf8ContextLen = context_len(context.c_str()); - int prefixLen = context.length() - utf8ContextLen; - - if ((prefixLen > 0) && (context[prefixLen - 1] == '\\')) + std::size_t codepointLength(const std::string& str) { - --prefixLen; - ++utf8ContextLen; + std::size_t len = 0; + for (auto c : str) + len += (c & 0xc0) != 0x80; + return len; } - contextLen = utf8str_codepoint_len(context.c_str() + prefixLen, utf8ContextLen); - - std::string prefix = context.substr(prefixLen); - for (auto const& e : examples) + std::size_t contextLen(const std::string& prefix) { - if (e.compare(0, prefix.size(), prefix) == 0) - completions.emplace_back(e.c_str()); - } + const std::string word_break = " \t\n\r\v\f=+*&^%$#@!,./?<>;`~'\"[]{}()\\|"; + int i = prefix.size() - 1; + std::size_t count = 0; - return completions; -} + while (i >= 0) + { + if (word_break.find(prefix[i]) != std::string::npos) + break; -void hook_color(std::string const& context, Replxx::colors_t& colors, std::vector> const& regex_color) -{ - // highlight matching regex sequences - for (auto const& e : regex_color) + ++count; + --i; + } + + return count; + } + + replxx::Replxx::completions_t hookCompletion(const std::string& context, int& length) { - std::size_t pos = 0; - std::string str = context; - std::smatch match; + replxx::Replxx::completions_t completions; + std::size_t utf8_context_len = contextLen(context); + std::size_t prefix_len = context.size() - utf8_context_len; - while (std::regex_search(str, match, std::regex(e.first))) + if (prefix_len > 0 && context[prefix_len - 1] == '\\') { - std::string c = match[0]; - std::string prefix = match.prefix().str(); - pos += utf8str_codepoint_len(prefix.c_str(), static_cast(prefix.length())); - int len = utf8str_codepoint_len(c.c_str(), static_cast(c.length())); + --prefix_len; + ++utf8_context_len; + } - for (int i = 0; i < len; ++i) - colors.at(pos + i) = e.second; + length = static_cast(codepointLength(context.substr(prefix_len, utf8_context_len))); - pos += len; - str = match.suffix(); + std::string prefix = context.substr(prefix_len); + for (const auto& e : KeywordsDict) + { + if (e.starts_with(prefix) == 0) + completions.emplace_back(e.c_str()); } + + return completions; } -} -Replxx::hints_t hook_hint(std::string const& context, int& contextLen, Replxx::Color& color, std::vector const& examples) -{ - Replxx::hints_t hints; - // only show hint if prefix is at least 'n' chars long - // or if prefix begins with a specific character - int utf8ContextLen = context_len(context.c_str()); - int prefixLen = context.length() - utf8ContextLen; - contextLen = utf8str_codepoint_len(context.c_str() + prefixLen, utf8ContextLen); - std::string prefix = context.substr(prefixLen); - - if (prefix.size() >= 2 || (!prefix.empty() && prefix.at(0) == '.')) + void hookColor(const std::string& context, replxx::Replxx::colors_t& colors) { - for (auto const& e : examples) + // highlight matching regex sequences + for (const auto& e : ColorsRegexDict) { - if (e.compare(0, prefix.size(), prefix) == 0) - hints.emplace_back(e.c_str()); + std::size_t pos = 0; + std::string str = context; + std::smatch match; + + while (std::regex_search(str, match, std::regex(e.first))) + { + std::string c = match[0]; + std::string prefix = match.prefix().str(); + std::size_t len = codepointLength(c); + + pos += codepointLength(prefix); + for (std::size_t i = 0; i < len; ++i) + colors.at(pos + i) = e.second; + + pos += len; + str = match.suffix(); + } } } - if (hints.size() == 1) - color = Replxx::Color::GREEN; + replxx::Replxx::hints_t hookHint(const std::string& context, int& length, replxx::Replxx::Color& color) + { + replxx::Replxx::hints_t hints; + // only show hint if prefix is at least 'n' chars long + // or if prefix begins with a specific character + std::size_t utf8_context_len = contextLen(context); + std::size_t prefix_len = context.size() - utf8_context_len; + length = static_cast(codepointLength(context.substr(prefix_len, utf8_context_len))); + std::string prefix = context.substr(prefix_len); + + if (prefix.size() >= 2 || (!prefix.empty() && prefix.at(0) == '.')) + { + for (const auto& e : KeywordsDict) + { + if (e.compare(0, prefix.size(), prefix) == 0) + hints.emplace_back(e.c_str()); + } + } + + if (hints.size() == 1) + color = replxx::Replxx::Color::GREEN; - return hints; + return hints; + } } From eed24cb9f3fe3ac53aa319ed5a2f94e6570fa1db Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sat, 11 May 2024 19:12:03 +0200 Subject: [PATCH 2/6] feat(vm, repl): added force reloading of plugins in the VM, closes #337 --- CHANGELOG.md | 1 + include/Ark/VM/VM.hpp | 8 ++++++++ src/arkreactor/VM/VM.cpp | 29 +++++++++++++++++++++++++++++ src/arkscript/REPL/Repl.cpp | 2 ++ 4 files changed, 40 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8725224e..9e5358198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - added boost-ext/ut to write unit tests in C++ - basic ArkScript code formatter, available through the CLI: `arkscript -f|--format` - comments are now tracked in the AST and attached to the nearest node below them +- `VM::forceReloadPlugins`, to be used by the REPL to force reload the plugins and be sure that their symbols are all defined ### Changed - instructions are on 4 bytes: 1 byte for the instruction, 1 byte of padding, 2 bytes for an immediate argument diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp index f1f1a2b34..a16356da3 100644 --- a/include/Ark/VM/VM.hpp +++ b/include/Ark/VM/VM.hpp @@ -146,6 +146,14 @@ namespace Ark */ void deleteFuture(internal::Future* f); + /** + * @brief Used by the REPL to force reload all the plugins and their bound methods + * + * @return true on success + * @return false if one or more plugins couldn't be reloaded + */ + bool forceReloadPlugins(); + friend class Value; friend class internal::Closure; friend class Repl; diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 92827af66..8ca9614fe 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -220,6 +220,35 @@ namespace Ark m_futures.erase(it); } + bool VM::forceReloadPlugins() + { + // load the mapping from the dynamic library + try + { + for (auto& shared_lib : m_shared_lib_objects) + { + mapping* map = shared_lib->template get("getFunctionsMapping")(); + // load the mapping data + std::size_t i = 0; + while (map[i].name != nullptr) + { + // put it in the global frame, aka the first one + auto it = std::find(m_state.m_symbols.begin(), m_state.m_symbols.end(), std::string(map[i].name)); + if (it != m_state.m_symbols.end()) + (m_execution_contexts[0]->locals[0]).push_back(static_cast(std::distance(m_state.m_symbols.begin(), it)), Value(map[i].value)); + + ++i; + } + } + + return true; + } + catch (const std::system_error& e) + { + return false; + } + } + int VM::run() noexcept { init(); diff --git a/src/arkscript/REPL/Repl.cpp b/src/arkscript/REPL/Repl.cpp index 61abb5ab2..d3d81820f 100644 --- a/src/arkscript/REPL/Repl.cpp +++ b/src/arkscript/REPL/Repl.cpp @@ -39,6 +39,8 @@ namespace Ark vm.init(); init = true; } + else + vm.forceReloadPlugins(); if (vm.safeRun(*vm.m_execution_contexts[0]) == 0) { From 91a61901e2fce33cf6cb93e023fcabe8e9f259d3 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sat, 11 May 2024 19:26:23 +0200 Subject: [PATCH 3/6] feat(repl): added help and save (writes history to disk) command to the repl --- CHANGELOG.md | 3 ++- src/arkscript/REPL/Repl.cpp | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e5358198..3e8b2003a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ - added boost-ext/ut to write unit tests in C++ - basic ArkScript code formatter, available through the CLI: `arkscript -f|--format` - comments are now tracked in the AST and attached to the nearest node below them -- `VM::forceReloadPlugins`, to be used by the REPL to force reload the plugins and be sure that their symbols are all defined +- `VM::forceReloadPlugins`, to be used by the REPL to force reload the plugins and be sure that their symbols are all define +- added `help` and `save` commands to the REPL ### Changed - instructions are on 4 bytes: 1 byte for the instruction, 1 byte of padding, 2 bytes for an immediate argument diff --git a/src/arkscript/REPL/Repl.cpp b/src/arkscript/REPL/Repl.cpp index d3d81820f..c248245e3 100644 --- a/src/arkscript/REPL/Repl.cpp +++ b/src/arkscript/REPL/Repl.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -18,7 +19,7 @@ namespace Ark state.setDebug(0); bool init = false; - fmt::print("ArkScript REPL -- Version {} [LICENSE: Mozilla Public License 2.0]\nType \"(quit)\" to quit.\n", ARK_FULL_VERSION); + fmt::print("ArkScript REPL -- Version {} [LICENSE: Mozilla Public License 2.0]\nType \"quit\" to quit. Try \"help\" for more informations\n", ARK_FULL_VERSION); cuiSetup(); std::string code; @@ -95,10 +96,29 @@ namespace Ark trimWhitespace(line); // specific commands handling - if (line == "(quit)" || buf == nullptr) + if (line == "quit" || buf == nullptr) { std::cout << "\nExiting REPL\n"; m_running = false; + + return std::nullopt; + } + else if (line == "help") + { + std::cout << "Available commands:\n"; + std::cout << " help -- display this message\n"; + std::cout << " quit -- quit the REPL\n"; + std::cout << " save -- save the history to disk\n"; + + return std::nullopt; + } + else if (line == "save") + { + std::ofstream history_file("arkscript_repl_history.ark"); + m_repl.history_save(history_file); + + fmt::print("Saved {} lines of history to arkscript_repl_history.ark\n", m_line_count); + return std::nullopt; } From 5d24877b048f304f9103140b8b0c00e2b3483676 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sat, 11 May 2024 19:35:34 +0200 Subject: [PATCH 4/6] feat(repl): show when a block isn't closed --- CHANGELOG.md | 1 + include/CLI/REPL/Repl.hpp | 14 ++++++++++--- src/arkscript/REPL/Repl.cpp | 40 ++++++++++++++++++++++++------------- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e8b2003a..37c8579a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - comments are now tracked in the AST and attached to the nearest node below them - `VM::forceReloadPlugins`, to be used by the REPL to force reload the plugins and be sure that their symbols are all define - added `help` and `save` commands to the REPL +- REPL can now show when a code block isn't terminated (prompt changes from `>` to `:`) ### Changed - instructions are on 4 bytes: 1 byte for the instruction, 1 byte of padding, 2 bytes for an immediate argument diff --git a/include/CLI/REPL/Repl.hpp b/include/CLI/REPL/Repl.hpp index 50765549f..b0cd29e1a 100644 --- a/include/CLI/REPL/Repl.hpp +++ b/include/CLI/REPL/Repl.hpp @@ -12,8 +12,7 @@ #ifndef ARK_REPL_REPL_HPP #define ARK_REPL_REPL_HPP -#include -#include +#include #include #include @@ -46,11 +45,20 @@ namespace Ark int m_old_ip; std::vector m_lib_env; unsigned m_line_count; + std::string m_code; bool m_running; + /** + * @brief Configure replxx + */ void cuiSetup(); - std::optional getLine(); + /** + * @brief Get a line via replxx and handle commands + * @param continuation if the prompt needs to be modified because a code block isn't entirely closed, set to true + * @return + */ + std::optional getLine(bool continuation); std::optional getCodeBlock(); }; } diff --git a/src/arkscript/REPL/Repl.cpp b/src/arkscript/REPL/Repl.cpp index c248245e3..b73f870ee 100644 --- a/src/arkscript/REPL/Repl.cpp +++ b/src/arkscript/REPL/Repl.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -22,7 +24,6 @@ namespace Ark fmt::print("ArkScript REPL -- Version {} [LICENSE: Mozilla Public License 2.0]\nType \"quit\" to quit. Try \"help\" for more informations\n", ARK_FULL_VERSION); cuiSetup(); - std::string code; while (m_running) { auto maybe_block = getCodeBlock(); @@ -31,7 +32,7 @@ namespace Ark m_old_ip = vm.m_execution_contexts[0]->ip; if (maybe_block.has_value() && !maybe_block.value().empty()) { - std::string new_code = code + maybe_block.value(); + std::string new_code = m_code + maybe_block.value(); if (state.doString(new_code)) { // for only one vm init @@ -46,7 +47,7 @@ namespace Ark if (vm.safeRun(*vm.m_execution_contexts[0]) == 0) { // save good code - code = new_code; + m_code = new_code; // place ip to end of bytecode instruction (HALT) vm.m_execution_contexts[0]->ip -= 4; } @@ -74,15 +75,15 @@ namespace Ark m_repl.set_word_break_characters(" \t.,-%!;:=*~^'\"/?<>|[](){}"); m_repl.set_completion_count_cutoff(128); - m_repl.set_double_tab_completion(false); + m_repl.set_double_tab_completion(true); m_repl.set_complete_on_empty(true); m_repl.set_beep_on_ambiguous_completion(false); m_repl.set_no_color(false); } - std::optional Repl::getLine() + std::optional Repl::getLine(bool continuation) { - std::string prompt = fmt::format("main:{:0>3}> ", m_line_count); + std::string prompt = fmt::format("main:{:0>3}{} ", m_line_count, continuation ? ":" : ">"); const char* buf { nullptr }; do @@ -109,6 +110,7 @@ namespace Ark std::cout << " help -- display this message\n"; std::cout << " quit -- quit the REPL\n"; std::cout << " save -- save the history to disk\n"; + std::cout << " code -- print saved code\n"; return std::nullopt; } @@ -121,6 +123,13 @@ namespace Ark return std::nullopt; } + else if (line == "code") + { + std::cout << "\n" + << m_code << "\n"; + + return std::nullopt; + } return line; } @@ -133,20 +142,23 @@ namespace Ark while (m_running) { - auto maybe_line = getLine(); - if (!maybe_line.has_value()) + bool unfinished_block = open_parentheses != 0 || open_braces != 0; + + auto maybe_line = getLine(unfinished_block); + if (!maybe_line.has_value() && !unfinished_block) return std::nullopt; - else if (!maybe_line.value().empty()) + + if (maybe_line.has_value() && !maybe_line.value().empty()) { code_block += maybe_line.value() + "\n"; open_parentheses += countOpenEnclosures(maybe_line.value(), '(', ')'); open_braces += countOpenEnclosures(maybe_line.value(), '{', '}'); - } - // lines number incrementation - ++m_line_count; - if (open_parentheses == 0 && open_braces == 0) - break; + // lines number incrementation + ++m_line_count; + if (open_parentheses == 0 && open_braces == 0) + break; + } } return code_block; From 4b4e84e73d792cc30307f6cbe8a40930f22d731e Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sat, 11 May 2024 19:43:48 +0200 Subject: [PATCH 5/6] feat(repl): adding more controls inside the REPL, closes #278 --- CHANGELOG.md | 1 + src/arkscript/REPL/Repl.cpp | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37c8579a1..4f8cc47a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - `VM::forceReloadPlugins`, to be used by the REPL to force reload the plugins and be sure that their symbols are all define - added `help` and `save` commands to the REPL - REPL can now show when a code block isn't terminated (prompt changes from `>` to `:`) +- more controls available inside the REPL ### Changed - instructions are on 4 bytes: 1 byte for the instruction, 1 byte of padding, 2 bytes for an immediate argument diff --git a/src/arkscript/REPL/Repl.cpp b/src/arkscript/REPL/Repl.cpp index b73f870ee..0d3fc6454 100644 --- a/src/arkscript/REPL/Repl.cpp +++ b/src/arkscript/REPL/Repl.cpp @@ -79,6 +79,24 @@ namespace Ark m_repl.set_complete_on_empty(true); m_repl.set_beep_on_ambiguous_completion(false); m_repl.set_no_color(false); + + m_repl.bind_key_internal(replxx::Replxx::KEY::HOME, "move_cursor_to_begining_of_line"); + m_repl.bind_key_internal(replxx::Replxx::KEY::END, "move_cursor_to_end_of_line"); + m_repl.bind_key_internal(replxx::Replxx::KEY::TAB, "complete_line"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control(replxx::Replxx::KEY::LEFT), "move_cursor_one_word_left"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control(replxx::Replxx::KEY::RIGHT), "move_cursor_one_word_right"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control(replxx::Replxx::KEY::UP), "hint_previous"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control(replxx::Replxx::KEY::DOWN), "hint_next"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control(replxx::Replxx::KEY::ENTER), "commit_line"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control('R'), "history_incremental_search"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control('W'), "kill_to_begining_of_word"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control('U'), "kill_to_begining_of_line"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control('K'), "kill_to_end_of_line"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control('Y'), "yank"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control('L'), "clear_screen"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control('D'), "send_eof"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control('C'), "abort_line"); + m_repl.bind_key_internal(replxx::Replxx::KEY::control('T'), "transpose_characters"); } std::optional Repl::getLine(bool continuation) @@ -110,7 +128,7 @@ namespace Ark std::cout << " help -- display this message\n"; std::cout << " quit -- quit the REPL\n"; std::cout << " save -- save the history to disk\n"; - std::cout << " code -- print saved code\n"; + std::cout << " history -- print saved code\n"; return std::nullopt; } @@ -123,7 +141,7 @@ namespace Ark return std::nullopt; } - else if (line == "code") + else if (line == "history") { std::cout << "\n" << m_code << "\n"; From 5eb985bc4e84d9806e797fb5611ac09675941760 Mon Sep 17 00:00:00 2001 From: Alexandre Plateau Date: Sat, 11 May 2024 19:52:11 +0200 Subject: [PATCH 6/6] feat(repl): added reset command to the repl, closes #278 --- CHANGELOG.md | 2 +- include/CLI/REPL/Repl.hpp | 8 +++++-- src/arkscript/REPL/Repl.cpp | 42 +++++++++++++++++++++---------------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f8cc47a8..ab066c155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - basic ArkScript code formatter, available through the CLI: `arkscript -f|--format` - comments are now tracked in the AST and attached to the nearest node below them - `VM::forceReloadPlugins`, to be used by the REPL to force reload the plugins and be sure that their symbols are all define -- added `help` and `save` commands to the REPL +- added `help`, `save` (save history to disk), `history` (print history), `reset` (reset vm and code) commands to the REPL - REPL can now show when a code block isn't terminated (prompt changes from `>` to `:`) - more controls available inside the REPL diff --git a/include/CLI/REPL/Repl.hpp b/include/CLI/REPL/Repl.hpp index b0cd29e1a..f8d58c969 100644 --- a/include/CLI/REPL/Repl.hpp +++ b/include/CLI/REPL/Repl.hpp @@ -42,12 +42,16 @@ namespace Ark private: replxx::Replxx m_repl; - int m_old_ip; - std::vector m_lib_env; unsigned m_line_count; std::string m_code; bool m_running; + int m_old_ip; + std::vector m_lib_env; + State m_state; + VM m_vm; + bool m_has_init_vm; + /** * @brief Configure replxx */ diff --git a/src/arkscript/REPL/Repl.cpp b/src/arkscript/REPL/Repl.cpp index 0d3fc6454..509bdf857 100644 --- a/src/arkscript/REPL/Repl.cpp +++ b/src/arkscript/REPL/Repl.cpp @@ -11,17 +11,14 @@ namespace Ark using namespace internal; Repl::Repl(const std::vector& lib_env) : - m_old_ip(0), m_lib_env(lib_env), m_line_count(1), m_running(true) + m_line_count(1), m_running(true), + m_old_ip(0), m_lib_env(lib_env), + m_state(m_lib_env), m_vm(m_state), m_has_init_vm(false) {} int Repl::run() { - Ark::State state(m_lib_env); - Ark::VM vm(state); - state.setDebug(0); - bool init = false; - - fmt::print("ArkScript REPL -- Version {} [LICENSE: Mozilla Public License 2.0]\nType \"quit\" to quit. Try \"help\" for more informations\n", ARK_FULL_VERSION); + fmt::print("ArkScript REPL -- Version {} [LICENSE: Mozilla Public License 2.0]\nType \"quit\" to quit. Try \"help\" for more information\n", ARK_FULL_VERSION); cuiSetup(); while (m_running) @@ -29,38 +26,38 @@ namespace Ark auto maybe_block = getCodeBlock(); // save a valid ip if execution failed - m_old_ip = vm.m_execution_contexts[0]->ip; + m_old_ip = m_vm.m_execution_contexts[0]->ip; if (maybe_block.has_value() && !maybe_block.value().empty()) { std::string new_code = m_code + maybe_block.value(); - if (state.doString(new_code)) + if (m_state.doString(new_code)) { // for only one vm init - if (!init) + if (!m_has_init_vm) { - vm.init(); - init = true; + m_vm.init(); + m_has_init_vm = true; } else - vm.forceReloadPlugins(); + m_vm.forceReloadPlugins(); - if (vm.safeRun(*vm.m_execution_contexts[0]) == 0) + if (m_vm.safeRun(*m_vm.m_execution_contexts[0]) == 0) { // save good code m_code = new_code; // place ip to end of bytecode instruction (HALT) - vm.m_execution_contexts[0]->ip -= 4; + m_vm.m_execution_contexts[0]->ip -= 4; } else { // reset ip if execution failed - vm.m_execution_contexts[0]->ip = m_old_ip; + m_vm.m_execution_contexts[0]->ip = m_old_ip; } - state.reset(); + m_state.reset(); } else - std::cout << "Couldn't run code\n"; + std::cout << "\nCouldn't run code\n"; } } @@ -129,6 +126,7 @@ namespace Ark std::cout << " quit -- quit the REPL\n"; std::cout << " save -- save the history to disk\n"; std::cout << " history -- print saved code\n"; + std::cout << " reset -- reset the VM state\n"; return std::nullopt; } @@ -148,6 +146,14 @@ namespace Ark return std::nullopt; } + else if (line == "reset") + { + m_state.reset(); + m_has_init_vm = false; + m_code.clear(); + + return std::nullopt; + } return line; }