ChaiScript
chaiscript_eval.hpp
1 // This file is distributed under the BSD License.
2 // See "license.txt" for details.
3 // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
4 // Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
5 // http://www.chaiscript.com
6 
7 // This is an open source non-commercial project. Dear PVS-Studio, please check it.
8 // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
9 
10 #ifndef CHAISCRIPT_EVAL_HPP_
11 #define CHAISCRIPT_EVAL_HPP_
12 
13 #include <exception>
14 #include <functional>
15 #include <limits>
16 #include <map>
17 #include <memory>
18 #include <ostream>
19 #include <stdexcept>
20 #include <string>
21 #include <vector>
22 
23 #include "../chaiscript_defines.hpp"
24 #include "../dispatchkit/boxed_cast.hpp"
25 #include "../dispatchkit/boxed_number.hpp"
26 #include "../dispatchkit/boxed_value.hpp"
27 #include "../dispatchkit/dispatchkit.hpp"
28 #include "../dispatchkit/dynamic_object_detail.hpp"
29 #include "../dispatchkit/proxy_functions.hpp"
30 #include "../dispatchkit/proxy_functions_detail.hpp"
31 #include "../dispatchkit/register_function.hpp"
32 #include "../dispatchkit/type_info.hpp"
33 #include "chaiscript_algebraic.hpp"
34 #include "chaiscript_common.hpp"
35 
36 namespace chaiscript::exception {
37  class bad_boxed_cast;
38 } // namespace chaiscript::exception
39 
40 namespace chaiscript {
42  namespace eval {
43  template<typename T>
44  struct AST_Node_Impl;
45 
46  template<typename T>
47  using AST_Node_Impl_Ptr = typename std::unique_ptr<AST_Node_Impl<T>>;
48 
49  namespace detail {
51  template<typename T>
53  const AST_Node_Impl<T> &t_node,
54  const std::vector<std::string> &t_param_names,
55  const Function_Params &t_vals,
56  const std::map<std::string, Boxed_Value> *t_locals = nullptr,
57  bool has_this_capture = false) {
59 
60  const Boxed_Value *thisobj = [&]() -> const Boxed_Value * {
61  if (auto &stack = t_ss.get_stack_data(state.stack_holder()).back(); !stack.empty() && stack.back().first == "__this") {
62  return &stack.back().second;
63  } else if (!t_vals.empty()) {
64  return &t_vals[0];
65  } else {
66  return nullptr;
67  }
68  }();
69 
71  if (thisobj && !has_this_capture) {
72  state.add_object("this", *thisobj);
73  }
74 
75  if (t_locals) {
76  for (const auto &[name, value] : *t_locals) {
77  state.add_object(name, value);
78  }
79  }
80 
81  for (size_t i = 0; i < t_param_names.size(); ++i) {
82  if (t_param_names[i] != "this") {
83  state.add_object(t_param_names[i], t_vals[i]);
84  }
85  }
86 
87  try {
88  return t_node.eval(state);
89  } catch (detail::Return_Value &rv) {
90  return std::move(rv.retval);
91  }
92  }
93 
94  inline Boxed_Value clone_if_necessary(Boxed_Value incoming, std::atomic_uint_fast32_t &t_loc, const chaiscript::detail::Dispatch_State &t_ss) {
95  if (!incoming.is_return_value()) {
96  if (incoming.get_type_info().is_arithmetic()) {
97  return Boxed_Number::clone(incoming);
98  } else if (incoming.get_type_info().bare_equal_type_info(typeid(bool))) {
99  return Boxed_Value(*static_cast<const bool *>(incoming.get_const_ptr()));
100  } else if (incoming.get_type_info().bare_equal_type_info(typeid(std::string))) {
101  return Boxed_Value(*static_cast<const std::string *>(incoming.get_const_ptr()));
102  } else {
103  std::array<Boxed_Value, 1> params{std::move(incoming)};
104  return t_ss->call_function("clone", t_loc, Function_Params{params}, t_ss.conversions());
105  }
106  } else {
107  incoming.reset_return_value();
108  return incoming;
109  }
110  }
111  } // namespace detail
112 
113  template<typename T>
114  struct AST_Node_Impl : AST_Node {
115  AST_Node_Impl(std::string t_ast_node_text,
116  AST_Node_Type t_id,
117  Parse_Location t_loc,
118  std::vector<AST_Node_Impl_Ptr<T>> t_children = std::vector<AST_Node_Impl_Ptr<T>>())
119  : AST_Node(std::move(t_ast_node_text), t_id, std::move(t_loc))
120  , children(std::move(t_children)) {
121  }
122 
123  static bool get_scoped_bool_condition(const AST_Node_Impl<T> &node, const chaiscript::detail::Dispatch_State &t_ss) {
125  return get_bool_condition(node.eval(t_ss), t_ss);
126  }
127 
128  std::vector<std::reference_wrapper<AST_Node>> get_children() const final {
129  std::vector<std::reference_wrapper<AST_Node>> retval;
130  retval.reserve(children.size());
131  for (auto &child : children) {
132  retval.emplace_back(*child);
133  }
134 
135  return retval;
136  }
137 
138  Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const final {
139  try {
140  T::trace(t_e, this);
141  return eval_internal(t_e);
142  } catch (exception::eval_error &ee) {
143  ee.call_stack.push_back(*this);
144  throw;
145  }
146  }
147 
148  std::vector<AST_Node_Impl_Ptr<T>> children;
149 
150  protected:
151  virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const {
152  throw std::runtime_error("Undispatched ast_node (internal error)");
153  }
154  };
155 
156  template<typename T>
158  Compiled_AST_Node(AST_Node_Impl_Ptr<T> t_original_node,
159  std::vector<AST_Node_Impl_Ptr<T>> t_children,
160  std::function<Boxed_Value(const std::vector<AST_Node_Impl_Ptr<T>> &, const chaiscript::detail::Dispatch_State &t_ss)> t_func)
161  : AST_Node_Impl<T>(t_original_node->text, AST_Node_Type::Compiled, t_original_node->location, std::move(t_children))
162  , m_func(std::move(t_func))
163  , m_original_node(std::move(t_original_node)) {
164  }
165 
166  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { return m_func(this->children, t_ss); }
167 
168  std::function<Boxed_Value(const std::vector<AST_Node_Impl_Ptr<T>> &, const chaiscript::detail::Dispatch_State &t_ss)> m_func;
169  AST_Node_Impl_Ptr<T> m_original_node;
170  };
171 
172  template<typename T>
174  Fold_Right_Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children, Boxed_Value t_rhs)
175  : AST_Node_Impl<T>(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children))
176  , m_oper(Operators::to_operator(t_oper))
177  , m_rhs(std::move(t_rhs)) {
178  }
179 
180  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
181  return do_oper(t_ss, this->text, this->children[0]->eval(t_ss));
182  }
183 
184  protected:
185  Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss, const std::string &t_oper_string, const Boxed_Value &t_lhs) const {
186  try {
187  if (t_lhs.get_type_info().is_arithmetic()) {
188  // If it's an arithmetic operation we want to short circuit dispatch
189  try {
190  return Boxed_Number::do_oper(m_oper, t_lhs, m_rhs);
191  } catch (const chaiscript::exception::arithmetic_error &) {
192  throw;
193  } catch (...) {
194  throw exception::eval_error("Error with numeric operator calling: " + t_oper_string);
195  }
196  } else {
198  std::array<Boxed_Value, 2> params{t_lhs, m_rhs};
199  fpp.save_params(Function_Params{params});
200  return t_ss->call_function(t_oper_string, m_loc, Function_Params{params}, t_ss.conversions());
201  }
202  } catch (const exception::dispatch_error &e) {
203  throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss);
204  }
205  }
206 
207  private:
208  Operators::Opers m_oper;
209  Boxed_Value m_rhs;
210  mutable std::atomic_uint_fast32_t m_loc = {0};
211  };
212 
213  template<typename T>
215  Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
216  : AST_Node_Impl<T>(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children))
217  , m_oper(Operators::to_operator(t_oper)) {
218  }
219 
220  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
221  auto lhs = this->children[0]->eval(t_ss);
222  auto rhs = this->children[1]->eval(t_ss);
223  return do_oper(t_ss, m_oper, this->text, lhs, rhs);
224  }
225 
226  protected:
228  Operators::Opers t_oper,
229  const std::string &t_oper_string,
230  const Boxed_Value &t_lhs,
231  const Boxed_Value &t_rhs) const {
232  try {
233  if (t_oper != Operators::Opers::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic()) {
234  // If it's an arithmetic operation we want to short circuit dispatch
235  try {
236  return Boxed_Number::do_oper(t_oper, t_lhs, t_rhs);
237  } catch (const chaiscript::exception::arithmetic_error &) {
238  throw;
239  } catch (...) {
240  throw exception::eval_error("Error with numeric operator calling: " + t_oper_string);
241  }
242  } else {
244  std::array<Boxed_Value, 2> params{t_lhs, t_rhs};
245  fpp.save_params(Function_Params(params));
246  return t_ss->call_function(t_oper_string, m_loc, Function_Params(params), t_ss.conversions());
247  }
248  } catch (const exception::dispatch_error &e) {
249  throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss);
250  }
251  }
252 
253  private:
254  Operators::Opers m_oper;
255  mutable std::atomic_uint_fast32_t m_loc = {0};
256  };
257 
258  template<typename T>
259  struct Constant_AST_Node final : AST_Node_Impl<T> {
260  Constant_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, Boxed_Value t_value)
261  : AST_Node_Impl<T>(t_ast_node_text, AST_Node_Type::Constant, std::move(t_loc))
262  , m_value(std::move(t_value)) {
263  }
264 
265  explicit Constant_AST_Node(Boxed_Value t_value)
266  : AST_Node_Impl<T>("", AST_Node_Type::Constant, Parse_Location())
267  , m_value(std::move(t_value)) {
268  }
269 
270  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { return m_value; }
271 
272  Boxed_Value m_value;
273  };
274 
275  template<typename T>
276  struct Id_AST_Node final : AST_Node_Impl<T> {
277  Id_AST_Node(const std::string &t_ast_node_text, Parse_Location t_loc)
278  : AST_Node_Impl<T>(t_ast_node_text, AST_Node_Type::Id, std::move(t_loc)) {
279  }
280 
281  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
282  try {
283  return t_ss.get_object(this->text, m_loc);
284  } catch (std::exception &) {
285  throw exception::eval_error("Can not find object: " + this->text);
286  }
287  }
288 
289  private:
290  mutable std::atomic_uint_fast32_t m_loc = {0};
291  };
292 
293  template<typename T>
295  Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
296  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) {
297  assert(!this->children.empty());
298  }
299 
300  template<bool Save_Params>
301  Boxed_Value do_eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const {
303 
304  std::vector<Boxed_Value> params;
305 
306  params.reserve(this->children[1]->children.size());
307  for (const auto &child : this->children[1]->children) {
308  params.push_back(child->eval(t_ss));
309  }
310 
311  if (Save_Params) {
312  fpp.save_params(Function_Params{params});
313  }
314 
315  Boxed_Value fn(this->children[0]->eval(t_ss));
316 
317  try {
318  return (*t_ss->boxed_cast<const dispatch::Proxy_Function_Base *>(fn))(Function_Params{params}, t_ss.conversions());
319  } catch (const exception::dispatch_error &e) {
320  throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'",
321  e.parameters,
322  e.functions,
323  false,
324  *t_ss);
325  } catch (const exception::bad_boxed_cast &) {
326  try {
327  using ConstFunctionTypeRef = const Const_Proxy_Function &;
328  Const_Proxy_Function f = t_ss->boxed_cast<ConstFunctionTypeRef>(fn);
329  // handle the case where there is only 1 function to try to call and dispatch fails on it
330  throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, make_vector(f), false, *t_ss);
331  } catch (const exception::bad_boxed_cast &) {
332  throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function.");
333  }
334  } catch (const exception::arity_error &e) {
335  throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'");
336  } catch (const exception::guard_error &e) {
337  throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'");
338  } catch (detail::Return_Value &rv) {
339  return rv.retval;
340  }
341  }
342 
343  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { return do_eval_internal<true>(t_ss); }
344  };
345 
346  template<typename T>
348  Unused_Return_Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
349  : Fun_Call_AST_Node<T>(std::move(t_ast_node_text), std::move(t_loc), std::move(t_children)) {
350  }
351 
352  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
353  return this->template do_eval_internal<false>(t_ss);
354  }
355  };
356 
357  template<typename T>
359  Arg_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
360  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) {
361  }
362  };
363 
364  template<typename T>
366  Arg_List_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
367  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) {
368  }
369 
370  static std::string get_arg_name(const AST_Node_Impl<T> &t_node) {
371  if (t_node.children.empty()) {
372  return t_node.text;
373  } else if (t_node.children.size() == 1) {
374  return t_node.children[0]->text;
375  } else {
376  return t_node.children[1]->text;
377  }
378  }
379 
380  static std::vector<std::string> get_arg_names(const AST_Node_Impl<T> &t_node) {
381  std::vector<std::string> retval;
382 
383  for (const auto &node : t_node.children) {
384  retval.push_back(get_arg_name(*node));
385  }
386 
387  return retval;
388  }
389 
390  static std::pair<std::string, Type_Info> get_arg_type(const AST_Node_Impl<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) {
391  if (t_node.children.size() < 2) {
392  return {};
393  } else {
394  return {t_node.children[0]->text, t_ss->get_type(t_node.children[0]->text, false)};
395  }
396  }
397 
398  static dispatch::Param_Types get_arg_types(const AST_Node_Impl<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) {
399  std::vector<std::pair<std::string, Type_Info>> retval;
400 
401  for (const auto &child : t_node.children) {
402  retval.push_back(get_arg_type(*child, t_ss));
403  }
404 
405  return dispatch::Param_Types(std::move(retval));
406  }
407  };
408 
409  template<typename T>
410  struct Equation_AST_Node final : AST_Node_Impl<T> {
411  Equation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
412  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Equation, std::move(t_loc), std::move(t_children))
413  , m_oper(Operators::to_operator(this->text)) {
414  assert(this->children.size() == 2);
415  }
416 
419 
420  auto params = [&]() {
421  // The RHS *must* be evaluated before the LHS
422  // consider `var range = range(x)`
423  // if we declare the variable in scope first, then the name lookup fails
424  // for the RHS
425  auto rhs = this->children[1]->eval(t_ss);
426  auto lhs = this->children[0]->eval(t_ss);
427  std::array<Boxed_Value, 2> p{std::move(lhs), std::move(rhs)};
428  return p;
429  }();
430 
431  if (params[0].is_return_value()) {
432  throw exception::eval_error("Error, cannot assign to temporary value.");
433  } else if (params[0].is_const()) {
434  throw exception::eval_error("Error, cannot assign to constant value.");
435  }
436 
437  if (m_oper != Operators::Opers::invalid && params[0].get_type_info().is_arithmetic() && params[1].get_type_info().is_arithmetic()) {
438  try {
439  return Boxed_Number::do_oper(m_oper, params[0], params[1]);
440  } catch (const std::exception &) {
441  throw exception::eval_error("Error with unsupported arithmetic assignment operation.");
442  }
443  } else if (m_oper == Operators::Opers::assign) {
444  try {
445  if (params[0].is_undef()) {
446  if (!this->children.empty()
447  && ((this->children[0]->identifier == AST_Node_Type::Reference)
448  || (!this->children[0]->children.empty() && this->children[0]->children[0]->identifier == AST_Node_Type::Reference)))
449 
450  {
453  params[0].assign(params[1]);
454  params[0].reset_return_value();
455  return params[1];
456  } else {
457  params[1] = detail::clone_if_necessary(std::move(params[1]), m_clone_loc, t_ss);
458  }
459  }
460 
461  try {
462  return t_ss->call_function(this->text, m_loc, Function_Params{params}, t_ss.conversions());
463  } catch (const exception::dispatch_error &e) {
464  throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss);
465  }
466  } catch (const exception::dispatch_error &e) {
467  throw exception::eval_error("Missing clone or copy constructor for right hand side of equation",
468  e.parameters,
469  e.functions,
470  false,
471  *t_ss);
472  }
473  } else if (this->text == ":=") {
474  if (params[0].is_undef() || Boxed_Value::type_match(params[0], params[1])) {
475  params[0].assign(params[1]);
476  params[0].reset_return_value();
477  } else {
478  throw exception::eval_error("Mismatched types in equation");
479  }
480  } else {
481  try {
482  return t_ss->call_function(this->text, m_loc, Function_Params{params}, t_ss.conversions());
483  } catch (const exception::dispatch_error &e) {
484  throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss);
485  }
486  }
487 
488  return params[1];
489  }
490 
491  private:
492  Operators::Opers m_oper;
493  mutable std::atomic_uint_fast32_t m_loc = {0};
494  mutable std::atomic_uint_fast32_t m_clone_loc = {0};
495  };
496 
497  template<typename T>
499  Global_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
500  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Global_Decl, std::move(t_loc), std::move(t_children)) {
501  }
502 
503  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
504  const std::string &idname = (this->children[0]->identifier == AST_Node_Type::Reference) ? this->children[0]->children[0]->text : this->children[0]->text;
505 
506  return t_ss->add_global_no_throw(Boxed_Value(), idname);
507  }
508  };
509 
510  template<typename T>
511  struct Var_Decl_AST_Node final : AST_Node_Impl<T> {
512  Var_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
513  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Var_Decl, std::move(t_loc), std::move(t_children)) {
514  }
515 
516  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
517  const std::string &idname = this->children[0]->text;
518 
519  try {
520  Boxed_Value bv;
521  t_ss.add_object(idname, bv);
522  return bv;
523  } catch (const exception::name_conflict_error &e) {
524  throw exception::eval_error("Variable redefined '" + e.name() + "'");
525  }
526  }
527  };
528 
529  template<typename T>
531  Assign_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
532  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Assign_Decl, std::move(t_loc), std::move(t_children)) {
533  }
534 
535  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
536  const std::string &idname = this->children[0]->text;
537 
538  try {
539  Boxed_Value bv(detail::clone_if_necessary(this->children[1]->eval(t_ss), m_loc, t_ss));
540  bv.reset_return_value();
541  t_ss.add_object(idname, bv);
542  return bv;
543  } catch (const exception::name_conflict_error &e) {
544  throw exception::eval_error("Variable redefined '" + e.name() + "'");
545  }
546  }
547 
548  private:
549  mutable std::atomic_uint_fast32_t m_loc = {0};
550  };
551 
552  template<typename T>
553  struct Array_Call_AST_Node final : AST_Node_Impl<T> {
554  Array_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
555  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Array_Call, std::move(t_loc), std::move(t_children)) {
556  }
557 
558  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
560 
561  std::array<Boxed_Value, 2> params{this->children[0]->eval(t_ss), this->children[1]->eval(t_ss)};
562 
563  try {
564  fpp.save_params(Function_Params{params});
565  return t_ss->call_function("[]", m_loc, Function_Params{params}, t_ss.conversions());
566  } catch (const exception::dispatch_error &e) {
567  throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss);
568  }
569  }
570 
571  private:
572  mutable std::atomic_uint_fast32_t m_loc = {0};
573  };
574 
575  template<typename T>
576  struct Dot_Access_AST_Node final : AST_Node_Impl<T> {
577  Dot_Access_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
578  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Dot_Access, std::move(t_loc), std::move(t_children))
579  , m_fun_name(((this->children[1]->identifier == AST_Node_Type::Fun_Call) || (this->children[1]->identifier == AST_Node_Type::Array_Call))
580  ? this->children[1]->children[0]->text
581  : this->children[1]->text) {
582  }
583 
584  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
586 
587  Boxed_Value retval = this->children[0]->eval(t_ss);
588  auto params = make_vector(retval);
589 
590  bool has_function_params = false;
591  if (this->children[1]->children.size() > 1) {
592  has_function_params = true;
593  for (const auto &child : this->children[1]->children[1]->children) {
594  params.push_back(child->eval(t_ss));
595  }
596  }
597 
598  fpp.save_params(Function_Params{params});
599 
600  try {
601  retval = t_ss->call_member(m_fun_name, m_loc, Function_Params{params}, has_function_params, t_ss.conversions());
602  } catch (const exception::dispatch_error &e) {
603  if (e.functions.empty()) {
604  throw exception::eval_error("'" + m_fun_name + "' is not a function.");
605  } else {
606  throw exception::eval_error(std::string(e.what()) + " for function '" + m_fun_name + "'", e.parameters, e.functions, true, *t_ss);
607  }
608  } catch (detail::Return_Value &rv) {
609  retval = std::move(rv.retval);
610  }
611 
612  if (this->children[1]->identifier == AST_Node_Type::Array_Call) {
613  try {
614  std::array<Boxed_Value, 2> p{retval, this->children[1]->children[1]->eval(t_ss)};
615  retval = t_ss->call_function("[]", m_array_loc, Function_Params{p}, t_ss.conversions());
616  } catch (const exception::dispatch_error &e) {
617  throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss);
618  }
619  }
620 
621  return retval;
622  }
623 
624  private:
625  mutable std::atomic_uint_fast32_t m_loc = {0};
626  mutable std::atomic_uint_fast32_t m_array_loc = {0};
627  const std::string m_fun_name;
628  };
629 
630  template<typename T>
631  struct Lambda_AST_Node final : AST_Node_Impl<T> {
632  Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
633  : AST_Node_Impl<T>(t_ast_node_text,
634  AST_Node_Type::Lambda,
635  std::move(t_loc),
636  std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
637  std::make_move_iterator(std::prev(t_children.end()))))
638  , m_param_names(Arg_List_AST_Node<T>::get_arg_names(*this->children[1]))
639  , m_this_capture(has_this_capture(this->children[0]->children))
640  , m_lambda_node(std::move(t_children.back())) {
641  }
642 
643  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
644  const auto captures = [&]() -> std::map<std::string, Boxed_Value> {
645  std::map<std::string, Boxed_Value> named_captures;
646  for (const auto &capture : this->children[0]->children) {
647  named_captures.insert(std::make_pair(capture->children[0]->text, capture->children[0]->eval(t_ss)));
648  }
649  return named_captures;
650  }();
651 
652  const auto numparams = this->children[1]->children.size();
653  const auto param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[1], t_ss);
654 
655  std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
656 
657  return Boxed_Value(dispatch::make_dynamic_proxy_function(
658  [engine, lambda_node = this->m_lambda_node, param_names = this->m_param_names, captures, this_capture = this->m_this_capture](
659  const Function_Params &t_params) {
660  return detail::eval_function(engine, *lambda_node, param_names, t_params, &captures, this_capture);
661  },
662  static_cast<int>(numparams),
663  m_lambda_node,
664  param_types));
665  }
666 
667  static bool has_this_capture(const std::vector<AST_Node_Impl_Ptr<T>> &t_children) noexcept {
668  return std::any_of(std::begin(t_children), std::end(t_children), [](const auto &child) { return child->children[0]->text == "this"; });
669  }
670 
671  private:
672  const std::vector<std::string> m_param_names;
673  const bool m_this_capture = false;
674  const std::shared_ptr<AST_Node_Impl<T>> m_lambda_node;
675  };
676 
677  template<typename T>
679  Scopeless_Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
680  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Scopeless_Block, std::move(t_loc), std::move(t_children)) {
681  }
682 
683  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
684  const auto num_children = this->children.size();
685  for (size_t i = 0; i < num_children - 1; ++i) {
686  this->children[i]->eval(t_ss);
687  }
688  return this->children.back()->eval(t_ss);
689  }
690  };
691 
692  template<typename T>
693  struct Block_AST_Node final : AST_Node_Impl<T> {
694  Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
695  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Block, std::move(t_loc), std::move(t_children)) {
696  }
697 
698  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
700 
701  const auto num_children = this->children.size();
702  for (size_t i = 0; i < num_children - 1; ++i) {
703  this->children[i]->eval(t_ss);
704  }
705  return this->children.back()->eval(t_ss);
706  }
707  };
708 
709  template<typename T>
710  struct Def_AST_Node final : AST_Node_Impl<T> {
711  std::shared_ptr<AST_Node_Impl<T>> m_body_node;
712  std::shared_ptr<AST_Node_Impl<T>> m_guard_node;
713 
714  Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
715  : AST_Node_Impl<T>(std::move(t_ast_node_text),
716  AST_Node_Type::Def,
717  std::move(t_loc),
718  std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
719  std::make_move_iterator(
720  std::prev(t_children.end(), has_guard(t_children, 1) ? 2 : 1))))
721  ,
722  // This apparent use after move is safe because we are only moving out the specific elements we need
723  // on each operation.
724  m_body_node(get_body_node(std::move(t_children)))
725  , m_guard_node(get_guard_node(std::move(t_children), t_children.size() - this->children.size() == 2))
726 
727  {
728  }
729 
730  static std::shared_ptr<AST_Node_Impl<T>> get_guard_node(std::vector<AST_Node_Impl_Ptr<T>> &&vec, bool has_guard) {
731  if (has_guard) {
732  return std::move(*std::prev(vec.end(), 2));
733  } else {
734  return {};
735  }
736  }
737 
738  static std::shared_ptr<AST_Node_Impl<T>> get_body_node(std::vector<AST_Node_Impl_Ptr<T>> &&vec) { return std::move(vec.back()); }
739 
740  static bool has_guard(const std::vector<AST_Node_Impl_Ptr<T>> &t_children, const std::size_t offset) noexcept {
741  if ((t_children.size() > 2 + offset) && (t_children[1 + offset]->identifier == AST_Node_Type::Arg_List)) {
742  if (t_children.size() > 3 + offset) {
743  return true;
744  }
745  } else {
746  if (t_children.size() > 2 + offset) {
747  return true;
748  }
749  }
750  return false;
751  }
752 
753  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
754  std::vector<std::string> t_param_names;
755  size_t numparams = 0;
756 
757  dispatch::Param_Types param_types;
758 
759  if ((this->children.size() > 1) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) {
760  numparams = this->children[1]->children.size();
761  t_param_names = Arg_List_AST_Node<T>::get_arg_names(*this->children[1]);
762  param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[1], t_ss);
763  }
764 
765  std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
766  std::shared_ptr<dispatch::Proxy_Function_Base> guard;
767  if (m_guard_node) {
768  guard = dispatch::make_dynamic_proxy_function(
769  [engine, guardnode = m_guard_node, t_param_names](const Function_Params &t_params) {
770  return detail::eval_function(engine, *guardnode, t_param_names, t_params);
771  },
772  static_cast<int>(numparams),
773  m_guard_node);
774  }
775 
776  try {
777  const std::string &l_function_name = this->children[0]->text;
778  t_ss->add(dispatch::make_dynamic_proxy_function(
779  [engine, func_node = m_body_node, t_param_names](const Function_Params &t_params) {
780  return detail::eval_function(engine, *func_node, t_param_names, t_params);
781  },
782  static_cast<int>(numparams),
783  m_body_node,
784  param_types,
785  guard),
786  l_function_name);
787  } catch (const exception::name_conflict_error &e) {
788  throw exception::eval_error("Function redefined '" + e.name() + "'");
789  }
790  return void_var();
791  }
792  };
793 
794  template<typename T>
795  struct While_AST_Node final : AST_Node_Impl<T> {
796  While_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
797  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::While, std::move(t_loc), std::move(t_children)) {
798  }
799 
800  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
802 
803  try {
804  while (this->get_scoped_bool_condition(*this->children[0], t_ss)) {
805  try {
806  this->children[1]->eval(t_ss);
807  } catch (detail::Continue_Loop &) {
808  // we got a continue exception, which means all of the remaining
809  // loop implementation is skipped and we just need to continue to
810  // the next condition test
811  }
812  }
813  } catch (detail::Break_Loop &) {
814  // loop was broken intentionally
815  }
816 
817  return void_var();
818  }
819  };
820 
821  template<typename T>
822  struct Class_AST_Node final : AST_Node_Impl<T> {
823  Class_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
824  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Class, std::move(t_loc), std::move(t_children)) {
825  }
826 
829 
831  // put class name in current scope so it can be looked up by the attrs and methods
832  t_ss.add_object("_current_class_name", const_var(this->children[0]->text));
833 
834  this->children[1]->eval(t_ss);
835 
836  return void_var();
837  }
838  };
839 
840  template<typename T>
841  struct If_AST_Node final : AST_Node_Impl<T> {
842  If_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
843  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::If, std::move(t_loc), std::move(t_children)) {
844  assert(this->children.size() == 3);
845  }
846 
847  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
848  if (this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)) {
849  return this->children[1]->eval(t_ss);
850  } else {
851  return this->children[2]->eval(t_ss);
852  }
853  }
854  };
855 
856  template<typename T>
857  struct Ranged_For_AST_Node final : AST_Node_Impl<T> {
858  Ranged_For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
859  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Ranged_For, std::move(t_loc), std::move(t_children)) {
860  assert(this->children.size() == 3);
861  }
862 
864  const auto get_function = [&t_ss](const std::string &t_name, auto &t_hint) {
865  uint_fast32_t hint = t_hint;
866  auto [funs_loc, funs] = t_ss->get_function(t_name, hint);
867  if (funs_loc != hint) {
868  t_hint = uint_fast32_t(funs_loc);
869  }
870  return std::move(funs);
871  };
872 
873  const auto call_function = [&t_ss](const auto &t_funcs, const Boxed_Value &t_param) {
874  return dispatch::dispatch(*t_funcs, Function_Params{t_param}, t_ss.conversions());
875  };
876 
877  const std::string &loop_var_name = this->children[0]->text;
878  Boxed_Value range_expression_result = this->children[1]->eval(t_ss);
879 
880  const auto do_loop = [&loop_var_name, &t_ss, this](const auto &ranged_thing) {
881  try {
882  for (auto &&loop_var : ranged_thing) {
883  // This scope push and pop might not be the best thing for perf
884  // but we know it's 100% correct
887  if (!std::is_same<std::decay_t<decltype(loop_var)>, Boxed_Value>::value) {
888  t_ss.add_get_object(loop_var_name, Boxed_Value(std::ref(loop_var)));
889  } else {
890  t_ss.add_get_object(loop_var_name, Boxed_Value(loop_var));
891  }
892  try {
893  this->children[2]->eval(t_ss);
894  } catch (detail::Continue_Loop &) {
895  }
896  }
897  } catch (detail::Break_Loop &) {
898  // loop broken
899  }
900  return void_var();
901  };
902 
903  if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::vector<Boxed_Value>))) {
904  return do_loop(boxed_cast<const std::vector<Boxed_Value> &>(range_expression_result));
905  } else if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::map<std::string, Boxed_Value>))) {
906  return do_loop(boxed_cast<const std::map<std::string, Boxed_Value> &>(range_expression_result));
907  } else {
908  const auto range_funcs = get_function("range", m_range_loc);
909  const auto empty_funcs = get_function("empty", m_empty_loc);
910  const auto front_funcs = get_function("front", m_front_loc);
911  const auto pop_front_funcs = get_function("pop_front", m_pop_front_loc);
912 
913  try {
914  const auto range_obj = call_function(range_funcs, range_expression_result);
915  while (!boxed_cast<bool>(call_function(empty_funcs, range_obj))) {
917  t_ss.add_get_object(loop_var_name, call_function(front_funcs, range_obj));
918  try {
919  this->children[2]->eval(t_ss);
920  } catch (detail::Continue_Loop &) {
921  // continue statement hit
922  }
923  call_function(pop_front_funcs, range_obj);
924  }
925  } catch (detail::Break_Loop &) {
926  // loop broken
927  }
928  return void_var();
929  }
930  }
931 
932  private:
933  mutable std::atomic_uint_fast32_t m_range_loc = {0};
934  mutable std::atomic_uint_fast32_t m_empty_loc = {0};
935  mutable std::atomic_uint_fast32_t m_front_loc = {0};
936  mutable std::atomic_uint_fast32_t m_pop_front_loc = {0};
937  };
938 
939  template<typename T>
940  struct For_AST_Node final : AST_Node_Impl<T> {
941  For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
942  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::For, std::move(t_loc), std::move(t_children)) {
943  assert(this->children.size() == 4);
944  }
945 
946  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
948 
949  try {
950  for (this->children[0]->eval(t_ss); this->get_scoped_bool_condition(*this->children[1], t_ss); this->children[2]->eval(t_ss)) {
951  try {
952  // Body of Loop
953  this->children[3]->eval(t_ss);
954  } catch (detail::Continue_Loop &) {
955  // we got a continue exception, which means all of the remaining
956  // loop implementation is skipped and we just need to continue to
957  // the next iteration step
958  }
959  }
960  } catch (detail::Break_Loop &) {
961  // loop broken
962  }
963 
964  return void_var();
965  }
966  };
967 
968  template<typename T>
969  struct Switch_AST_Node final : AST_Node_Impl<T> {
970  Switch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
971  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Switch, std::move(t_loc), std::move(t_children)) {
972  }
973 
974  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
975  bool breaking = false;
976  size_t currentCase = 1;
977  bool hasMatched = false;
978 
980 
981  Boxed_Value match_value(this->children[0]->eval(t_ss));
982 
983  while (!breaking && (currentCase < this->children.size())) {
984  try {
985  if (this->children[currentCase]->identifier == AST_Node_Type::Case) {
986  // This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here.
987  try {
988  std::array<Boxed_Value, 2> p{match_value, this->children[currentCase]->children[0]->eval(t_ss)};
989  if (hasMatched || boxed_cast<bool>(t_ss->call_function("==", m_loc, Function_Params{p}, t_ss.conversions()))) {
990  this->children[currentCase]->eval(t_ss);
991  hasMatched = true;
992  }
993  } catch (const exception::bad_boxed_cast &) {
994  throw exception::eval_error("Internal error: case guard evaluation not boolean");
995  }
996  } else if (this->children[currentCase]->identifier == AST_Node_Type::Default) {
997  this->children[currentCase]->eval(t_ss);
998  hasMatched = true;
999  }
1000  } catch (detail::Break_Loop &) {
1001  breaking = true;
1002  }
1003  ++currentCase;
1004  }
1005  return void_var();
1006  }
1007 
1008  mutable std::atomic_uint_fast32_t m_loc = {0};
1009  };
1010 
1011  template<typename T>
1012  struct Case_AST_Node final : AST_Node_Impl<T> {
1013  Case_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1014  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Case, std::move(t_loc), std::move(t_children)) {
1015  assert(this->children.size() == 2); /* how many children does it have? */
1016  }
1017 
1018  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1020 
1021  this->children[1]->eval(t_ss);
1022 
1023  return void_var();
1024  }
1025  };
1026 
1027  template<typename T>
1028  struct Default_AST_Node final : AST_Node_Impl<T> {
1029  Default_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1030  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Default, std::move(t_loc), std::move(t_children)) {
1031  assert(this->children.size() == 1);
1032  }
1033 
1034  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1036 
1037  this->children[0]->eval(t_ss);
1038 
1039  return void_var();
1040  }
1041  };
1042 
1043  template<typename T>
1045  Inline_Array_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1046  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Inline_Array, std::move(t_loc), std::move(t_children)) {
1047  }
1048 
1049  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1050  try {
1051  std::vector<Boxed_Value> vec;
1052  if (!this->children.empty()) {
1053  vec.reserve(this->children[0]->children.size());
1054  for (const auto &child : this->children[0]->children) {
1055  vec.push_back(detail::clone_if_necessary(child->eval(t_ss), m_loc, t_ss));
1056  }
1057  }
1058  return const_var(std::move(vec));
1059  } catch (const exception::dispatch_error &) {
1060  throw exception::eval_error("Can not find appropriate 'clone' or copy constructor for vector elements");
1061  }
1062  }
1063 
1064  private:
1065  mutable std::atomic_uint_fast32_t m_loc = {0};
1066  };
1067 
1068  template<typename T>
1070  Inline_Map_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1071  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Inline_Map, std::move(t_loc), std::move(t_children)) {
1072  }
1073 
1074  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1075  try {
1076  std::map<std::string, Boxed_Value> retval;
1077 
1078  for (const auto &child : this->children[0]->children) {
1079  retval.insert(std::make_pair(t_ss->boxed_cast<std::string>(child->children[0]->eval(t_ss)),
1080  detail::clone_if_necessary(child->children[1]->eval(t_ss), m_loc, t_ss)));
1081  }
1082 
1083  return const_var(std::move(retval));
1084  } catch (const exception::dispatch_error &e) {
1085  throw exception::eval_error("Can not find appropriate copy constructor or 'clone' while inserting into Map.",
1086  e.parameters,
1087  e.functions,
1088  false,
1089  *t_ss);
1090  }
1091  }
1092 
1093  private:
1094  mutable std::atomic_uint_fast32_t m_loc = {0};
1095  };
1096 
1097  template<typename T>
1098  struct Return_AST_Node final : AST_Node_Impl<T> {
1099  Return_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1100  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Return, std::move(t_loc), std::move(t_children)) {
1101  }
1102 
1103  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1104  if (!this->children.empty()) {
1105  throw detail::Return_Value{this->children[0]->eval(t_ss)};
1106  } else {
1107  throw detail::Return_Value{void_var()};
1108  }
1109  }
1110  };
1111 
1112  template<typename T>
1113  struct File_AST_Node final : AST_Node_Impl<T> {
1114  File_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1115  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::File, std::move(t_loc), std::move(t_children)) {
1116  }
1117 
1118  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1119  try {
1120  const auto num_children = this->children.size();
1121 
1122  if (num_children > 0) {
1123  for (size_t i = 0; i < num_children - 1; ++i) {
1124  this->children[i]->eval(t_ss);
1125  }
1126  return this->children.back()->eval(t_ss);
1127  } else {
1128  return void_var();
1129  }
1130  } catch (const detail::Continue_Loop &) {
1131  throw exception::eval_error("Unexpected `continue` statement outside of a loop");
1132  } catch (const detail::Break_Loop &) {
1133  throw exception::eval_error("Unexpected `break` statement outside of a loop");
1134  }
1135  }
1136  };
1137 
1138  template<typename T>
1139  struct Reference_AST_Node final : AST_Node_Impl<T> {
1140  Reference_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1141  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Reference, std::move(t_loc), std::move(t_children)) {
1142  assert(this->children.size() == 1);
1143  }
1144 
1145  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1146  Boxed_Value bv;
1147  t_ss.add_object(this->children[0]->text, bv);
1148  return bv;
1149  }
1150  };
1151 
1152  template<typename T>
1153  struct Prefix_AST_Node final : AST_Node_Impl<T> {
1154  Prefix_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1155  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Prefix, std::move(t_loc), std::move(t_children))
1156  , m_oper(Operators::to_operator(this->text, true)) {
1157  }
1158 
1159  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1160  Boxed_Value bv(this->children[0]->eval(t_ss));
1161 
1162  try {
1163  // short circuit arithmetic operations
1164  if (m_oper != Operators::Opers::invalid && m_oper != Operators::Opers::bitwise_and && bv.get_type_info().is_arithmetic()) {
1165  if ((m_oper == Operators::Opers::pre_increment || m_oper == Operators::Opers::pre_decrement) && bv.is_const()) {
1166  throw exception::eval_error("Error with prefix operator evaluation: cannot modify constant value.");
1167  }
1168  return Boxed_Number::do_oper(m_oper, bv);
1169  } else {
1171  fpp.save_params(Function_Params{bv});
1172  return t_ss->call_function(this->text, m_loc, Function_Params{bv}, t_ss.conversions());
1173  }
1174  } catch (const exception::dispatch_error &e) {
1175  throw exception::eval_error("Error with prefix operator evaluation: '" + this->text + "'", e.parameters, e.functions, false, *t_ss);
1176  }
1177  }
1178 
1179  private:
1180  Operators::Opers m_oper = Operators::Opers::invalid;
1181  mutable std::atomic_uint_fast32_t m_loc = {0};
1182  };
1183 
1184  template<typename T>
1185  struct Break_AST_Node final : AST_Node_Impl<T> {
1186  Break_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1187  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Break, std::move(t_loc), std::move(t_children)) {
1188  }
1189 
1190  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { throw detail::Break_Loop(); }
1191  };
1192 
1193  template<typename T>
1194  struct Continue_AST_Node final : AST_Node_Impl<T> {
1195  Continue_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1196  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Continue, std::move(t_loc), std::move(t_children)) {
1197  }
1198 
1199  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { throw detail::Continue_Loop(); }
1200  };
1201 
1202  template<typename T>
1203  struct Noop_AST_Node final : AST_Node_Impl<T> {
1204  Noop_AST_Node()
1205  : AST_Node_Impl<T>("", AST_Node_Type::Noop, Parse_Location()) {
1206  }
1207 
1208  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override {
1209  // It's a no-op, that evaluates to "void"
1210  return val;
1211  }
1212 
1213  Boxed_Value val = void_var();
1214  };
1215 
1216  template<typename T>
1217  struct Map_Pair_AST_Node final : AST_Node_Impl<T> {
1218  Map_Pair_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1219  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Map_Pair, std::move(t_loc), std::move(t_children)) {
1220  }
1221  };
1222 
1223  template<typename T>
1225  Value_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1226  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Value_Range, std::move(t_loc), std::move(t_children)) {
1227  }
1228  };
1229 
1230  template<typename T>
1232  Inline_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1233  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Inline_Range, std::move(t_loc), std::move(t_children)) {
1234  }
1235 
1236  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1237  try {
1238  std::array<Boxed_Value, 2> params{this->children[0]->children[0]->children[0]->eval(t_ss),
1239  this->children[0]->children[0]->children[1]->eval(t_ss)};
1240 
1241  return t_ss->call_function("generate_range", m_loc, Function_Params{params}, t_ss.conversions());
1242  } catch (const exception::dispatch_error &e) {
1243  throw exception::eval_error("Unable to generate range vector, while calling 'generate_range'", e.parameters, e.functions, false, *t_ss);
1244  }
1245  }
1246 
1247  private:
1248  mutable std::atomic_uint_fast32_t m_loc = {0};
1249  };
1250 
1251  template<typename T>
1252  struct Try_AST_Node final : AST_Node_Impl<T> {
1253  Try_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1254  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Try, std::move(t_loc), std::move(t_children)) {
1255  }
1256 
1257  Boxed_Value handle_exception(const chaiscript::detail::Dispatch_State &t_ss, const Boxed_Value &t_except) const {
1258  Boxed_Value retval;
1259 
1260  size_t end_point = this->children.size();
1261  if (this->children.back()->identifier == AST_Node_Type::Finally) {
1262  assert(end_point > 0);
1263  end_point = this->children.size() - 1;
1264  }
1265  for (size_t i = 1; i < end_point; ++i) {
1266  chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss);
1267  auto &catch_block = *this->children[i];
1268 
1269  if (catch_block.children.size() == 1) {
1270  // No variable capture
1271  retval = catch_block.children[0]->eval(t_ss);
1272  break;
1273  } else if (catch_block.children.size() == 2 || catch_block.children.size() == 3) {
1274  const auto name = Arg_List_AST_Node<T>::get_arg_name(*catch_block.children[0]);
1275 
1277  std::vector<std::pair<std::string, Type_Info>>{Arg_List_AST_Node<T>::get_arg_type(*catch_block.children[0], t_ss)})
1278  .match(Function_Params{t_except}, t_ss.conversions())
1279  .first) {
1280  t_ss.add_object(name, t_except);
1281 
1282  if (catch_block.children.size() == 2) {
1283  // Variable capture
1284  retval = catch_block.children[1]->eval(t_ss);
1285  break;
1286  }
1287  }
1288  } else {
1289  if (this->children.back()->identifier == AST_Node_Type::Finally) {
1290  this->children.back()->children[0]->eval(t_ss);
1291  }
1292  throw exception::eval_error("Internal error: catch block size unrecognized");
1293  }
1294  }
1295 
1296  return retval;
1297  }
1298 
1299  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1300  Boxed_Value retval;
1301 
1303 
1304  try {
1305  retval = this->children[0]->eval(t_ss);
1306  } catch (const exception::eval_error &e) {
1307  retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
1308  } catch (const std::runtime_error &e) {
1309  retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
1310  } catch (const std::out_of_range &e) {
1311  retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
1312  } catch (const std::exception &e) {
1313  retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
1314  } catch (Boxed_Value &e) {
1315  retval = handle_exception(t_ss, e);
1316  } catch (...) {
1317  if (this->children.back()->identifier == AST_Node_Type::Finally) {
1318  this->children.back()->children[0]->eval(t_ss);
1319  }
1320  throw;
1321  }
1322 
1323  if (this->children.back()->identifier == AST_Node_Type::Finally) {
1324  retval = this->children.back()->children[0]->eval(t_ss);
1325  }
1326 
1327  return retval;
1328  }
1329  };
1330 
1331  template<typename T>
1332  struct Catch_AST_Node final : AST_Node_Impl<T> {
1333  Catch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1334  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Catch, std::move(t_loc), std::move(t_children)) {
1335  }
1336  };
1337 
1338  template<typename T>
1339  struct Finally_AST_Node final : AST_Node_Impl<T> {
1340  Finally_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1341  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Finally, std::move(t_loc), std::move(t_children)) {
1342  }
1343  };
1344 
1345  template<typename T>
1346  struct Method_AST_Node final : AST_Node_Impl<T> {
1347  std::shared_ptr<AST_Node_Impl<T>> m_body_node;
1348  std::shared_ptr<AST_Node_Impl<T>> m_guard_node;
1349 
1350  Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1351  : AST_Node_Impl<T>(std::move(t_ast_node_text),
1352  AST_Node_Type::Method,
1353  std::move(t_loc),
1354  std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
1355  std::make_move_iterator(
1356  std::prev(t_children.end(), Def_AST_Node<T>::has_guard(t_children, 1) ? 2 : 1))))
1357  , m_body_node(Def_AST_Node<T>::get_body_node(std::move(t_children)))
1358  , m_guard_node(Def_AST_Node<T>::get_guard_node(std::move(t_children), t_children.size() - this->children.size() == 2)) {
1359  }
1360 
1361  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1362  AST_Node_Impl_Ptr<T> guardnode;
1363 
1364  const std::string &class_name = this->children[0]->text;
1365 
1366  // The first param of a method is always the implied this ptr.
1367  std::vector<std::string> t_param_names{"this"};
1368  dispatch::Param_Types param_types;
1369 
1370  if ((this->children.size() > 2) && (this->children[2]->identifier == AST_Node_Type::Arg_List)) {
1371  auto args = Arg_List_AST_Node<T>::get_arg_names(*this->children[2]);
1372  t_param_names.insert(t_param_names.end(), args.begin(), args.end());
1373  param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[2], t_ss);
1374  }
1375 
1376  const size_t numparams = t_param_names.size();
1377 
1378  std::shared_ptr<dispatch::Proxy_Function_Base> guard;
1379  std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
1380  if (m_guard_node) {
1381  guard = dispatch::make_dynamic_proxy_function(
1382  [engine, t_param_names, guardnode = m_guard_node](const Function_Params &t_params) {
1383  return chaiscript::eval::detail::eval_function(engine, *guardnode, t_param_names, t_params);
1384  },
1385  static_cast<int>(numparams),
1386  m_guard_node);
1387  }
1388 
1389  try {
1390  const std::string &function_name = this->children[1]->text;
1391 
1392  if (function_name == class_name) {
1393  param_types.push_front(class_name, Type_Info());
1394 
1395  t_ss->add(std::make_shared<dispatch::detail::Dynamic_Object_Constructor>(
1396  class_name,
1397  dispatch::make_dynamic_proxy_function(
1398  [engine, t_param_names, node = m_body_node](const Function_Params &t_params) {
1399  return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params);
1400  },
1401  static_cast<int>(numparams),
1402  m_body_node,
1403  param_types,
1404  guard)),
1405  function_name);
1406 
1407  } else {
1408  // if the type is unknown, then this generates a function that looks up the type
1409  // at runtime. Defining the type first before this is called is better
1410  auto type = t_ss->get_type(class_name, false);
1411  param_types.push_front(class_name, type);
1412 
1413  t_ss->add(std::make_shared<dispatch::detail::Dynamic_Object_Function>(
1414  class_name,
1415  dispatch::make_dynamic_proxy_function(
1416  [engine, t_param_names, node = m_body_node](const Function_Params &t_params) {
1417  return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params);
1418  },
1419  static_cast<int>(numparams),
1420  m_body_node,
1421  param_types,
1422  guard),
1423  type),
1424  function_name);
1425  }
1426  } catch (const exception::name_conflict_error &e) {
1427  throw exception::eval_error("Method redefined '" + e.name() + "'");
1428  }
1429  return void_var();
1430  }
1431  };
1432 
1433  template<typename T>
1434  struct Attr_Decl_AST_Node final : AST_Node_Impl<T> {
1435  Attr_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1436  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Attr_Decl, std::move(t_loc), std::move(t_children)) {
1437  }
1438 
1439  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1440  std::string class_name = this->children[0]->text;
1441 
1442  try {
1443  std::string attr_name = this->children[1]->text;
1444 
1445  t_ss->add(std::make_shared<dispatch::detail::Dynamic_Object_Function>(std::move(class_name),
1446  fun([attr_name](dispatch::Dynamic_Object &t_obj) {
1447  return t_obj.get_attr(attr_name);
1448  }),
1449  true
1450 
1451  ),
1452  this->children[1]->text);
1453  } catch (const exception::name_conflict_error &e) {
1454  throw exception::eval_error("Attribute redefined '" + e.name() + "'");
1455  }
1456  return void_var();
1457  }
1458  };
1459 
1460  template<typename T>
1462  Logical_And_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1463  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Logical_And, std::move(t_loc), std::move(t_children)) {
1464  assert(this->children.size() == 2);
1465  }
1466 
1467  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1468  return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)
1469  && this->get_bool_condition(this->children[1]->eval(t_ss), t_ss));
1470  }
1471  };
1472 
1473  template<typename T>
1475  Logical_Or_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
1476  : AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Logical_Or, std::move(t_loc), std::move(t_children)) {
1477  assert(this->children.size() == 2);
1478  }
1479 
1480  Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
1481  return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)
1482  || this->get_bool_condition(this->children[1]->eval(t_ss), t_ss));
1483  }
1484  };
1485  } // namespace eval
1486 
1487 } // namespace chaiscript
1488 #endif /* CHAISCRIPT_EVAL_HPP_ */
void add(const Type_Conversion &d)
Add a new conversion for upcasting to a base class.
Definition: dispatchkit.hpp:381
Exception thrown in the case that an object name is invalid because it already exists in current cont...
Definition: dispatchkit.hpp:97
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override
Definition: chaiscript_eval.hpp:417
Definition: chaiscript_eval.hpp:276
Definition: chaiscript_eval.hpp:1339
Compile time deduced information about a type.
Definition: type_info.hpp:27
Creates a new scope then pops it on destruction.
Definition: chaiscript_common.hpp:681
Special type indicating a call to &#39;continue&#39;.
Definition: chaiscript_common.hpp:639
Boxed_Value const_var(const T &t)
Takes an object and returns an immutable Boxed_Value.
Definition: boxed_value.hpp:336
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override
Definition: chaiscript_eval.hpp:863
Definition: chaiscript_eval.hpp:365
Creates a new function call and pops it on destruction.
Definition: chaiscript_common.hpp:661
Definition: chaiscript_eval.hpp:822
Definition: chaiscript_eval.hpp:969
Definition: chaiscript_eval.hpp:710
Definition: chaiscript_eval.hpp:1346
decltype(auto) boxed_cast(const Boxed_Value &bv, const Type_Conversions_State *t_conversions=nullptr)
Function for extracting a value stored in a Boxed_Value object.
Definition: boxed_cast.hpp:71
Exception thrown if a function&#39;s guard fails.
Definition: proxy_functions.hpp:285
Namespace chaiscript contains every API call that the average user will be concerned with...
Definition: function_params.hpp:16
Definition: chaiscript_eval.hpp:44
Definition: chaiscript_eval.hpp:1139
Definition: chaiscript_eval.hpp:498
Definition: chaiscript_eval.hpp:511
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override
Definition: chaiscript_eval.hpp:827
Definition: chaiscript_eval.hpp:940
Definition: chaiscript_eval.hpp:1203
std::pair< size_t, std::shared_ptr< std::vector< Proxy_Function > > > get_function(std::string_view t_name, const size_t t_hint) const
Return a function by name.
Definition: dispatchkit.hpp:612
Definition: chaiscript_eval.hpp:157
Pure virtual base class for all Proxy_Function implementations Proxy_Functions are a type erasure of ...
Definition: proxy_functions.hpp:175
AST_Node_Type
Types of AST nodes available to the parser and eval.
Definition: chaiscript_common.hpp:66
Definition: chaiscript_eval.hpp:553
A wrapper for holding any valid C++ type.
Definition: boxed_value.hpp:24
Definition: chaiscript_eval.hpp:259
Definition: chaiscript_eval.hpp:410
Thrown in the event that a Boxed_Value cannot be cast to the desired type.
Definition: bad_boxed_cast.hpp:31
Definition: chaiscript_eval.hpp:1461
Definition: chaiscript_eval.hpp:576
Definition: proxy_functions.hpp:48
Definition: chaiscript_eval.hpp:214
Definition: chaiscript_eval.hpp:1028
Definition: chaiscript_eval.hpp:1113
Definition: chaiscript_eval.hpp:358
Definition: chaiscript_eval.hpp:693
Boxed_Value dispatch(const Funcs &funcs, const Function_Params &plist, const Type_Conversions_State &t_conversions)
Take a vector of functions and a vector of parameters.
Definition: proxy_functions.hpp:779
Creates a new scope then pops it on destruction.
Definition: chaiscript_common.hpp:643
Definition: chaiscript_eval.hpp:857
Special type indicating a call to &#39;break&#39;.
Definition: chaiscript_common.hpp:635
Definition: chaiscript_eval.hpp:294
Definition: chaiscript_eval.hpp:1098
Definition: chaiscript_eval.hpp:1194
Struct that doubles as both a parser ast_node and an AST node.
Definition: chaiscript_common.hpp:174
Definition: chaiscript_eval.hpp:1217
Boxed_Value add_global_no_throw(Boxed_Value obj, std::string name)
Adds a new global (non-const) shared object, between all the threads.
Definition: dispatchkit.hpp:448
std::shared_ptr< const dispatch::Proxy_Function_Base > Const_Proxy_Function
Const version of Proxy_Function.
Definition: proxy_functions.hpp:281
Definition: chaiscript_common.hpp:151
Classes which may be thrown during error cases when ChaiScript is executing.
Definition: bad_boxed_cast.hpp:25
Exception thrown when there is a mismatch in number of parameters during Proxy_Function execution...
Definition: proxy_functions_detail.hpp:38
const StackData & get_stack_data() const noexcept
Returns the current stack make const/non const versions.
Definition: dispatchkit.hpp:1022
Definition: chaiscript_eval.hpp:841
Definition: chaiscript_eval.hpp:1474
Definition: chaiscript_eval.hpp:1185
Definition: chaiscript_eval.hpp:1012
Type_Info get_type(std::string_view name, bool t_throw=true) const
Returns the type info for a named type.
Definition: dispatchkit.hpp:563
Special type for returned values.
Definition: chaiscript_common.hpp:630
Definition: dynamic_object.hpp:38
Definition: chaiscript_eval.hpp:1044
decltype(auto) boxed_cast(const Boxed_Value &bv) const
casts an object while applying any Dynamic_Conversion available
Definition: dispatchkit.hpp:375
Definition: chaiscript_eval.hpp:795
Definition: chaiscript_eval.hpp:1434
Definition: chaiscript_eval.hpp:1224
Definition: chaiscript_eval.hpp:1231
Definition: chaiscript_eval.hpp:347
Definition: dispatchkit.hpp:1166
Exception thrown in the case that a method dispatch fails because no matching function was found...
Definition: proxy_functions.hpp:667
Definition: chaiscript_eval.hpp:631
Definition: chaiscript_eval.hpp:1332
Errors generated during parsing or evaluation.
Definition: chaiscript_common.hpp:301
Definition: chaiscript_eval.hpp:1153
Proxy_Function fun(T &&t)
Creates a new Proxy_Function object from a free function, member function or data member...
Definition: register_function.hpp:81
static bool type_match(const Boxed_Value &l, const Boxed_Value &r) noexcept
Definition: boxed_value.hpp:237
Definition: chaiscript_eval.hpp:1069
Main class for the dispatchkit.
Definition: dispatchkit.hpp:354
Definition: boxed_number.hpp:29
Definition: chaiscript_eval.hpp:1252
Definition: chaiscript_eval.hpp:530
Definition: chaiscript_eval.hpp:678