Skip to content

Commit

Permalink
feat(repl): repl completion and colors are generated automatically
Browse files Browse the repository at this point in the history
  • Loading branch information
SuperFola committed Aug 24, 2024
1 parent 7192005 commit 2f0e181
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 180 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
- added a max macro evaluation depth
- introduced `internal::listInstructions` with the different instructions, to be used by the compiler and name resolution pass
- checking for forbidden variable/constant name in the name & scope resolution pass, to give errors to the user before compiling some weird code
- repl completion and colors are now generated automatically from the builtins, keywords & operators

### Removed
- removed unused `NodeType::Closure`
Expand Down
2 changes: 2 additions & 0 deletions include/CLI/REPL/Repl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ namespace Ark
State m_state;
VM m_vm;
bool m_has_init_vm;
std::vector<std::string> m_keywords;
std::vector<std::pair<std::string, replxx::Replxx::Color>> m_words_colors;

/**
* @brief Configure replxx
Expand Down
157 changes: 4 additions & 153 deletions include/CLI/REPL/Utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @file Utils.hpp
* @author Alexandre Plateau ([email protected])
* @brief replxx utilities
* @version 1.0
* @version 1.1
* @date 2020-10-27
*
* @copyright Copyright (c) 2020-2024
Expand All @@ -19,155 +19,6 @@

namespace Ark::internal
{
const std::vector<std::string> 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<std::pair<std::string, replxx::Replxx::Color>> 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
Expand All @@ -183,11 +34,11 @@ namespace Ark::internal
*/
void trimWhitespace(std::string& line);

replxx::Replxx::completions_t hookCompletion(const std::string& context, int& length);
replxx::Replxx::completions_t hookCompletion(const std::vector<std::string>& words, const std::string& context, int& length);

void hookColor(const std::string& context, replxx::Replxx::colors_t& colors);
void hookColor(const std::vector<std::pair<std::string, replxx::Replxx::Color>>& words_colors, const std::string& context, replxx::Replxx::colors_t& colors);

replxx::Replxx::hints_t hookHint(const std::string& context, int& length, replxx::Replxx::Color& color);
replxx::Replxx::hints_t hookHint(const std::vector<std::string>& words, const std::string& context, int& length, replxx::Replxx::Color& color);
}

#endif
81 changes: 60 additions & 21 deletions src/arkscript/REPL/Repl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,52 @@
#include <iostream>
#include <filesystem>
#include <fmt/core.h>
#include <ranges>

#include <Ark/Builtins/Builtins.hpp>
#include <Ark/Compiler/Common.hpp>

#include <CLI/REPL/Repl.hpp>
#include <CLI/REPL/Utils.hpp>

namespace Ark
{
using namespace internal;
using namespace replxx;

Repl::Repl(const std::vector<std::filesystem::path>& lib_env) :
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)
{}
{
m_keywords.reserve(keywords.size() + listInstructions.size() + operators.size() + Builtins::builtins.size());
for (auto keyword : keywords)
m_keywords.emplace_back(keyword);
for (auto inst : listInstructions)
m_keywords.emplace_back(inst);
for (auto op : operators)
m_keywords.emplace_back(op);
for (const auto& builtin : std::ranges::views::keys(Builtins::builtins))
m_keywords.push_back(builtin);

m_words_colors.reserve(keywords.size() + listInstructions.size() + operators.size() + Builtins::builtins.size() + 2);
for (auto keyword : keywords)
m_words_colors.emplace_back(keyword, Replxx::Color::BRIGHTRED);
for (auto inst : listInstructions)
m_words_colors.emplace_back(inst, Replxx::Color::GREEN);
for (auto op : operators)
{
auto safe_op = std::string(op);
if (const auto it = safe_op.find_first_of(R"(-+=/*<>[]()?")"); it != std::string::npos)
safe_op.insert(it, "\\");
m_words_colors.emplace_back(safe_op, Replxx::Color::BRIGHTBLUE);
}
for (const auto& builtin : std::ranges::views::keys(Builtins::builtins))
m_words_colors.emplace_back(builtin, Replxx::Color::GREEN);

m_words_colors.emplace_back("[\\-|+]?[0-9]+(\\.[0-9]+)?", Replxx::Color::YELLOW);
m_words_colors.emplace_back("\".*\"", Replxx::Color::MAGENTA);
}

int Repl::run()
{
Expand Down Expand Up @@ -66,9 +99,15 @@ namespace Ark

void Repl::cuiSetup()
{
m_repl.set_completion_callback(hookCompletion);
m_repl.set_highlighter_callback(hookColor);
m_repl.set_hint_callback(hookHint);
m_repl.set_completion_callback([this](const std::string& ctx, int& len) {
return hookCompletion(m_keywords, ctx, len);
});
m_repl.set_highlighter_callback([this](const std::string& ctx, Replxx::colors_t& colors) {
return hookColor(m_words_colors, ctx, colors);
});
m_repl.set_hint_callback([this](const std::string& ctx, int& len, Replxx::Color& color) {
return hookHint(m_keywords, ctx, len, color);
});

m_repl.set_word_break_characters(" \t.,-%!;:=*~^'\"/?<>|[](){}");
m_repl.set_completion_count_cutoff(128);
Expand All @@ -77,23 +116,23 @@ namespace Ark
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");
m_repl.bind_key_internal(Replxx::KEY::HOME, "move_cursor_to_begining_of_line");
m_repl.bind_key_internal(Replxx::KEY::END, "move_cursor_to_end_of_line");
m_repl.bind_key_internal(Replxx::KEY::TAB, "complete_line");
m_repl.bind_key_internal(Replxx::KEY::control(Replxx::KEY::LEFT), "move_cursor_one_word_left");
m_repl.bind_key_internal(Replxx::KEY::control(Replxx::KEY::RIGHT), "move_cursor_one_word_right");
m_repl.bind_key_internal(Replxx::KEY::control(Replxx::KEY::UP), "hint_previous");
m_repl.bind_key_internal(Replxx::KEY::control(Replxx::KEY::DOWN), "hint_next");
m_repl.bind_key_internal(Replxx::KEY::control(Replxx::KEY::ENTER), "commit_line");
m_repl.bind_key_internal(Replxx::KEY::control('R'), "history_incremental_search");
m_repl.bind_key_internal(Replxx::KEY::control('W'), "kill_to_begining_of_word");
m_repl.bind_key_internal(Replxx::KEY::control('U'), "kill_to_begining_of_line");
m_repl.bind_key_internal(Replxx::KEY::control('K'), "kill_to_end_of_line");
m_repl.bind_key_internal(Replxx::KEY::control('Y'), "yank");
m_repl.bind_key_internal(Replxx::KEY::control('L'), "clear_screen");
m_repl.bind_key_internal(Replxx::KEY::control('D'), "send_eof");
m_repl.bind_key_internal(Replxx::KEY::control('C'), "abort_line");
m_repl.bind_key_internal(Replxx::KEY::control('T'), "transpose_characters");
}

std::optional<std::string> Repl::getLine(const bool continuation)
Expand Down
12 changes: 6 additions & 6 deletions src/arkscript/REPL/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ namespace Ark::internal
return count;
}

replxx::Replxx::completions_t hookCompletion(const std::string& context, int& length)
replxx::Replxx::completions_t hookCompletion(const std::vector<std::string>& words, const std::string& context, int& length)
{
replxx::Replxx::completions_t completions;
std::size_t utf8_context_len = contextLen(context);
Expand All @@ -61,7 +61,7 @@ namespace Ark::internal
length = static_cast<int>(codepointLength(context.substr(prefix_len, utf8_context_len)));

const std::string prefix = context.substr(prefix_len);
for (const auto& e : KeywordsDict)
for (const auto& e : words)
{
if (e.starts_with(prefix) == 0)
completions.emplace_back(e.c_str());
Expand All @@ -70,10 +70,10 @@ namespace Ark::internal
return completions;
}

void hookColor(const std::string& context, replxx::Replxx::colors_t& colors)
void hookColor(const std::vector<std::pair<std::string, replxx::Replxx::Color>>& words_colors, const std::string& context, replxx::Replxx::colors_t& colors)
{
// highlight matching regex sequences
for (const auto& [regex, color] : ColorsRegexDict)
for (const auto& [regex, color] : words_colors)
{
std::size_t pos = 0;
std::string str = context;
Expand All @@ -95,7 +95,7 @@ namespace Ark::internal
}
}

replxx::Replxx::hints_t hookHint(const std::string& context, int& length, replxx::Replxx::Color& color)
replxx::Replxx::hints_t hookHint(const std::vector<std::string>& words, 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
Expand All @@ -107,7 +107,7 @@ namespace Ark::internal

if (prefix.size() >= 2 || (!prefix.empty() && prefix.at(0) == '.'))
{
for (const auto& e : KeywordsDict)
for (const auto& e : words)
{
if (e.compare(0, prefix.size(), prefix) == 0)
hints.emplace_back(e.c_str());
Expand Down

0 comments on commit 2f0e181

Please sign in to comment.