ArkScript
A small, fast, functional and scripting language for video games
Repl.cpp
Go to the documentation of this file.
1#include <fstream>
2#include <filesystem>
3#include <fmt/core.h>
4#include <ranges>
5
7
8#include <CLI/REPL/Repl.hpp>
9#include <CLI/REPL/Utils.hpp>
10
11namespace Ark
12{
13 using namespace internal;
14 using namespace replxx;
15
16 Repl::Repl(const std::vector<std::filesystem::path>& lib_env) :
17 m_line_count(1), m_running(true),
18 m_old_ip(0), m_lib_env(lib_env),
19 m_state(m_lib_env), m_vm(m_state), m_has_init_vm(false),
20 m_keywords(getAllKeywords()),
21 m_words_colors(getColorPerKeyword())
22 {}
23
25 {
26 fmt::print("ArkScript REPL -- Version {} [LICENSE: Mozilla Public License 2.0]\nType \"quit\" to quit. Try \"help\" for more information\n", ARK_FULL_VERSION);
27 cuiSetup();
28
29 while (m_running)
30 {
31 std::optional<std::string> maybe_block = getCodeBlock();
32
33 // save a valid ip if execution failed
35 if (maybe_block.has_value() && !maybe_block.value().empty())
36 {
37 std::string new_code = m_code + maybe_block.value();
38 if (m_state.doString(new_code))
39 {
40 // for only one vm init
41 if (!m_has_init_vm)
42 {
43 m_vm.init();
44 m_has_init_vm = true;
45 }
46 else
47 std::ignore = m_vm.forceReloadPlugins();
48
50 {
51 // save good code
52 m_code = new_code;
53 // place ip to end of bytecode instruction (HALT)
54 m_vm.m_execution_contexts[0]->ip -= 4;
55 }
56 else
57 {
58 // reset ip if execution failed
60 }
61
62 m_state.reset();
63 }
64 else
65 fmt::println("\nCouldn't run code");
66 }
67 }
68
69 return 0;
70 }
71
73 {
74 m_repl.set_completion_callback([this](const std::string& ctx, int& len) {
75 return hookCompletion(m_keywords, ctx, len);
76 });
77 m_repl.set_highlighter_callback([this](const std::string& ctx, Replxx::colors_t& colors) {
78 return hookColor(m_words_colors, ctx, colors);
79 });
80 m_repl.set_hint_callback([this](const std::string& ctx, int& len, Replxx::Color& color) {
81 return hookHint(m_keywords, ctx, len, color);
82 });
83
84 m_repl.set_word_break_characters(" \t.,-%!;:=*~^'\"/?<>|[](){}");
85 m_repl.set_completion_count_cutoff(128);
86 m_repl.set_double_tab_completion(true);
87 m_repl.set_complete_on_empty(true);
88 m_repl.set_beep_on_ambiguous_completion(false);
89 m_repl.set_no_color(false);
90
91 m_repl.bind_key_internal(Replxx::KEY::HOME, "move_cursor_to_begining_of_line");
92 m_repl.bind_key_internal(Replxx::KEY::END, "move_cursor_to_end_of_line");
93 m_repl.bind_key_internal(Replxx::KEY::TAB, "complete_line");
94 m_repl.bind_key_internal(Replxx::KEY::control(Replxx::KEY::LEFT), "move_cursor_one_word_left");
95 m_repl.bind_key_internal(Replxx::KEY::control(Replxx::KEY::RIGHT), "move_cursor_one_word_right");
96 m_repl.bind_key_internal(Replxx::KEY::control(Replxx::KEY::UP), "hint_previous");
97 m_repl.bind_key_internal(Replxx::KEY::control(Replxx::KEY::DOWN), "hint_next");
98 m_repl.bind_key_internal(Replxx::KEY::control(Replxx::KEY::ENTER), "commit_line");
99 m_repl.bind_key_internal(Replxx::KEY::control('R'), "history_incremental_search");
100 m_repl.bind_key_internal(Replxx::KEY::control('W'), "kill_to_begining_of_word");
101 m_repl.bind_key_internal(Replxx::KEY::control('U'), "kill_to_begining_of_line");
102 m_repl.bind_key_internal(Replxx::KEY::control('K'), "kill_to_end_of_line");
103 m_repl.bind_key_internal(Replxx::KEY::control('Y'), "yank");
104 m_repl.bind_key_internal(Replxx::KEY::control('L'), "clear_screen");
105 m_repl.bind_key_internal(Replxx::KEY::control('D'), "send_eof");
106 m_repl.bind_key_internal(Replxx::KEY::control('C'), "abort_line");
107 m_repl.bind_key_internal(Replxx::KEY::control('T'), "transpose_characters");
108 }
109
110 std::optional<std::string> Repl::getLine(const bool continuation)
111 {
112 const std::string prompt = fmt::format("main:{:0>3}{} ", m_line_count, continuation ? ":" : ">");
113
114 const char* buf { nullptr };
115 do
116 {
117 buf = m_repl.input(prompt);
118 } while ((buf == nullptr) && (errno == EAGAIN));
119 std::string line = (buf != nullptr) ? std::string(buf) : "";
120
121 // line history
122 m_repl.history_add(line);
123 trimWhitespace(line);
124
125 // specific commands handling
126 if (line == "quit" || buf == nullptr)
127 {
128 fmt::println("\nExiting REPL");
129 m_running = false;
130
131 return std::nullopt;
132 }
133 if (line == "help")
134 {
135 fmt::println("Available commands:");
136 fmt::println(" help -- display this message");
137 fmt::println(" quit -- quit the REPL");
138 fmt::println(" save -- save the history to disk");
139 fmt::println(" history -- print saved code");
140 fmt::println(" reset -- reset the VM state");
141
142 return std::nullopt;
143 }
144 if (line == "save")
145 {
146 std::ofstream history_file("arkscript_repl_history.ark");
147 m_repl.history_save(history_file);
148
149 fmt::println("Saved {} lines of history to arkscript_repl_history.ark", m_line_count);
150 return std::nullopt;
151 }
152 if (line == "history")
153 {
154 fmt::println("\n{}", m_code);
155 return std::nullopt;
156 }
157 if (line == "reset")
158 {
159 m_state.reset();
160 m_has_init_vm = false;
161 m_code.clear();
162
163 return std::nullopt;
164 }
165
166 return line;
167 }
168
169 std::optional<std::string> Repl::getCodeBlock()
170 {
171 std::string code_block;
172 long open_parentheses = 0;
173 long open_braces = 0;
174
175 while (m_running)
176 {
177 const bool unfinished_block = open_parentheses != 0 || open_braces != 0;
178
179 auto maybe_line = getLine(unfinished_block);
180 if (!maybe_line.has_value() && !unfinished_block)
181 return std::nullopt;
182
183 if (maybe_line.has_value() && !maybe_line.value().empty())
184 {
185 code_block += maybe_line.value() + "\n";
186 open_parentheses += countOpenEnclosures(maybe_line.value(), '(', ')');
187 open_braces += countOpenEnclosures(maybe_line.value(), '{', '}');
188
189 // lines number incrementation
190 ++m_line_count;
191 if (open_parentheses == 0 && open_braces == 0)
192 break;
193 }
194 }
195
196 return code_block;
197 }
198}
Host the declaration of all the ArkScript builtins.
replxx utilities
constexpr std::string_view ARK_FULL_VERSION
Definition Constants.hpp:22
ArkScript REPL - Read Eval Print Loop.
bool m_has_init_vm
Definition Repl.hpp:50
std::vector< std::pair< std::string, replxx::Replxx::Color > > m_words_colors
Definition Repl.hpp:52
State m_state
Definition Repl.hpp:48
std::vector< std::string > m_keywords
Definition Repl.hpp:51
int run()
Start the REPL.
Definition Repl.cpp:24
std::string m_code
Definition Repl.hpp:43
void cuiSetup()
Configure replxx.
Definition Repl.cpp:72
bool m_running
Definition Repl.hpp:44
replxx::Replxx m_repl
Definition Repl.hpp:41
std::optional< std::string > getCodeBlock()
Prompt the user to enter a complete code block and handle the prompt modifications until the code blo...
Definition Repl.cpp:169
std::size_t m_old_ip
Definition Repl.hpp:46
Repl(const std::vector< std::filesystem::path > &lib_env)
Construct a new Repl object.
Definition Repl.cpp:16
VM m_vm
Definition Repl.hpp:49
unsigned m_line_count
Definition Repl.hpp:42
std::optional< std::string > getLine(bool continuation)
Get a line via replxx and handle commands.
Definition Repl.cpp:110
void reset() noexcept
Reset State (all member variables related to execution)
Definition State.cpp:181
bool doString(const std::string &code, uint16_t features=DefaultFeatures)
Compile a string (representing ArkScript code) and store resulting bytecode in m_bytecode.
Definition State.cpp:103
std::vector< std::unique_ptr< internal::ExecutionContext > > m_execution_contexts
Definition VM.hpp:157
bool forceReloadPlugins() const
Used by the REPL to force reload all the plugins and their bound methods.
Definition VM.cpp:292
int safeRun(internal::ExecutionContext &context, std::size_t untilFrameCount=0, bool fail_with_exception=false)
Run ArkScript bytecode inside a try catch to retrieve all the exceptions and display a stack trace if...
Definition VM.cpp:330
void init() noexcept
Initialize the VM according to the parameters.
Definition VM.cpp:86
std::vector< std::string > getAllKeywords()
Compute a list of all the language keywords and builtins.
long countOpenEnclosures(const std::string &line, char open, char close)
Count the open enclosure and its counterpart: (), {}, [].
void hookColor(const std::vector< std::pair< std::string, replxx::Replxx::Color > > &words_colors, const std::string &context, replxx::Replxx::colors_t &colors)
constexpr std::array colors
Definition Logger.cpp:8
replxx::Replxx::completions_t hookCompletion(const std::vector< std::string > &words, const std::string &context, int &length)
std::vector< std::pair< std::string, replxx::Replxx::Color > > getColorPerKeyword()
Compute a list of pairs (word -> color) to be used for coloration by the REPL.
replxx::Replxx::hints_t hookHint(const std::vector< std::string > &words, const std::string &context, int &length, replxx::Replxx::Color &color)
void trimWhitespace(std::string &line)
Remove whitespaces at the start and end of a string.