ArkScript
A small, lisp-inspired, functional scripting language
Utils.cpp
Go to the documentation of this file.
1#include <CLI/REPL/Utils.hpp>
2
3#include <regex>
4#include <algorithm>
5#include <numeric>
6#include <ranges>
7
10
11namespace Ark::internal
12{
13 std::vector<std::string> getAllKeywords()
14 {
15 std::vector<std::string> output;
16 output.reserve(keywords.size() + Language::listInstructions.size() + Language::operators.size() + Builtins::builtins.size() + 2);
17
18 std::ranges::transform(keywords, std::back_inserter(output), [](const auto& string_view) {
19 return std::string(string_view);
20 });
21 std::ranges::transform(Language::listInstructions, std::back_inserter(output), [](const auto& string_view) {
22 return std::string(string_view);
23 });
24 std::ranges::transform(Language::operators, std::back_inserter(output), [](const auto& string_view) {
25 return std::string(string_view);
26 });
27 std::ranges::transform(
28 std::ranges::views::keys(Builtins::builtins) | std::ranges::views::filter([&output](const auto& val) -> bool {
29 return std::ranges::find(output, val) == output.end();
30 }),
31 std::back_inserter(output), [](const auto& string) {
32 return string;
33 });
34
35 output.emplace_back(Language::And);
36 output.emplace_back(Language::Or);
37 output.emplace_back(Language::Apply);
38
39 return output;
40 }
41
42 std::vector<std::pair<std::string, replxx::Replxx::Color>> getColorPerKeyword()
43 {
44 using namespace replxx;
45 using K = std::string;
46 using V = Replxx::Color;
47
48 std::vector<std::pair<K, V>> output;
49 output.reserve(keywords.size() + Language::listInstructions.size() + Language::operators.size() + Builtins::builtins.size() + 4);
50
51 std::ranges::transform(keywords, std::back_inserter(output), [](const auto& string_view) {
52 return std::make_pair(std::string(string_view), Replxx::Color::BRIGHTRED);
53 });
54 std::ranges::transform(Language::listInstructions, std::back_inserter(output), [](const auto& string_view) {
55 return std::make_pair(std::string(string_view), Replxx::Color::GREEN);
56 });
57 std::ranges::transform(Language::operators, std::back_inserter(output), [](const auto& string_view) {
58 auto safe_op = std::string(string_view);
59 if (const auto it = safe_op.find_first_of(R"(-+=/*<>[]()?")"); it != std::string::npos)
60 safe_op.insert(it, "\\");
61 return std::make_pair(safe_op, Replxx::Color::BRIGHTBLUE);
62 });
63 std::ranges::transform(
64 std::ranges::views::keys(Builtins::builtins) | std::ranges::views::filter([&output](const auto& val) -> bool {
65 return std::ranges::find_if(output, [&val](const std::pair<K, V>& pair) -> bool {
66 return pair.first == val;
67 }) == output.end();
68 }),
69 std::back_inserter(output), [](const auto& string) {
70 auto safe_op = string;
71 if (const auto it = safe_op.find_first_of(R"(-+=/*<>[]()?")"); it != std::string::npos)
72 safe_op.insert(it, "\\");
73 return std::make_pair(safe_op, Replxx::Color::GREEN);
74 });
75
76 output.emplace_back(Language::And, Replxx::Color::BRIGHTBLUE);
77 output.emplace_back(Language::Or, Replxx::Color::BRIGHTBLUE);
78 output.emplace_back(Language::Apply, Replxx::Color::BRIGHTBLUE);
79 output.emplace_back("[\\-|+]?[0-9]+(\\.[0-9]+)?", Replxx::Color::YELLOW);
80 output.emplace_back("\".*\"", Replxx::Color::MAGENTA);
81
82 return output;
83 }
84
85 std::size_t codepointLength(const std::string& str)
86 {
87 return std::accumulate(
88 str.begin(),
89 str.end(),
90 std::size_t { 0 },
91 [](const std::size_t acc, const char c) {
92 return acc + ((c & 0xc0) != 0x80);
93 });
94 }
95
96 std::size_t contextLen(const std::string& prefix)
97 {
98 const std::string word_break = " \t\n\r\v\f=+*&^%$#@!,./?<>;`~'\"[]{}()\\|";
99 std::size_t count = 0;
100
101 for (const auto c : std::ranges::views::reverse(prefix))
102 {
103 if (word_break.find(c) != std::string::npos)
104 break;
105 ++count;
106 }
107
108 return count;
109 }
110
111 replxx::Replxx::completions_t hookCompletion(const std::vector<std::string>& words, const std::string& context, int& length)
112 {
113 replxx::Replxx::completions_t completions;
114 std::size_t utf8_context_len = contextLen(context);
115 std::size_t prefix_len = context.size() - utf8_context_len;
116
117 if (prefix_len > 0 && context[prefix_len - 1] == '\\')
118 {
119 --prefix_len;
120 ++utf8_context_len;
121 }
122
123 length = static_cast<int>(codepointLength(context.substr(prefix_len, utf8_context_len)));
124
125 const std::string prefix = context.substr(prefix_len);
126 for (const auto& e : words)
127 {
128 if (e.starts_with(prefix))
129 completions.emplace_back(e.c_str());
130 }
131
132 return completions;
133 }
134
135 void hookColor(const std::vector<std::pair<std::string, replxx::Replxx::Color>>& words_colors, const std::string& context, replxx::Replxx::colors_t& colors)
136 {
137 // highlight matching regex sequences
138 for (const auto& [regex, color] : words_colors)
139 {
140 std::size_t pos = 0;
141 std::string str = context;
142 std::smatch match;
143
144 while (std::regex_search(str, match, std::regex(regex)))
145 {
146 std::string c = match[0];
147 std::string prefix = match.prefix().str();
148 const std::size_t len = codepointLength(c);
149
150 pos += codepointLength(prefix);
151 for (std::size_t i = 0; i < len; ++i)
152 {
153 if (colors.at(pos + i) == replxx::Replxx::Color::DEFAULT)
154 colors.at(pos + i) = color;
155 }
156
157 pos += len;
158 str = match.suffix();
159 }
160 }
161 }
162
163 replxx::Replxx::hints_t hookHint(const std::vector<std::string>& words, const std::string& context, int& length, replxx::Replxx::Color& color)
164 {
165 replxx::Replxx::hints_t hints;
166 // only show hint if prefix is at least 'n' chars long
167 // or if prefix begins with a specific character
168 const std::size_t utf8_context_len = contextLen(context);
169 const std::size_t prefix_len = context.size() - utf8_context_len;
170 length = static_cast<int>(codepointLength(context.substr(prefix_len, utf8_context_len)));
171 const std::string prefix = context.substr(prefix_len);
172
173 if (prefix.size() >= 2 || (!prefix.empty() && prefix.at(0) == '.'))
174 {
175 for (const auto& e : words)
176 {
177 if (e.compare(0, prefix.size(), prefix) == 0)
178 hints.emplace_back(e.c_str());
179 }
180 }
181
182 if (hints.size() == 1)
183 color = replxx::Replxx::Color::GREEN;
184
185 return hints;
186 }
187}
Host the declaration of all the ArkScript builtins.
replxx utilities
Common code for the compiler.
ARK_API const std::vector< std::pair< std::string, Value > > builtins
constexpr std::array< std::string_view, 9 > listInstructions
Definition Common.hpp:119
constexpr std::string_view Apply
Definition Common.hpp:137
constexpr std::array< std::string_view, 24 > operators
Definition Common.hpp:158
constexpr std::string_view And
Definition Common.hpp:134
constexpr std::string_view Or
Definition Common.hpp:135
std::vector< std::string > getAllKeywords()
Compute a list of all the language keywords and builtins.
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)
constexpr std::array< std::string_view, 9 > keywords
List of available keywords in ArkScript.
Definition Common.hpp:92