header_utils
Loading...
Searching...
No Matches
eval.h
1
4
5#pragma once
6
7#include "json_helpers.h"
8#include "string_ops.h"
9#include <format>
10#include <variant>
11#include <span>
12
14{
15 using json = nlohmann::json;
16 static inline const json null_json;
17 struct value
18 {
19 std::variant<json, json*, json const*> v;
20
21 value() noexcept = default;
22 value(value const&) noexcept = default;
23 value(value&&) noexcept = default;
24 value& operator=(value const&) noexcept = default;
25 value& operator=(value&&) noexcept = default;
26
27 template <typename... ARGS>
28 requires std::constructible_from<json, ARGS...>
29 explicit(false) value(ARGS&&... args) noexcept : v(json(std::forward<ARGS>(args)...)) {}
30
31 explicit(false) value(json&& j) noexcept : v(std::move(j)) {}
32 explicit(false) value(json const* j) noexcept : v(std::move(j)) {}
33 explicit(false) value(json* j) noexcept : v(std::move(j)) {}
34
35 bool is_lval() const noexcept { return v.index() == 1; }
36 bool is_rval() const noexcept { return v.index() == 0; }
37 bool is_ref() const noexcept { return v.index() == 2; }
38
39 json& lval() { return *std::get<json*>(v); }
40
41 void ref() && = delete;
42
43 json const& ref() const&
44 {
45 switch (v.index())
46 {
47 case 0: return std::get<json>(v);
48 case 1: return *std::get<json*>(v);
49 case 2: return *std::get<json const*>(v);
50 }
51 return null_json;
52 }
53
54 json const& forward() const&
55 {
56 return ref();
57 }
58
59 json forward() &&
60 {
61 switch (v.index())
62 {
63 case 0: return std::move(std::get<json>(v));
64 case 1: return *std::get<json*>(v);
65 case 2: return *std::get<json const*>(v);
66 }
67 return null_json;
68 }
69
70 explicit(false) operator json const& () const&
71 {
72 return ref();
73 }
74
75 json const& operator*() const&
76 {
77 return ref();
78 }
79
80 explicit(false) operator json () &&
81 {
82 return static_cast<value&&>(*this).forward();
83 }
84
85 json operator*() &&
86 {
87 return static_cast<value&&>(*this).forward();
88 }
89
90 json const* operator->() const& noexcept { return &ref(); }
91 };
92
94 template <bool DECADE_SYNTAX = false>
96 {
98 static constexpr bool decade_syntax = DECADE_SYNTAX;
99 static constexpr bool sexps_syntax = !DECADE_SYNTAX;
100
101 using eval_func = std::function<value(self_type&, std::vector<value>)>;
102
103 self_type* parent_env = nullptr;
104 std::map<std::string, eval_func, std::less<>> funcs;
105 eval_func unknown_func_eval;
106 std::function<value(self_type&, std::string_view)> unknown_var_eval;
107 std::function<void(std::string_view)> error_handler;
108 std::map<std::string, json, std::less<>> user_storage;
109 void* user_data = nullptr;
110 std::map<std::string, eval_func, std::less<>> prefix_macros;
111 std::function<bool(json const&)> truthiness_function;
112
113 self_type* get_root_env() const noexcept { return parent_env ? parent_env->get_root_env() : this; }
114
115 auto find_in_user_storage(this auto&& self, std::string_view name)
116 {
117 if (auto it = self.user_storage.find(name); it != self.user_storage.end())
118 return std::pair{ &self, it };
119 else
120 return self.parent_env ? self.parent_env->find_in_user_storage(name) : std::pair<decltype(&self), decltype(it)>{};
121 }
122
123 value user_var(std::string_view name)
124 {
125 auto var = find_in_user_storage(name);
126 if (var.first)
127 return &var.second->second;
128 return unknown_var_eval ? unknown_var_eval(*this, name) : value(null_json);
129 }
130
131 json& set_user_var(std::string_view name, value val, bool force_local = false)
132 {
133 auto* storage = &user_storage;
134 if (!force_local)
135 {
136 auto [owning_store, it] = this->find_in_user_storage(name);
137 if (owning_store)
138 storage = &owning_store->user_storage;
139 }
140 auto it = storage->find(name);
141 if (it == storage->end())
142 return storage->emplace(name, val.forward()).first->second;
143 return it->second = val.forward();
144 }
145
146 eval_func const* find_unknown_func_eval() const noexcept
147 {
148 if (unknown_func_eval)
149 return &unknown_func_eval;
150 if (parent_env)
151 return parent_env->find_unknown_func_eval();
152 return nullptr;
153 }
154
155 eval_func const* find_func(std::string_view name) const
156 {
157 if (auto it = funcs.find(name); it != funcs.end())
158 return &it->second;
159 if (parent_env)
160 return parent_env->find_func(name);
161 return find_unknown_func_eval();
162 }
163
164 template <typename... ARGS>
165 json report_error(std::string_view fmt, ARGS&&... args)
166 {
167 auto errstr = std::vformat(fmt, std::make_format_args(std::forward<ARGS>(args)...));
168 if (error_handler)
169 {
170 error_handler(errstr);
171 return null_json;
172 }
173 throw std::runtime_error(std::move(errstr));
174 }
175
176 struct e_scope_terminator {
177 value result = json(json::value_t::discarded);
178 virtual std::string_view type() const noexcept = 0;
179 virtual ~e_scope_terminator() noexcept = default;
180 };
181
182 value eval_call(std::vector<value> args)
183 {
184 if (args.empty())
185 return null_json;
186
187 //const auto orig_args = args;
188
189 std::string funcname;
190 std::vector<value> arguments;
191 if constexpr (decade_syntax)
192 {
193 const auto args_count = args.size();
194 const bool infix = (args_count % 2) == 1;
195
196 if (args_count == 1)
197 {
198 funcname = *args[0];
199 arguments = std::move(args);
200 }
201 else
202 {
203 arguments.push_back({});
204 if (infix)
205 {
206 arguments.push_back(std::move(args[0]));
207 funcname += ':';
208 }
209
211 std::string last_function_identifier;
212 bool argument_variadic = false;
213 for (size_t i = infix; i < args_count; i += 2)
214 {
215 auto& function_identifier = args[i];
216 if (function_identifier->is_string() && !std::string_view{*function_identifier}.empty())
217 {
218 if (last_function_identifier == std::string_view{ *function_identifier })
219 {
221 {
222 funcname.back() = '*';
223 funcname += ':';
224 argument_variadic = true;
225 }
226 }
227 else
228 {
229 argument_variadic = false;
231 funcname += ':';
233 }
234 arguments.push_back(std::move(args[i + 1]));
235 }
236 else
237 return report_error("expected function name part, got: {}", function_identifier->dump());
238 }
239
240 arguments[0] = funcname;
241 }
242 }
243 else
244 {
245 auto& func = *args[0];
246 if (func.is_string())
247 funcname = func.get_ref<json::string_t const&>();
248 else if (func.is_array())
249 {
250 auto eres = eval_args(func.get_ref<json::array_t const&>());
251 if (eres.is_string())
253 }
254 if (funcname.empty())
255 return report_error("first element of eval array must eval to a string func name, got: {}", func.dump());
256 }
257
258 if (eval_func const* func = find_func(funcname))
259 return (*func)(*this, std::move(arguments));
260 return report_error("func with name '{}' not found", funcname);
261 }
262
263 template <typename V>
264 value eval(V&& val)
265 {
266 if constexpr (std::same_as<V, value const&>)
267 {
268 return eval(value{ val });
269 }
270 else if constexpr (std::same_as<std::remove_cvref_t<V>, json>)
271 {
272 return eval(value(std::forward<V>(val)));
273 }
274 else if constexpr (std::same_as<std::remove_cvref_t<V>, value>)
275 {
276 if (val->is_string())
277 {
278 if (auto str = std::string_view{ *val }; !str.empty())
279 {
280 for (auto& [prefix, macro] : prefix_macros)
281 {
282 if (str.starts_with(prefix))
283 return eval(macro(*this, { std::move(val) }));
284 }
285 }
286 }
287
288 if (!val->is_array())
289 return std::move(val);
290
291 std::vector<value> args;
292 switch (val.v.index())
293 {
294 case 0:
295 {
296 json::array_t arr = std::move(std::get<json>(val.v).template get_ref<json::array_t&>());
297 for (auto& a : arr)
298 args.push_back(std::move(a));
299 break;
300 }
301 case 1:
302 {
303 json::array_t& arr = std::get<json*>(val.v)->template get_ref<json::array_t&>();
304 for (auto& a : arr)
305 args.push_back(&a);
306 break;
307 }
308 case 2:
309 {
310 json::array_t const& arr = std::get<json const*>(val.v)->template get_ref<json::array_t const&>();
311 for (auto const& a : arr)
312 args.push_back(&a);
313 break;
314 }
315 }
316
317 return eval_call(std::move(args));
318 }
319 }
320
321 template <typename T>
322 json safe_eval(T&& value)
323 {
324 try
325 {
326 auto result = eval(value);
327 return result.forward();
328 }
329 catch (e_scope_terminator const& e)
330 {
331 return report_error("'{}' not in loop", e.type());
332 }
333 }
334
335 inline bool is_true(json const& val)
336 {
337 switch (val.type())
338 {
339 case json::value_t::boolean: return bool(val);
340 case json::value_t::null: return false;
341 default: return truthiness_function ? truthiness_function(val) : true;
342 }
343 }
344
345 inline bool is_true(value const& val)
346 {
347 return is_true(*val);
348 }
349
350 static void assert_args(std::span<value const> args, size_t arg_count)
351 {
352 if (args.size() != arg_count + 1)
353 throw std::runtime_error(std::format("function {} requires exactly {} arguments, {} given", args[0]->dump(), arg_count, args.size() - 1));
354 }
355
356 static void assert_args(std::span<value const> args, size_t min_args, size_t max_args)
357 {
358 if (args.size() < min_args + 1 && args.size() >= max_args + 1)
359 throw std::runtime_error(std::format("function {} requires between {} and {} arguments, {} given", args[0]->dump(), min_args, max_args, args.size() - 1));
360 }
361
362 static void assert_min_args(std::span<value const> args, size_t arg_count)
363 {
364 if (args.size() < arg_count + 1)
365 throw std::runtime_error(std::format("function {} requires at least {} arguments, {} given", args[0]->dump(), arg_count, args.size() - 1));
366 }
367
368 static auto assert_arg(std::span<value const> args, size_t arg_num, json::value_t type = json::value_t::discarded)
369 {
370 if (arg_num >= args.size())
371 throw std::runtime_error(std::format("function {} requires {} arguments, {} given", args[0]->dump(), arg_num, args.size() - 1));
372
373 if (type != json::value_t::discarded && args[arg_num]->type() != type)
374 {
375 throw std::runtime_error(std::format("argument #{} to function {} must be of type {}, {} given",
376 arg_num, args[0]->dump(), formats::json::type_name(type), formats::json::type_name(args[arg_num]->type())));
377 }
378
379 return args[arg_num]->type();
380 }
381
382 template <std::same_as<nlohmann::json::value_t>... T>
383 static void assert_args(std::vector<value> args, T... arg_types)
384 {
385 static constexpr size_t arg_count = sizeof...(T);
386 assert_args(args, arg_count);
387
388 const auto types = std::array{ arg_types... };
389 for (size_t i = 0; i < types.size(); ++i)
390 {
391 if (types[i] != json::value_t::discarded)
392 assert_arg(args, i + 1, types[i]);
393 }
394 }
395
396 value eval_arg(std::vector<value>& args, size_t n, json::value_t type = json::value_t::discarded)
397 {
398 assert_arg(args, n, type);
399 return eval(std::move(args[n]));
400 }
401
402 void eval_args(std::vector<value>& args, size_t n)
403 {
404 assert_args(args, n);
405 for (auto& arg : std::span{ args }.subspan(1))
406 arg = eval(std::move(arg));
407 }
408
409 void eval_args(std::vector<value>& args)
410 {
411 eval_args(args, args.size() - 1);
412 }
413
414 template <typename LIB_TYPE>
415 void import_lib()
416 {
417 LIB_TYPE::import_to(*this);
418 }
419
420 template <template<bool> typename LIB_TYPE>
421 void import_lib()
422 {
423 LIB_TYPE<decade_syntax>::import_to(*this);
424 }
425 };
426
427}
constexpr auto bit_count
Equal to the number of bits in the type.
Definition bits.h:33
NOTE: Decade syntax is slower to execute but more natural.
Definition eval.h:96
std::function< bool(json const &)> truthiness_function
eval('.test') -> eval(prefix_macros['.']('.test'))
Definition eval.h:111
std::map< std::string, eval_func, std::less<> > funcs
TODO: How to do const parent envs, or const vars?
Definition eval.h:104