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