mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-04-30 19:09:26 +08:00
* Fix #677: Add strong typedefs via 'using Type = BaseType' syntax Strong typedefs create distinct types backed by Dynamic_Object, so 'using Meters = int' makes Meters a type that is not interchangeable with int or other typedefs of int. The constructor Meters(val) wraps the base value, and function dispatch enforces the type distinction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review: add to_underlying function for strong typedefs Registers a to_underlying() function for each strong typedef that returns the wrapped base value from the Dynamic_Object's __value attr. Requested by @lefticus in PR #680 review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review: expose strongly-typed operators for strong typedefs Register forwarding binary operators at typedef creation time via a custom Proxy_Function_Base subclass (Strong_Typedef_Binary_Op). Each operator unwraps __value from both operands, dispatches on the underlying types, and re-wraps arithmetic results in the typedef. Comparison operators return the raw bool. - Arithmetic: +, -, *, /, % → Meters + Meters -> Meters - Comparison: <, >, <=, >=, ==, != → Meters < Meters -> bool - Operators that don't exist on the base type error at call time (e.g. StrongString * StrongString -> error) - Users can extend typedefs with their own operations using to_underlying() for unwrapping Tests cover int-based arithmetic, string-based concatenation, string multiplication error, comparison ops, type safety of results, and user-defined operator extensions. Requested by @lefticus in PR #680 review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review: conditionally register operators based on underlying type support Only register strong typedef operators that actually exist for the underlying type. Previously all operators were added unconditionally, causing confusing reflection entries (e.g. * for StrongString) that would fail at runtime. Now each operator is probed via call_match against default-constructed base type values before registration. Also adds bitwise/shift operators (&, |, ^, <<, >>) for types that support them, and expands test coverage for unsupported operator rejection. Requested by @lefticus in PR #680 review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review: register all operators unconditionally and add compound assignment operators Remove conditional operator registration (op_exists_for_base_type check) since users could add underlying operators later, and the runtime check was expensive. Operators that fail on the underlying type now error at call time instead of being absent. Add compound assignment operators (*=, +=, -=, /=, %=, <<=, >>=, &=, |=, ^=) via Strong_Typedef_Compound_Assign_Op which computes the base operation and stores the result back in __value. Requested by @lefticus in PR #680 review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Merge upstream/develop into fix/issue-677-add-strong-typedefs Resolve merge conflicts with ChaiScript:develop. Upstream added nested namespace support (#675), grammar railroad diagrams (#673), and WASM exception support (#689). Conflicts in chaiscript_common.hpp, chaiscript_eval.hpp, and chaiscript_parser.hpp resolved by keeping both Using and Namespace_Block AST node types. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review: add strong typedef documentation to cheatsheet Add a new "Strong Typedefs" section to the cheatsheet covering: - Basic usage with `using Type = BaseType` syntax - Arithmetic and comparison operator forwarding - String-based strong typedefs - Accessing the underlying value via to_underlying - Extending strong typedefs with custom operations Requested by @lefticus in PR #680 review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: leftibot <leftibot@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1974 lines
85 KiB
C++
1974 lines
85 KiB
C++
// This file is distributed under the BSD License.
|
|
// See "license.txt" for details.
|
|
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
|
|
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
|
|
// http://www.chaiscript.com
|
|
|
|
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
|
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
|
|
|
#ifndef CHAISCRIPT_EVAL_HPP_
|
|
#define CHAISCRIPT_EVAL_HPP_
|
|
|
|
#include <exception>
|
|
#include <functional>
|
|
#include <limits>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <ostream>
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "../chaiscript_defines.hpp"
|
|
#include "../dispatchkit/boxed_cast.hpp"
|
|
#include "../dispatchkit/boxed_number.hpp"
|
|
#include "../dispatchkit/boxed_value.hpp"
|
|
#include "../dispatchkit/dispatchkit.hpp"
|
|
#include "../dispatchkit/dynamic_object_detail.hpp"
|
|
#include "../dispatchkit/proxy_functions.hpp"
|
|
#include "../dispatchkit/proxy_functions_detail.hpp"
|
|
#include "../dispatchkit/register_function.hpp"
|
|
#include "../dispatchkit/type_info.hpp"
|
|
#include "chaiscript_algebraic.hpp"
|
|
#include "chaiscript_common.hpp"
|
|
|
|
namespace chaiscript::exception {
|
|
class bad_boxed_cast;
|
|
} // namespace chaiscript::exception
|
|
|
|
namespace chaiscript {
|
|
/// \brief Classes and functions that are part of the runtime eval system
|
|
namespace eval {
|
|
template<typename T>
|
|
struct AST_Node_Impl;
|
|
|
|
template<typename T>
|
|
using AST_Node_Impl_Ptr = typename std::unique_ptr<AST_Node_Impl<T>>;
|
|
|
|
namespace detail {
|
|
/// Helper function that will set up the scope around a function call, including handling the named function parameters
|
|
template<typename T>
|
|
Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss,
|
|
const AST_Node_Impl<T> &t_node,
|
|
const std::vector<std::string> &t_param_names,
|
|
const Function_Params &t_vals,
|
|
const std::map<std::string, Boxed_Value> *t_locals = nullptr,
|
|
bool has_this_capture = false) {
|
|
chaiscript::detail::Dispatch_State state(t_ss);
|
|
|
|
const Boxed_Value *thisobj = [&]() -> const Boxed_Value * {
|
|
if (auto &stack = t_ss.get_stack_data(state.stack_holder()).back(); !stack.empty() && stack.back().first == "__this") {
|
|
return &stack.back().second;
|
|
} else if (!t_vals.empty()) {
|
|
return &t_vals[0];
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}();
|
|
|
|
chaiscript::eval::detail::Stack_Push_Pop tpp(state);
|
|
if (thisobj && !has_this_capture) {
|
|
state.add_object("this", *thisobj);
|
|
}
|
|
|
|
if (t_locals) {
|
|
for (const auto &[name, value] : *t_locals) {
|
|
state.add_object(name, value);
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < t_param_names.size(); ++i) {
|
|
if (t_param_names[i] != "this") {
|
|
state.add_object(t_param_names[i], t_vals[i]);
|
|
}
|
|
}
|
|
|
|
try {
|
|
return t_node.eval(state);
|
|
} catch (detail::Return_Value &rv) {
|
|
return std::move(rv.retval);
|
|
}
|
|
}
|
|
|
|
inline Boxed_Value clone_if_necessary(Boxed_Value incoming, std::atomic_uint_fast32_t &t_loc, const chaiscript::detail::Dispatch_State &t_ss) {
|
|
if (!incoming.is_return_value()) {
|
|
if (incoming.get_type_info().is_arithmetic()) {
|
|
return Boxed_Number::clone(incoming);
|
|
} else if (incoming.get_type_info().bare_equal_type_info(typeid(bool))) {
|
|
return Boxed_Value(*static_cast<const bool *>(incoming.get_const_ptr()));
|
|
} else if (incoming.get_type_info().bare_equal_type_info(typeid(std::string))) {
|
|
return Boxed_Value(*static_cast<const std::string *>(incoming.get_const_ptr()));
|
|
} else {
|
|
std::array<Boxed_Value, 1> params{std::move(incoming)};
|
|
return t_ss->call_function("clone", t_loc, Function_Params{params}, t_ss.conversions());
|
|
}
|
|
} else {
|
|
incoming.reset_return_value();
|
|
return incoming;
|
|
}
|
|
}
|
|
class Strong_Typedef_Binary_Op final : public dispatch::Proxy_Function_Base {
|
|
public:
|
|
Strong_Typedef_Binary_Op(
|
|
std::string t_type_name,
|
|
std::string t_op_name,
|
|
Operators::Opers t_oper,
|
|
bool t_rewrap,
|
|
chaiscript::detail::Dispatch_Engine &t_engine)
|
|
: Proxy_Function_Base(
|
|
{chaiscript::detail::Get_Type_Info<Boxed_Value>::get(),
|
|
user_type<dispatch::Dynamic_Object>(),
|
|
user_type<dispatch::Dynamic_Object>()},
|
|
2)
|
|
, m_type_name(std::move(t_type_name))
|
|
, m_op_name(std::move(t_op_name))
|
|
, m_oper(t_oper)
|
|
, m_rewrap(t_rewrap)
|
|
, m_engine(t_engine) {
|
|
}
|
|
|
|
bool operator==(const Proxy_Function_Base &f) const noexcept override {
|
|
if (const auto *other = dynamic_cast<const Strong_Typedef_Binary_Op *>(&f)) {
|
|
return m_type_name == other->m_type_name && m_op_name == other->m_op_name;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override {
|
|
return vals.size() == 2
|
|
&& type_matches(vals[0], t_conversions)
|
|
&& type_matches(vals[1], t_conversions);
|
|
}
|
|
|
|
protected:
|
|
Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override {
|
|
if (!call_match(params, t_conversions)) {
|
|
throw chaiscript::exception::guard_error();
|
|
}
|
|
|
|
const auto &lhs = boxed_cast<const dispatch::Dynamic_Object &>(params[0], &t_conversions);
|
|
const auto &rhs = boxed_cast<const dispatch::Dynamic_Object &>(params[1], &t_conversions);
|
|
const auto lhs_val = lhs.get_attr("__value");
|
|
const auto rhs_val = rhs.get_attr("__value");
|
|
|
|
Boxed_Value result;
|
|
if (m_oper != Operators::Opers::invalid
|
|
&& lhs_val.get_type_info().is_arithmetic()
|
|
&& rhs_val.get_type_info().is_arithmetic()) {
|
|
result = Boxed_Number::do_oper(m_oper, lhs_val, rhs_val);
|
|
} else {
|
|
std::array<Boxed_Value, 2> underlying_params{lhs_val, rhs_val};
|
|
result = m_engine.call_function(m_op_name, m_loc, Function_Params(underlying_params), t_conversions);
|
|
}
|
|
|
|
if (m_rewrap) {
|
|
auto bv = Boxed_Value(dispatch::Dynamic_Object(m_type_name), true);
|
|
auto *obj = static_cast<dispatch::Dynamic_Object *>(bv.get_ptr());
|
|
obj->get_attr("__value") = result;
|
|
return bv;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
bool type_matches(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const noexcept {
|
|
if (!bv.get_type_info().bare_equal(user_type<dispatch::Dynamic_Object>())) {
|
|
return false;
|
|
}
|
|
try {
|
|
const auto &d = boxed_cast<const dispatch::Dynamic_Object &>(bv, &t_conversions);
|
|
return d.get_type_name() == m_type_name;
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string m_type_name;
|
|
std::string m_op_name;
|
|
Operators::Opers m_oper;
|
|
bool m_rewrap;
|
|
chaiscript::detail::Dispatch_Engine &m_engine;
|
|
mutable std::atomic_uint_fast32_t m_loc{0};
|
|
};
|
|
|
|
class Strong_Typedef_Compound_Assign_Op final : public dispatch::Proxy_Function_Base {
|
|
public:
|
|
Strong_Typedef_Compound_Assign_Op(
|
|
std::string t_type_name,
|
|
std::string t_op_name,
|
|
Operators::Opers t_base_oper,
|
|
std::string t_base_op_name,
|
|
chaiscript::detail::Dispatch_Engine &t_engine)
|
|
: Proxy_Function_Base(
|
|
{user_type<dispatch::Dynamic_Object>(),
|
|
user_type<dispatch::Dynamic_Object>(),
|
|
user_type<dispatch::Dynamic_Object>()},
|
|
2)
|
|
, m_type_name(std::move(t_type_name))
|
|
, m_op_name(std::move(t_op_name))
|
|
, m_base_oper(t_base_oper)
|
|
, m_base_op_name(std::move(t_base_op_name))
|
|
, m_engine(t_engine) {
|
|
}
|
|
|
|
bool operator==(const Proxy_Function_Base &f) const noexcept override {
|
|
if (const auto *other = dynamic_cast<const Strong_Typedef_Compound_Assign_Op *>(&f)) {
|
|
return m_type_name == other->m_type_name && m_op_name == other->m_op_name;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override {
|
|
return vals.size() == 2
|
|
&& type_matches(vals[0], t_conversions)
|
|
&& type_matches(vals[1], t_conversions);
|
|
}
|
|
|
|
protected:
|
|
Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override {
|
|
if (!call_match(params, t_conversions)) {
|
|
throw chaiscript::exception::guard_error();
|
|
}
|
|
|
|
auto &lhs = boxed_cast<dispatch::Dynamic_Object &>(params[0], &t_conversions);
|
|
const auto &rhs = boxed_cast<const dispatch::Dynamic_Object &>(params[1], &t_conversions);
|
|
const auto lhs_val = lhs.get_attr("__value");
|
|
const auto rhs_val = rhs.get_attr("__value");
|
|
|
|
Boxed_Value result;
|
|
if (m_base_oper != Operators::Opers::invalid
|
|
&& lhs_val.get_type_info().is_arithmetic()
|
|
&& rhs_val.get_type_info().is_arithmetic()) {
|
|
result = Boxed_Number::do_oper(m_base_oper, lhs_val, rhs_val);
|
|
} else {
|
|
std::array<Boxed_Value, 2> underlying_params{lhs_val, rhs_val};
|
|
result = m_engine.call_function(m_base_op_name, m_loc, Function_Params(underlying_params), t_conversions);
|
|
}
|
|
|
|
lhs.get_attr("__value") = result;
|
|
return params[0];
|
|
}
|
|
|
|
private:
|
|
bool type_matches(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const noexcept {
|
|
if (!bv.get_type_info().bare_equal(user_type<dispatch::Dynamic_Object>())) {
|
|
return false;
|
|
}
|
|
try {
|
|
const auto &d = boxed_cast<const dispatch::Dynamic_Object &>(bv, &t_conversions);
|
|
return d.get_type_name() == m_type_name;
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string m_type_name;
|
|
std::string m_op_name;
|
|
Operators::Opers m_base_oper;
|
|
std::string m_base_op_name;
|
|
chaiscript::detail::Dispatch_Engine &m_engine;
|
|
mutable std::atomic_uint_fast32_t m_loc{0};
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
template<typename T>
|
|
struct AST_Node_Impl : AST_Node {
|
|
AST_Node_Impl(std::string t_ast_node_text,
|
|
AST_Node_Type t_id,
|
|
Parse_Location t_loc,
|
|
std::vector<AST_Node_Impl_Ptr<T>> t_children = std::vector<AST_Node_Impl_Ptr<T>>())
|
|
: AST_Node(std::move(t_ast_node_text), t_id, std::move(t_loc))
|
|
, children(std::move(t_children)) {
|
|
}
|
|
|
|
static bool get_scoped_bool_condition(const AST_Node_Impl<T> &node, const chaiscript::detail::Dispatch_State &t_ss) {
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
return get_bool_condition(node.eval(t_ss), t_ss);
|
|
}
|
|
|
|
std::vector<std::reference_wrapper<AST_Node>> get_children() const final {
|
|
std::vector<std::reference_wrapper<AST_Node>> retval;
|
|
retval.reserve(children.size());
|
|
for (auto &child : children) {
|
|
retval.emplace_back(*child);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const final {
|
|
try {
|
|
T::trace(t_e, this);
|
|
return eval_internal(t_e);
|
|
} catch (exception::eval_error &ee) {
|
|
ee.call_stack.push_back(*this);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
std::vector<AST_Node_Impl_Ptr<T>> children;
|
|
|
|
protected:
|
|
virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const {
|
|
throw std::runtime_error("Undispatched ast_node (internal error)");
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Compiled_AST_Node : AST_Node_Impl<T> {
|
|
Compiled_AST_Node(AST_Node_Impl_Ptr<T> t_original_node,
|
|
std::vector<AST_Node_Impl_Ptr<T>> t_children,
|
|
std::function<Boxed_Value(const std::vector<AST_Node_Impl_Ptr<T>> &, const chaiscript::detail::Dispatch_State &t_ss)> t_func)
|
|
: AST_Node_Impl<T>(t_original_node->text, AST_Node_Type::Compiled, t_original_node->location, std::move(t_children))
|
|
, m_func(std::move(t_func))
|
|
, m_original_node(std::move(t_original_node)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { return m_func(this->children, t_ss); }
|
|
|
|
std::function<Boxed_Value(const std::vector<AST_Node_Impl_Ptr<T>> &, const chaiscript::detail::Dispatch_State &t_ss)> m_func;
|
|
AST_Node_Impl_Ptr<T> m_original_node;
|
|
};
|
|
|
|
template<typename T>
|
|
struct Fold_Right_Binary_Operator_AST_Node : AST_Node_Impl<T> {
|
|
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)
|
|
: AST_Node_Impl<T>(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children))
|
|
, m_oper(Operators::to_operator(t_oper))
|
|
, m_rhs(std::move(t_rhs)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
return do_oper(t_ss, this->text, this->children[0]->eval(t_ss));
|
|
}
|
|
|
|
protected:
|
|
Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss, const std::string &t_oper_string, const Boxed_Value &t_lhs) const {
|
|
try {
|
|
if (t_lhs.get_type_info().is_arithmetic()) {
|
|
// If it's an arithmetic operation we want to short circuit dispatch
|
|
try {
|
|
return Boxed_Number::do_oper(m_oper, t_lhs, m_rhs);
|
|
} catch (const chaiscript::exception::arithmetic_error &) {
|
|
throw;
|
|
} catch (...) {
|
|
throw exception::eval_error("Error with numeric operator calling: " + t_oper_string);
|
|
}
|
|
} else {
|
|
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
|
|
std::array<Boxed_Value, 2> params{t_lhs, m_rhs};
|
|
fpp.save_params(Function_Params{params});
|
|
return t_ss->call_function(t_oper_string, m_loc, Function_Params{params}, t_ss.conversions());
|
|
}
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Operators::Opers m_oper;
|
|
Boxed_Value m_rhs;
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Binary_Operator_AST_Node : AST_Node_Impl<T> {
|
|
Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children))
|
|
, m_oper(Operators::to_operator(t_oper)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
auto lhs = this->children[0]->eval(t_ss);
|
|
auto rhs = this->children[1]->eval(t_ss);
|
|
return do_oper(t_ss, m_oper, this->text, lhs, rhs, m_loc);
|
|
}
|
|
|
|
// static and public so we can use this to process Switch_AST_Node case equality
|
|
static Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss,
|
|
Operators::Opers t_oper,
|
|
const std::string &t_oper_string,
|
|
const Boxed_Value &t_lhs,
|
|
const Boxed_Value &t_rhs,
|
|
std::atomic_uint_fast32_t &t_loc) {
|
|
try {
|
|
if (t_oper != Operators::Opers::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic()) {
|
|
// If it's an arithmetic operation we want to short circuit dispatch
|
|
try {
|
|
return Boxed_Number::do_oper(t_oper, t_lhs, t_rhs);
|
|
} catch (const chaiscript::exception::arithmetic_error &) {
|
|
throw;
|
|
} catch (...) {
|
|
throw exception::eval_error("Error with numeric operator calling: " + t_oper_string);
|
|
}
|
|
} else {
|
|
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
|
|
std::array<Boxed_Value, 2> params{t_lhs, t_rhs};
|
|
fpp.save_params(Function_Params(params));
|
|
return t_ss->call_function(t_oper_string, t_loc, Function_Params(params), t_ss.conversions());
|
|
}
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Operators::Opers m_oper;
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Constant_AST_Node final : AST_Node_Impl<T> {
|
|
Constant_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, Boxed_Value t_value)
|
|
: AST_Node_Impl<T>(t_ast_node_text, AST_Node_Type::Constant, std::move(t_loc))
|
|
, m_value(std::move(t_value)) {
|
|
}
|
|
|
|
explicit Constant_AST_Node(Boxed_Value t_value)
|
|
: AST_Node_Impl<T>("", AST_Node_Type::Constant, Parse_Location())
|
|
, m_value(std::move(t_value)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { return m_value; }
|
|
|
|
Boxed_Value m_value;
|
|
};
|
|
|
|
template<typename T>
|
|
struct Id_AST_Node final : AST_Node_Impl<T> {
|
|
Id_AST_Node(const std::string &t_ast_node_text, Parse_Location t_loc)
|
|
: AST_Node_Impl<T>(t_ast_node_text, AST_Node_Type::Id, std::move(t_loc)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
try {
|
|
return t_ss.get_object(this->text, m_loc);
|
|
} catch (std::exception &) {
|
|
throw exception::eval_error("Can not find object: " + this->text);
|
|
}
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Fun_Call_AST_Node : AST_Node_Impl<T> {
|
|
Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) {
|
|
assert(!this->children.empty());
|
|
}
|
|
|
|
template<bool Save_Params>
|
|
Boxed_Value do_eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const {
|
|
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
|
|
|
|
std::vector<Boxed_Value> params;
|
|
|
|
params.reserve(this->children[1]->children.size());
|
|
for (const auto &child : this->children[1]->children) {
|
|
params.push_back(child->eval(t_ss));
|
|
}
|
|
|
|
if (Save_Params) {
|
|
fpp.save_params(Function_Params{params});
|
|
}
|
|
|
|
Boxed_Value fn(this->children[0]->eval(t_ss));
|
|
|
|
try {
|
|
return (*t_ss->boxed_cast<const dispatch::Proxy_Function_Base *>(fn))(Function_Params{params}, t_ss.conversions());
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'",
|
|
e.parameters,
|
|
e.functions,
|
|
false,
|
|
*t_ss);
|
|
} catch (const exception::bad_boxed_cast &) {
|
|
try {
|
|
using ConstFunctionTypeRef = const Const_Proxy_Function &;
|
|
Const_Proxy_Function f = t_ss->boxed_cast<ConstFunctionTypeRef>(fn);
|
|
// handle the case where there is only 1 function to try to call and dispatch fails on it
|
|
throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, make_vector(f), false, *t_ss);
|
|
} catch (const exception::bad_boxed_cast &) {
|
|
throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function.");
|
|
}
|
|
} catch (const exception::arity_error &e) {
|
|
throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'");
|
|
} catch (const exception::guard_error &e) {
|
|
throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'");
|
|
} catch (detail::Return_Value &rv) {
|
|
return rv.retval;
|
|
}
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { return do_eval_internal<true>(t_ss); }
|
|
};
|
|
|
|
template<typename T>
|
|
struct Unused_Return_Fun_Call_AST_Node final : Fun_Call_AST_Node<T> {
|
|
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)
|
|
: Fun_Call_AST_Node<T>(std::move(t_ast_node_text), std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
return this->template do_eval_internal<false>(t_ss);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Arg_AST_Node final : AST_Node_Impl<T> {
|
|
Arg_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Arg_List_AST_Node final : AST_Node_Impl<T> {
|
|
Arg_List_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
static std::string get_arg_name(const AST_Node_Impl<T> &t_node) {
|
|
if (t_node.children.empty()) {
|
|
return t_node.text;
|
|
} else if (t_node.children.size() == 1) {
|
|
return t_node.children[0]->text;
|
|
} else {
|
|
return t_node.children[1]->text;
|
|
}
|
|
}
|
|
|
|
static std::vector<std::string> get_arg_names(const AST_Node_Impl<T> &t_node) {
|
|
std::vector<std::string> retval;
|
|
|
|
for (const auto &node : t_node.children) {
|
|
retval.push_back(get_arg_name(*node));
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static std::pair<std::string, Type_Info> get_arg_type(const AST_Node_Impl<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) {
|
|
if (t_node.children.size() < 2) {
|
|
return {};
|
|
} else {
|
|
return {t_node.children[0]->text, t_ss->get_type(t_node.children[0]->text, false)};
|
|
}
|
|
}
|
|
|
|
static dispatch::Param_Types get_arg_types(const AST_Node_Impl<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) {
|
|
std::vector<std::pair<std::string, Type_Info>> retval;
|
|
|
|
for (const auto &child : t_node.children) {
|
|
retval.push_back(get_arg_type(*child, t_ss));
|
|
}
|
|
|
|
return dispatch::Param_Types(std::move(retval));
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Equation_AST_Node final : AST_Node_Impl<T> {
|
|
Equation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Equation, std::move(t_loc), std::move(t_children))
|
|
, m_oper(Operators::to_operator(this->text)) {
|
|
assert(this->children.size() == 2);
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
|
|
|
|
auto params = [&]() {
|
|
// The RHS *must* be evaluated before the LHS
|
|
// consider `var range = range(x)`
|
|
// if we declare the variable in scope first, then the name lookup fails
|
|
// for the RHS
|
|
auto rhs = this->children[1]->eval(t_ss);
|
|
auto lhs = this->children[0]->eval(t_ss);
|
|
std::array<Boxed_Value, 2> p{std::move(lhs), std::move(rhs)};
|
|
return p;
|
|
}();
|
|
|
|
if (params[0].is_return_value()) {
|
|
throw exception::eval_error("Error, cannot assign to temporary value.");
|
|
} else if (params[0].is_const()) {
|
|
throw exception::eval_error("Error, cannot assign to constant value.");
|
|
}
|
|
|
|
if (m_oper != Operators::Opers::invalid && params[0].get_type_info().is_arithmetic() && params[1].get_type_info().is_arithmetic()) {
|
|
try {
|
|
return Boxed_Number::do_oper(m_oper, params[0], params[1]);
|
|
} catch (const chaiscript::exception::arithmetic_error &) {
|
|
throw;
|
|
} catch (const std::exception &) {
|
|
throw exception::eval_error("Error with unsupported arithmetic assignment operation.");
|
|
}
|
|
} else if (m_oper == Operators::Opers::assign) {
|
|
try {
|
|
if (params[0].is_undef()) {
|
|
if (!this->children.empty()
|
|
&& ((this->children[0]->identifier == AST_Node_Type::Reference)
|
|
|| (!this->children[0]->children.empty() && this->children[0]->children[0]->identifier == AST_Node_Type::Reference)))
|
|
|
|
{
|
|
/// \todo This does not handle the case of an unassigned reference variable
|
|
/// being assigned outside of its declaration
|
|
params[0].assign(params[1]);
|
|
params[0].reset_return_value();
|
|
return params[1];
|
|
} else {
|
|
params[1] = detail::clone_if_necessary(std::move(params[1]), m_clone_loc, t_ss);
|
|
}
|
|
}
|
|
|
|
try {
|
|
return t_ss->call_function(this->text, m_loc, Function_Params{params}, t_ss.conversions());
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss);
|
|
}
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error("Missing clone or copy constructor for right hand side of equation",
|
|
e.parameters,
|
|
e.functions,
|
|
false,
|
|
*t_ss);
|
|
}
|
|
} else if (this->text == ":=") {
|
|
if (params[0].is_undef() || Boxed_Value::type_match(params[0], params[1])) {
|
|
params[0].assign(params[1]);
|
|
params[0].reset_return_value();
|
|
} else {
|
|
throw exception::eval_error("Mismatched types in equation");
|
|
}
|
|
} else {
|
|
try {
|
|
return t_ss->call_function(this->text, m_loc, Function_Params{params}, t_ss.conversions());
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss);
|
|
}
|
|
}
|
|
|
|
return params[1];
|
|
}
|
|
|
|
private:
|
|
Operators::Opers m_oper;
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
mutable std::atomic_uint_fast32_t m_clone_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Global_Decl_AST_Node final : AST_Node_Impl<T> {
|
|
Global_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Global_Decl, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
const std::string &idname = (this->children[0]->identifier == AST_Node_Type::Reference) ? this->children[0]->children[0]->text : this->children[0]->text;
|
|
|
|
return t_ss->add_global_no_throw(Boxed_Value(), idname);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Var_Decl_AST_Node final : AST_Node_Impl<T> {
|
|
Var_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Var_Decl, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
const std::string &idname = this->children[0]->text;
|
|
|
|
try {
|
|
Boxed_Value bv;
|
|
t_ss.add_object(idname, bv);
|
|
return bv;
|
|
} catch (const exception::name_conflict_error &e) {
|
|
throw exception::eval_error("Variable redefined '" + e.name() + "'");
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Assign_Decl_AST_Node final : AST_Node_Impl<T> {
|
|
Assign_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Assign_Decl, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
const std::string &idname = this->children[0]->text;
|
|
|
|
try {
|
|
Boxed_Value bv(detail::clone_if_necessary(this->children[1]->eval(t_ss), m_loc, t_ss));
|
|
bv.reset_return_value();
|
|
t_ss.add_object(idname, bv);
|
|
return bv;
|
|
} catch (const exception::name_conflict_error &e) {
|
|
throw exception::eval_error("Variable redefined '" + e.name() + "'");
|
|
}
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Const_Var_Decl_AST_Node final : AST_Node_Impl<T> {
|
|
Const_Var_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Const_Var_Decl, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override {
|
|
throw exception::eval_error("const variables must be initialized at declaration");
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Const_Assign_Decl_AST_Node final : AST_Node_Impl<T> {
|
|
Const_Assign_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Const_Assign_Decl, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
const std::string &idname = this->children[0]->text;
|
|
|
|
try {
|
|
Boxed_Value bv(detail::clone_if_necessary(this->children[1]->eval(t_ss), m_loc, t_ss));
|
|
bv.reset_return_value();
|
|
bv.make_const();
|
|
t_ss.add_object(idname, bv);
|
|
return bv;
|
|
} catch (const exception::name_conflict_error &e) {
|
|
throw exception::eval_error("Variable redefined '" + e.name() + "'");
|
|
}
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Array_Call_AST_Node final : AST_Node_Impl<T> {
|
|
Array_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Array_Call, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
|
|
|
|
std::array<Boxed_Value, 2> params{this->children[0]->eval(t_ss), this->children[1]->eval(t_ss)};
|
|
|
|
try {
|
|
fpp.save_params(Function_Params{params});
|
|
return t_ss->call_function("[]", m_loc, Function_Params{params}, t_ss.conversions());
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss);
|
|
}
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Dot_Access_AST_Node final : AST_Node_Impl<T> {
|
|
Dot_Access_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Dot_Access, std::move(t_loc), std::move(t_children))
|
|
, m_fun_name(((this->children[1]->identifier == AST_Node_Type::Fun_Call) || (this->children[1]->identifier == AST_Node_Type::Array_Call))
|
|
? this->children[1]->children[0]->text
|
|
: this->children[1]->text) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
|
|
|
|
Boxed_Value retval = this->children[0]->eval(t_ss);
|
|
auto params = make_vector(retval);
|
|
|
|
bool has_function_params = false;
|
|
if (this->children[1]->children.size() > 1) {
|
|
has_function_params = true;
|
|
for (const auto &child : this->children[1]->children[1]->children) {
|
|
params.push_back(child->eval(t_ss));
|
|
}
|
|
}
|
|
|
|
fpp.save_params(Function_Params{params});
|
|
|
|
try {
|
|
retval = t_ss->call_member(m_fun_name, m_loc, Function_Params{params}, has_function_params, t_ss.conversions());
|
|
} catch (const exception::dispatch_error &e) {
|
|
if (e.functions.empty()) {
|
|
throw exception::eval_error("'" + m_fun_name + "' is not a function.");
|
|
} else {
|
|
throw exception::eval_error(std::string(e.what()) + " for function '" + m_fun_name + "'", e.parameters, e.functions, true, *t_ss);
|
|
}
|
|
} catch (detail::Return_Value &rv) {
|
|
retval = std::move(rv.retval);
|
|
}
|
|
|
|
if (this->children[1]->identifier == AST_Node_Type::Array_Call) {
|
|
try {
|
|
std::array<Boxed_Value, 2> p{retval, this->children[1]->children[1]->eval(t_ss)};
|
|
retval = t_ss->call_function("[]", m_array_loc, Function_Params{p}, t_ss.conversions());
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
mutable std::atomic_uint_fast32_t m_array_loc = {0};
|
|
const std::string m_fun_name;
|
|
};
|
|
|
|
template<typename T>
|
|
struct Lambda_AST_Node final : AST_Node_Impl<T> {
|
|
Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(t_ast_node_text,
|
|
AST_Node_Type::Lambda,
|
|
std::move(t_loc),
|
|
std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
|
|
std::make_move_iterator(std::prev(t_children.end()))))
|
|
, m_param_names(Arg_List_AST_Node<T>::get_arg_names(*this->children[1]))
|
|
, m_this_capture(has_this_capture(this->children[0]->children))
|
|
, m_lambda_node(std::move(t_children.back())) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
const auto captures = [&]() -> std::map<std::string, Boxed_Value> {
|
|
std::map<std::string, Boxed_Value> named_captures;
|
|
for (const auto &capture : this->children[0]->children) {
|
|
named_captures.insert(std::make_pair(capture->children[0]->text, capture->children[0]->eval(t_ss)));
|
|
}
|
|
return named_captures;
|
|
}();
|
|
|
|
const auto numparams = this->children[1]->children.size();
|
|
const auto param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[1], t_ss);
|
|
|
|
std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
|
|
|
|
return Boxed_Value(dispatch::make_dynamic_proxy_function(
|
|
[engine, lambda_node = this->m_lambda_node, param_names = this->m_param_names, captures, this_capture = this->m_this_capture](
|
|
const Function_Params &t_params) {
|
|
return detail::eval_function(engine, *lambda_node, param_names, t_params, &captures, this_capture);
|
|
},
|
|
static_cast<int>(numparams),
|
|
m_lambda_node,
|
|
param_types));
|
|
}
|
|
|
|
static bool has_this_capture(const std::vector<AST_Node_Impl_Ptr<T>> &t_children) noexcept {
|
|
return std::any_of(std::begin(t_children), std::end(t_children), [](const auto &child) { return child->children[0]->text == "this"; });
|
|
}
|
|
|
|
private:
|
|
const std::vector<std::string> m_param_names;
|
|
const bool m_this_capture = false;
|
|
const std::shared_ptr<AST_Node_Impl<T>> m_lambda_node;
|
|
};
|
|
|
|
template<typename T>
|
|
struct Scopeless_Block_AST_Node final : AST_Node_Impl<T> {
|
|
Scopeless_Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Scopeless_Block, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
const auto num_children = this->children.size();
|
|
for (size_t i = 0; i < num_children - 1; ++i) {
|
|
this->children[i]->eval(t_ss);
|
|
}
|
|
return this->children.back()->eval(t_ss);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Block_AST_Node final : AST_Node_Impl<T> {
|
|
Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Block, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
|
|
const auto num_children = this->children.size();
|
|
for (size_t i = 0; i < num_children - 1; ++i) {
|
|
this->children[i]->eval(t_ss);
|
|
}
|
|
return this->children.back()->eval(t_ss);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Def_AST_Node final : AST_Node_Impl<T> {
|
|
std::shared_ptr<AST_Node_Impl<T>> m_body_node;
|
|
std::shared_ptr<AST_Node_Impl<T>> m_guard_node;
|
|
|
|
Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text),
|
|
AST_Node_Type::Def,
|
|
std::move(t_loc),
|
|
std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
|
|
std::make_move_iterator(
|
|
std::prev(t_children.end(), has_guard(t_children, 1) ? 2 : 1))))
|
|
,
|
|
// This apparent use after move is safe because we are only moving out the specific elements we need
|
|
// on each operation.
|
|
m_body_node(get_body_node(std::move(t_children)))
|
|
, m_guard_node(get_guard_node(std::move(t_children), t_children.size() - this->children.size() == 2))
|
|
|
|
{
|
|
}
|
|
|
|
static std::shared_ptr<AST_Node_Impl<T>> get_guard_node(std::vector<AST_Node_Impl_Ptr<T>> &&vec, bool has_guard) {
|
|
if (has_guard) {
|
|
return std::move(*std::prev(vec.end(), 2));
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
static std::shared_ptr<AST_Node_Impl<T>> get_body_node(std::vector<AST_Node_Impl_Ptr<T>> &&vec) { return std::move(vec.back()); }
|
|
|
|
static bool has_guard(const std::vector<AST_Node_Impl_Ptr<T>> &t_children, const std::size_t offset) noexcept {
|
|
if ((t_children.size() > 2 + offset) && (t_children[1 + offset]->identifier == AST_Node_Type::Arg_List)) {
|
|
if (t_children.size() > 3 + offset) {
|
|
return true;
|
|
}
|
|
} else {
|
|
if (t_children.size() > 2 + offset) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static std::shared_ptr<dispatch::Proxy_Function_Base> make_proxy_function(
|
|
const Def_AST_Node<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) {
|
|
std::vector<std::string> t_param_names;
|
|
size_t numparams = 0;
|
|
|
|
dispatch::Param_Types param_types;
|
|
|
|
if ((t_node.children.size() > 1) && (t_node.children[1]->identifier == AST_Node_Type::Arg_List)) {
|
|
numparams = t_node.children[1]->children.size();
|
|
t_param_names = Arg_List_AST_Node<T>::get_arg_names(*t_node.children[1]);
|
|
param_types = Arg_List_AST_Node<T>::get_arg_types(*t_node.children[1], t_ss);
|
|
}
|
|
|
|
std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
|
|
std::shared_ptr<dispatch::Proxy_Function_Base> guard;
|
|
if (t_node.m_guard_node) {
|
|
guard = dispatch::make_dynamic_proxy_function(
|
|
[engine, guardnode = t_node.m_guard_node, t_param_names](const Function_Params &t_params) {
|
|
return detail::eval_function(engine, *guardnode, t_param_names, t_params);
|
|
},
|
|
static_cast<int>(numparams),
|
|
t_node.m_guard_node);
|
|
}
|
|
|
|
return dispatch::make_dynamic_proxy_function(
|
|
[engine, func_node = t_node.m_body_node, t_param_names](const Function_Params &t_params) {
|
|
return detail::eval_function(engine, *func_node, t_param_names, t_params);
|
|
},
|
|
static_cast<int>(numparams),
|
|
t_node.m_body_node,
|
|
param_types,
|
|
guard);
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
try {
|
|
t_ss->add(make_proxy_function(*this, t_ss), this->children[0]->text);
|
|
} catch (const exception::name_conflict_error &e) {
|
|
throw exception::eval_error("Function redefined '" + e.name() + "'");
|
|
}
|
|
return void_var();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct While_AST_Node final : AST_Node_Impl<T> {
|
|
While_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::While, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
|
|
try {
|
|
while (this->get_scoped_bool_condition(*this->children[0], t_ss)) {
|
|
try {
|
|
this->children[1]->eval(t_ss);
|
|
} catch (detail::Continue_Loop &) {
|
|
// we got a continue exception, which means all of the remaining
|
|
// loop implementation is skipped and we just need to continue to
|
|
// the next condition test
|
|
}
|
|
}
|
|
} catch (detail::Break_Loop &) {
|
|
// loop was broken intentionally
|
|
}
|
|
|
|
return void_var();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Class_AST_Node final : AST_Node_Impl<T> {
|
|
Class_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Class, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
|
|
const auto &class_name = this->children[0]->text;
|
|
|
|
/// \todo do this better
|
|
// put class name in current scope so it can be looked up by the attrs and methods
|
|
t_ss.add_object("_current_class_name", const_var(class_name));
|
|
|
|
const bool has_base_class = (this->children.size() == 3);
|
|
const auto &block = has_base_class ? this->children[2] : this->children[1];
|
|
|
|
// Register inheritance before evaluating the class body so that
|
|
// function dispatch ordering can account for the relationship
|
|
if (has_base_class) {
|
|
const auto &base_name = this->children[1]->text;
|
|
dispatch::Dynamic_Object::register_inheritance(class_name, base_name);
|
|
}
|
|
|
|
block->eval(t_ss);
|
|
|
|
return void_var();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Using_AST_Node final : AST_Node_Impl<T> {
|
|
Using_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Using, std::move(t_loc), std::move(t_children)) {
|
|
assert(this->children.size() == 2);
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
const auto &new_type_name = this->children[0]->text;
|
|
const auto &base_type_name = this->children[1]->text;
|
|
|
|
const auto base_type = t_ss->get_type(base_type_name, true);
|
|
|
|
t_ss->add(user_type<dispatch::Dynamic_Object>(), new_type_name);
|
|
|
|
dispatch::Param_Types param_types(std::vector<std::pair<std::string, Type_Info>>{
|
|
{new_type_name, Type_Info()},
|
|
{base_type_name, base_type}});
|
|
|
|
auto ctor_body = dispatch::make_dynamic_proxy_function(
|
|
[](const Function_Params &t_params) -> Boxed_Value {
|
|
auto *obj = static_cast<dispatch::Dynamic_Object *>(t_params[0].get_ptr());
|
|
obj->get_attr("__value") = t_params[1];
|
|
return void_var();
|
|
},
|
|
2,
|
|
std::shared_ptr<AST_Node>(),
|
|
param_types);
|
|
|
|
try {
|
|
t_ss->add(std::make_shared<dispatch::detail::Dynamic_Object_Constructor>(new_type_name, ctor_body), new_type_name);
|
|
} catch (const exception::name_conflict_error &e) {
|
|
throw exception::eval_error("Type alias redefined '" + e.name() + "'");
|
|
}
|
|
|
|
dispatch::Param_Types to_underlying_param_types(std::vector<std::pair<std::string, Type_Info>>{
|
|
{new_type_name, user_type<dispatch::Dynamic_Object>()}});
|
|
|
|
auto to_underlying_body = dispatch::make_dynamic_proxy_function(
|
|
[](const Function_Params &t_params) -> Boxed_Value {
|
|
const auto *obj = static_cast<const dispatch::Dynamic_Object *>(t_params[0].get_const_ptr());
|
|
return obj->get_attr("__value");
|
|
},
|
|
1,
|
|
std::shared_ptr<AST_Node>(),
|
|
to_underlying_param_types);
|
|
|
|
t_ss->add(to_underlying_body, "to_underlying");
|
|
|
|
auto &engine = *t_ss;
|
|
|
|
struct Op_Entry {
|
|
const char *name;
|
|
Operators::Opers oper;
|
|
bool rewrap;
|
|
};
|
|
|
|
static constexpr Op_Entry ops[] = {
|
|
{"+", Operators::Opers::sum, true},
|
|
{"-", Operators::Opers::difference, true},
|
|
{"*", Operators::Opers::product, true},
|
|
{"/", Operators::Opers::quotient, true},
|
|
{"%", Operators::Opers::remainder, true},
|
|
{"<<", Operators::Opers::shift_left, true},
|
|
{">>", Operators::Opers::shift_right, true},
|
|
{"&", Operators::Opers::bitwise_and, true},
|
|
{"|", Operators::Opers::bitwise_or, true},
|
|
{"^", Operators::Opers::bitwise_xor, true},
|
|
{"<", Operators::Opers::less_than, false},
|
|
{">", Operators::Opers::greater_than, false},
|
|
{"<=", Operators::Opers::less_than_equal, false},
|
|
{">=", Operators::Opers::greater_than_equal, false},
|
|
{"==", Operators::Opers::equals, false},
|
|
{"!=", Operators::Opers::not_equal, false},
|
|
};
|
|
|
|
for (const auto &op : ops) {
|
|
t_ss->add(
|
|
chaiscript::make_shared<dispatch::Proxy_Function_Base, detail::Strong_Typedef_Binary_Op>(
|
|
new_type_name, std::string(op.name), op.oper, op.rewrap, engine),
|
|
op.name);
|
|
}
|
|
|
|
struct Compound_Op_Entry {
|
|
const char *name;
|
|
Operators::Opers base_oper;
|
|
const char *base_op_name;
|
|
};
|
|
|
|
static constexpr Compound_Op_Entry compound_ops[] = {
|
|
{"+=", Operators::Opers::sum, "+"},
|
|
{"-=", Operators::Opers::difference, "-"},
|
|
{"*=", Operators::Opers::product, "*"},
|
|
{"/=", Operators::Opers::quotient, "/"},
|
|
{"%=", Operators::Opers::remainder, "%"},
|
|
{"<<=", Operators::Opers::shift_left, "<<"},
|
|
{">>=", Operators::Opers::shift_right, ">>"},
|
|
{"&=", Operators::Opers::bitwise_and, "&"},
|
|
{"|=", Operators::Opers::bitwise_or, "|"},
|
|
{"^=", Operators::Opers::bitwise_xor, "^"},
|
|
};
|
|
|
|
for (const auto &op : compound_ops) {
|
|
t_ss->add(
|
|
chaiscript::make_shared<dispatch::Proxy_Function_Base, detail::Strong_Typedef_Compound_Assign_Op>(
|
|
new_type_name, std::string(op.name), op.base_oper, std::string(op.base_op_name), engine),
|
|
op.name);
|
|
}
|
|
|
|
return void_var();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Enum_AST_Node final : AST_Node_Impl<T> {
|
|
Enum_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Enum, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
const auto &enum_name = this->children[0]->text;
|
|
const auto &underlying_type_name = this->children[1]->text;
|
|
const auto underlying_ti = t_ss->get_type(underlying_type_name);
|
|
|
|
dispatch::Dynamic_Object container(enum_name);
|
|
std::vector<Boxed_Value> valid_values;
|
|
|
|
for (size_t i = 2; i < this->children.size(); i += 2) {
|
|
const auto &val_name = this->children[i]->text;
|
|
const auto val_bv = Boxed_Number(this->children[i + 1]->eval(t_ss)).get_as(underlying_ti).bv;
|
|
valid_values.push_back(val_bv);
|
|
|
|
dispatch::Dynamic_Object dobj(enum_name);
|
|
dobj.get_attr("value") = val_bv;
|
|
dobj.set_explicit(true);
|
|
container[val_name] = const_var(dobj);
|
|
}
|
|
|
|
auto shared_valid = std::make_shared<const std::vector<Boxed_Value>>(std::move(valid_values));
|
|
|
|
container[enum_name] = var(
|
|
fun([shared_valid, enum_name, underlying_ti](const Boxed_Number &t_val) -> Boxed_Value {
|
|
const auto converted = t_val.get_as(underlying_ti);
|
|
for (const auto &v : *shared_valid) {
|
|
if (Boxed_Number::equals(Boxed_Number(v), converted)) {
|
|
dispatch::Dynamic_Object dobj(enum_name);
|
|
dobj.get_attr("value") = converted.bv;
|
|
dobj.set_explicit(true);
|
|
return const_var(dobj);
|
|
}
|
|
}
|
|
throw exception::eval_error("Value is not valid for enum '" + enum_name + "'");
|
|
}));
|
|
|
|
t_ss->add_global_const(const_var(container), enum_name);
|
|
|
|
t_ss->add(
|
|
std::make_shared<dispatch::detail::Dynamic_Object_Function>(
|
|
enum_name,
|
|
fun([](const dispatch::Dynamic_Object &lhs, const dispatch::Dynamic_Object &rhs) {
|
|
return Boxed_Number::equals(Boxed_Number(lhs.get_attr("value")), Boxed_Number(rhs.get_attr("value")));
|
|
})),
|
|
"==");
|
|
|
|
t_ss->add(
|
|
std::make_shared<dispatch::detail::Dynamic_Object_Function>(
|
|
enum_name,
|
|
fun([](const dispatch::Dynamic_Object &lhs, const dispatch::Dynamic_Object &rhs) {
|
|
return !Boxed_Number::equals(Boxed_Number(lhs.get_attr("value")), Boxed_Number(rhs.get_attr("value")));
|
|
})),
|
|
"!=");
|
|
|
|
t_ss->add(
|
|
std::make_shared<dispatch::detail::Dynamic_Object_Function>(
|
|
enum_name,
|
|
fun([](const dispatch::Dynamic_Object &obj) { return obj.get_attr("value"); })),
|
|
"to_underlying");
|
|
|
|
return void_var();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Namespace_Block_AST_Node final : AST_Node_Impl<T> {
|
|
Namespace_Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Namespace_Block, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
const auto &ns_name = this->children[0]->text;
|
|
|
|
auto ns_name_bv = const_var(ns_name);
|
|
t_ss->call_function("namespace", m_ns_loc, Function_Params{ns_name_bv}, t_ss.conversions());
|
|
|
|
std::vector<std::string> parts;
|
|
{
|
|
std::string::size_type start = 0;
|
|
std::string::size_type pos = 0;
|
|
while ((pos = ns_name.find("::", start)) != std::string::npos) {
|
|
parts.push_back(ns_name.substr(start, pos - start));
|
|
start = pos + 2;
|
|
}
|
|
parts.push_back(ns_name.substr(start));
|
|
}
|
|
|
|
Boxed_Value ns_bv = t_ss.get_object(parts[0], m_root_loc);
|
|
|
|
for (size_t i = 1; i < parts.size(); ++i) {
|
|
auto &parent_ns = boxed_cast<dispatch::Dynamic_Object &>(ns_bv);
|
|
ns_bv = parent_ns.get_attr(parts[i]);
|
|
}
|
|
|
|
auto &target_ns = boxed_cast<dispatch::Dynamic_Object &>(ns_bv);
|
|
|
|
const auto process_statement = [&](const AST_Node_Impl<T> &stmt) {
|
|
if (stmt.identifier == AST_Node_Type::Def) {
|
|
const auto &def_node = static_cast<const Def_AST_Node<T> &>(stmt);
|
|
target_ns[def_node.children[0]->text] =
|
|
Boxed_Value(Def_AST_Node<T>::make_proxy_function(def_node, t_ss));
|
|
} else if (stmt.identifier == AST_Node_Type::Assign_Decl
|
|
|| stmt.identifier == AST_Node_Type::Const_Assign_Decl) {
|
|
const auto &var_name = stmt.children[0]->text;
|
|
auto value = detail::clone_if_necessary(stmt.children[1]->eval(t_ss), m_clone_loc, t_ss);
|
|
value.reset_return_value();
|
|
if (stmt.identifier == AST_Node_Type::Const_Assign_Decl) {
|
|
value.make_const();
|
|
}
|
|
target_ns[var_name] = std::move(value);
|
|
} else if (stmt.identifier == AST_Node_Type::Equation
|
|
&& !stmt.children.empty()
|
|
&& (stmt.children[0]->identifier == AST_Node_Type::Var_Decl
|
|
|| stmt.children[0]->identifier == AST_Node_Type::Const_Var_Decl)) {
|
|
const auto &var_name = stmt.children[0]->children[0]->text;
|
|
auto value = detail::clone_if_necessary(stmt.children[1]->eval(t_ss), m_clone_loc, t_ss);
|
|
value.reset_return_value();
|
|
target_ns[var_name] = std::move(value);
|
|
} else if (stmt.identifier == AST_Node_Type::Var_Decl) {
|
|
const auto &var_name = stmt.children[0]->text;
|
|
target_ns[var_name] = Boxed_Value();
|
|
} else {
|
|
throw exception::eval_error("Only declarations (def, var, auto, global) are allowed inside namespace blocks");
|
|
}
|
|
};
|
|
|
|
const auto &body = this->children[1];
|
|
if (body->identifier == AST_Node_Type::Block
|
|
|| body->identifier == AST_Node_Type::Scopeless_Block) {
|
|
for (const auto &child : body->children) {
|
|
process_statement(*child);
|
|
}
|
|
} else {
|
|
process_statement(*body);
|
|
}
|
|
|
|
return void_var();
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic_uint_fast32_t m_ns_loc = {0};
|
|
mutable std::atomic_uint_fast32_t m_root_loc = {0};
|
|
mutable std::atomic_uint_fast32_t m_clone_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct If_AST_Node final : AST_Node_Impl<T> {
|
|
If_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::If, std::move(t_loc), std::move(t_children)) {
|
|
assert(this->children.size() == 3);
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
if (this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)) {
|
|
return this->children[1]->eval(t_ss);
|
|
} else {
|
|
return this->children[2]->eval(t_ss);
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Ranged_For_AST_Node final : AST_Node_Impl<T> {
|
|
Ranged_For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Ranged_For, std::move(t_loc), std::move(t_children)) {
|
|
assert(this->children.size() == 3);
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
const auto get_function = [&t_ss](const std::string &t_name, auto &t_hint) {
|
|
uint_fast32_t hint = t_hint;
|
|
auto [funs_loc, funs] = t_ss->get_function(t_name, hint);
|
|
if (funs_loc != hint) {
|
|
t_hint = uint_fast32_t(funs_loc);
|
|
}
|
|
return std::move(funs);
|
|
};
|
|
|
|
const auto call_function = [&t_ss](const auto &t_funcs, const Boxed_Value &t_param) {
|
|
return dispatch::dispatch(*t_funcs, Function_Params{t_param}, t_ss.conversions());
|
|
};
|
|
|
|
const std::string &loop_var_name = this->children[0]->text;
|
|
Boxed_Value range_expression_result = this->children[1]->eval(t_ss);
|
|
|
|
const auto do_loop = [&loop_var_name, &t_ss, this](const auto &ranged_thing) {
|
|
try {
|
|
for (auto &&loop_var : ranged_thing) {
|
|
// This scope push and pop might not be the best thing for perf
|
|
// but we know it's 100% correct
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
/// to-do make this if-constexpr with C++17 branch
|
|
if (!std::is_same<std::decay_t<decltype(loop_var)>, Boxed_Value>::value) {
|
|
t_ss.add_get_object(loop_var_name, Boxed_Value(std::ref(loop_var)));
|
|
} else {
|
|
t_ss.add_get_object(loop_var_name, Boxed_Value(loop_var));
|
|
}
|
|
try {
|
|
this->children[2]->eval(t_ss);
|
|
} catch (detail::Continue_Loop &) {
|
|
}
|
|
}
|
|
} catch (detail::Break_Loop &) {
|
|
// loop broken
|
|
}
|
|
return void_var();
|
|
};
|
|
|
|
if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::vector<Boxed_Value>))) {
|
|
return do_loop(boxed_cast<const std::vector<Boxed_Value> &>(range_expression_result));
|
|
} else if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::map<std::string, Boxed_Value>))) {
|
|
return do_loop(boxed_cast<const std::map<std::string, Boxed_Value> &>(range_expression_result));
|
|
} else {
|
|
const auto range_funcs = get_function("range", m_range_loc);
|
|
const auto empty_funcs = get_function("empty", m_empty_loc);
|
|
const auto front_funcs = get_function("front", m_front_loc);
|
|
const auto pop_front_funcs = get_function("pop_front", m_pop_front_loc);
|
|
|
|
try {
|
|
const auto range_obj = call_function(range_funcs, range_expression_result);
|
|
while (!boxed_cast<bool>(call_function(empty_funcs, range_obj))) {
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
t_ss.add_get_object(loop_var_name, call_function(front_funcs, range_obj));
|
|
try {
|
|
this->children[2]->eval(t_ss);
|
|
} catch (detail::Continue_Loop &) {
|
|
// continue statement hit
|
|
}
|
|
call_function(pop_front_funcs, range_obj);
|
|
}
|
|
} catch (detail::Break_Loop &) {
|
|
// loop broken
|
|
}
|
|
return void_var();
|
|
}
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic_uint_fast32_t m_range_loc = {0};
|
|
mutable std::atomic_uint_fast32_t m_empty_loc = {0};
|
|
mutable std::atomic_uint_fast32_t m_front_loc = {0};
|
|
mutable std::atomic_uint_fast32_t m_pop_front_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct For_AST_Node final : AST_Node_Impl<T> {
|
|
For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::For, std::move(t_loc), std::move(t_children)) {
|
|
assert(this->children.size() == 4);
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
|
|
try {
|
|
for (this->children[0]->eval(t_ss); this->get_scoped_bool_condition(*this->children[1], t_ss); this->children[2]->eval(t_ss)) {
|
|
try {
|
|
// Body of Loop
|
|
this->children[3]->eval(t_ss);
|
|
} catch (detail::Continue_Loop &) {
|
|
// we got a continue exception, which means all of the remaining
|
|
// loop implementation is skipped and we just need to continue to
|
|
// the next iteration step
|
|
}
|
|
}
|
|
} catch (detail::Break_Loop &) {
|
|
// loop broken
|
|
}
|
|
|
|
return void_var();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Switch_AST_Node final : AST_Node_Impl<T> {
|
|
Switch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Switch, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
bool breaking = false;
|
|
size_t currentCase = 1;
|
|
bool hasMatched = false;
|
|
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
|
|
Boxed_Value match_value(this->children[0]->eval(t_ss));
|
|
|
|
while (!breaking && (currentCase < this->children.size())) {
|
|
try {
|
|
if (this->children[currentCase]->identifier == AST_Node_Type::Case) {
|
|
// This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here.
|
|
try {
|
|
if (hasMatched || boxed_cast<bool>(Binary_Operator_AST_Node<T>::do_oper(t_ss, Operators::Opers::equals, "==", match_value, this->children[currentCase]->children[0]->eval(t_ss), m_loc))) {
|
|
this->children[currentCase]->eval(t_ss);
|
|
hasMatched = true;
|
|
}
|
|
} catch (const exception::bad_boxed_cast &) {
|
|
throw exception::eval_error("Internal error: case guard evaluation not boolean");
|
|
}
|
|
} else if (this->children[currentCase]->identifier == AST_Node_Type::Default) {
|
|
this->children[currentCase]->eval(t_ss);
|
|
hasMatched = true;
|
|
}
|
|
} catch (detail::Break_Loop &) {
|
|
breaking = true;
|
|
}
|
|
++currentCase;
|
|
}
|
|
return void_var();
|
|
}
|
|
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Case_AST_Node final : AST_Node_Impl<T> {
|
|
Case_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Case, std::move(t_loc), std::move(t_children)) {
|
|
assert(this->children.size() == 2); /* how many children does it have? */
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
|
|
this->children[1]->eval(t_ss);
|
|
|
|
return void_var();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Default_AST_Node final : AST_Node_Impl<T> {
|
|
Default_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Default, std::move(t_loc), std::move(t_children)) {
|
|
assert(this->children.size() == 1);
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
|
|
this->children[0]->eval(t_ss);
|
|
|
|
return void_var();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Inline_Array_AST_Node final : AST_Node_Impl<T> {
|
|
Inline_Array_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Inline_Array, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
try {
|
|
std::vector<Boxed_Value> vec;
|
|
if (!this->children.empty()) {
|
|
vec.reserve(this->children[0]->children.size());
|
|
for (const auto &child : this->children[0]->children) {
|
|
vec.push_back(detail::clone_if_necessary(child->eval(t_ss), m_loc, t_ss));
|
|
}
|
|
}
|
|
return const_var(std::move(vec));
|
|
} catch (const exception::dispatch_error &) {
|
|
throw exception::eval_error("Can not find appropriate 'clone' or copy constructor for vector elements");
|
|
}
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Inline_Map_AST_Node final : AST_Node_Impl<T> {
|
|
Inline_Map_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Inline_Map, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
try {
|
|
std::map<std::string, Boxed_Value> retval;
|
|
|
|
for (const auto &child : this->children[0]->children) {
|
|
retval.insert(std::make_pair(t_ss->boxed_cast<std::string>(child->children[0]->eval(t_ss)),
|
|
detail::clone_if_necessary(child->children[1]->eval(t_ss), m_loc, t_ss)));
|
|
}
|
|
|
|
return const_var(std::move(retval));
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error("Can not find appropriate copy constructor or 'clone' while inserting into Map.",
|
|
e.parameters,
|
|
e.functions,
|
|
false,
|
|
*t_ss);
|
|
}
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Return_AST_Node final : AST_Node_Impl<T> {
|
|
Return_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Return, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
if (!this->children.empty()) {
|
|
throw detail::Return_Value{this->children[0]->eval(t_ss)};
|
|
} else {
|
|
throw detail::Return_Value{void_var()};
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct File_AST_Node final : AST_Node_Impl<T> {
|
|
File_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::File, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
try {
|
|
const auto num_children = this->children.size();
|
|
|
|
if (num_children > 0) {
|
|
for (size_t i = 0; i < num_children - 1; ++i) {
|
|
this->children[i]->eval(t_ss);
|
|
}
|
|
return this->children.back()->eval(t_ss);
|
|
} else {
|
|
return void_var();
|
|
}
|
|
} catch (const detail::Continue_Loop &) {
|
|
throw exception::eval_error("Unexpected `continue` statement outside of a loop");
|
|
} catch (const detail::Break_Loop &) {
|
|
throw exception::eval_error("Unexpected `break` statement outside of a loop");
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Reference_AST_Node final : AST_Node_Impl<T> {
|
|
Reference_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Reference, std::move(t_loc), std::move(t_children)) {
|
|
assert(this->children.size() == 1);
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
Boxed_Value bv;
|
|
t_ss.add_object(this->children[0]->text, bv);
|
|
return bv;
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Prefix_AST_Node final : AST_Node_Impl<T> {
|
|
Prefix_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Prefix, std::move(t_loc), std::move(t_children))
|
|
, m_oper(Operators::to_operator(this->text, true)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
Boxed_Value bv(this->children[0]->eval(t_ss));
|
|
|
|
try {
|
|
// short circuit arithmetic operations
|
|
if (m_oper != Operators::Opers::invalid && m_oper != Operators::Opers::bitwise_and && bv.get_type_info().is_arithmetic()) {
|
|
if ((m_oper == Operators::Opers::pre_increment || m_oper == Operators::Opers::pre_decrement) && bv.is_const()) {
|
|
throw exception::eval_error("Error with prefix operator evaluation: cannot modify constant value.");
|
|
}
|
|
return Boxed_Number::do_oper(m_oper, bv);
|
|
} else {
|
|
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
|
|
fpp.save_params(Function_Params{bv});
|
|
return t_ss->call_function(this->text, m_loc, Function_Params{bv}, t_ss.conversions());
|
|
}
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error("Error with prefix operator evaluation: '" + this->text + "'", e.parameters, e.functions, false, *t_ss);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Operators::Opers m_oper = Operators::Opers::invalid;
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Break_AST_Node final : AST_Node_Impl<T> {
|
|
Break_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Break, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { throw detail::Break_Loop(); }
|
|
};
|
|
|
|
template<typename T>
|
|
struct Continue_AST_Node final : AST_Node_Impl<T> {
|
|
Continue_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Continue, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { throw detail::Continue_Loop(); }
|
|
};
|
|
|
|
template<typename T>
|
|
struct Noop_AST_Node final : AST_Node_Impl<T> {
|
|
Noop_AST_Node()
|
|
: AST_Node_Impl<T>("", AST_Node_Type::Noop, Parse_Location()) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override {
|
|
// It's a no-op, that evaluates to "void"
|
|
return val;
|
|
}
|
|
|
|
Boxed_Value val = void_var();
|
|
};
|
|
|
|
template<typename T>
|
|
struct Map_Pair_AST_Node final : AST_Node_Impl<T> {
|
|
Map_Pair_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Map_Pair, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Value_Range_AST_Node final : AST_Node_Impl<T> {
|
|
Value_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Value_Range, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Inline_Range_AST_Node final : AST_Node_Impl<T> {
|
|
Inline_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Inline_Range, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
try {
|
|
std::array<Boxed_Value, 2> params{this->children[0]->children[0]->children[0]->eval(t_ss),
|
|
this->children[0]->children[0]->children[1]->eval(t_ss)};
|
|
|
|
return t_ss->call_function("generate_range", m_loc, Function_Params{params}, t_ss.conversions());
|
|
} catch (const exception::dispatch_error &e) {
|
|
throw exception::eval_error("Unable to generate range vector, while calling 'generate_range'", e.parameters, e.functions, false, *t_ss);
|
|
}
|
|
}
|
|
|
|
private:
|
|
mutable std::atomic_uint_fast32_t m_loc = {0};
|
|
};
|
|
|
|
template<typename T>
|
|
struct Try_AST_Node final : AST_Node_Impl<T> {
|
|
Try_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Try, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value handle_exception(const chaiscript::detail::Dispatch_State &t_ss, const Boxed_Value &t_except) const {
|
|
Boxed_Value retval;
|
|
bool handled = false;
|
|
|
|
size_t end_point = this->children.size();
|
|
if (this->children.back()->identifier == AST_Node_Type::Finally) {
|
|
assert(end_point > 0);
|
|
end_point = this->children.size() - 1;
|
|
}
|
|
for (size_t i = 1; i < end_point; ++i) {
|
|
chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss);
|
|
auto &catch_block = *this->children[i];
|
|
|
|
if (catch_block.children.size() == 1) {
|
|
// No variable capture
|
|
retval = catch_block.children[0]->eval(t_ss);
|
|
handled = true;
|
|
break;
|
|
} else if (catch_block.children.size() == 2 || catch_block.children.size() == 3) {
|
|
const auto name = Arg_List_AST_Node<T>::get_arg_name(*catch_block.children[0]);
|
|
|
|
if (dispatch::Param_Types(
|
|
std::vector<std::pair<std::string, Type_Info>>{Arg_List_AST_Node<T>::get_arg_type(*catch_block.children[0], t_ss)})
|
|
.match(Function_Params{t_except}, t_ss.conversions())
|
|
.first) {
|
|
t_ss.add_object(name, t_except);
|
|
|
|
if (catch_block.children.size() == 2) {
|
|
// Variable capture
|
|
retval = catch_block.children[1]->eval(t_ss);
|
|
handled = true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
throw exception::eval_error("Internal error: catch block size unrecognized");
|
|
}
|
|
}
|
|
|
|
if (!handled) {
|
|
throw;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
Boxed_Value retval;
|
|
|
|
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
|
|
|
try {
|
|
try {
|
|
retval = this->children[0]->eval(t_ss);
|
|
} catch (const exception::eval_error &e) {
|
|
retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
|
|
} catch (const std::runtime_error &e) {
|
|
retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
|
|
} catch (const std::out_of_range &e) {
|
|
retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
|
|
} catch (const std::exception &e) {
|
|
retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
|
|
} catch (Boxed_Value &e) {
|
|
retval = handle_exception(t_ss, e);
|
|
}
|
|
} catch (...) {
|
|
if (this->children.back()->identifier == AST_Node_Type::Finally) {
|
|
this->children.back()->children[0]->eval(t_ss);
|
|
}
|
|
throw;
|
|
}
|
|
|
|
if (this->children.back()->identifier == AST_Node_Type::Finally) {
|
|
retval = this->children.back()->children[0]->eval(t_ss);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Catch_AST_Node final : AST_Node_Impl<T> {
|
|
Catch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Catch, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Finally_AST_Node final : AST_Node_Impl<T> {
|
|
Finally_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Finally, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Method_AST_Node final : AST_Node_Impl<T> {
|
|
std::shared_ptr<AST_Node_Impl<T>> m_body_node;
|
|
std::shared_ptr<AST_Node_Impl<T>> m_guard_node;
|
|
|
|
Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text),
|
|
AST_Node_Type::Method,
|
|
std::move(t_loc),
|
|
std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
|
|
std::make_move_iterator(
|
|
std::prev(t_children.end(), Def_AST_Node<T>::has_guard(t_children, 1) ? 2 : 1))))
|
|
, m_body_node(Def_AST_Node<T>::get_body_node(std::move(t_children)))
|
|
, m_guard_node(Def_AST_Node<T>::get_guard_node(std::move(t_children), t_children.size() - this->children.size() == 2)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
AST_Node_Impl_Ptr<T> guardnode;
|
|
|
|
const std::string &class_name = this->children[0]->text;
|
|
|
|
// The first param of a method is always the implied this ptr.
|
|
std::vector<std::string> t_param_names{"this"};
|
|
dispatch::Param_Types param_types;
|
|
|
|
if ((this->children.size() > 2) && (this->children[2]->identifier == AST_Node_Type::Arg_List)) {
|
|
auto args = Arg_List_AST_Node<T>::get_arg_names(*this->children[2]);
|
|
t_param_names.insert(t_param_names.end(), args.begin(), args.end());
|
|
param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[2], t_ss);
|
|
}
|
|
|
|
const size_t numparams = t_param_names.size();
|
|
|
|
std::shared_ptr<dispatch::Proxy_Function_Base> guard;
|
|
std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
|
|
if (m_guard_node) {
|
|
guard = dispatch::make_dynamic_proxy_function(
|
|
[engine, t_param_names, guardnode = m_guard_node](const Function_Params &t_params) {
|
|
return chaiscript::eval::detail::eval_function(engine, *guardnode, t_param_names, t_params);
|
|
},
|
|
static_cast<int>(numparams),
|
|
m_guard_node);
|
|
}
|
|
|
|
try {
|
|
const std::string &function_name = this->children[1]->text;
|
|
|
|
if (function_name == class_name) {
|
|
param_types.push_front(class_name, Type_Info());
|
|
|
|
t_ss->add(std::make_shared<dispatch::detail::Dynamic_Object_Constructor>(
|
|
class_name,
|
|
dispatch::make_dynamic_proxy_function(
|
|
[engine, t_param_names, node = m_body_node](const Function_Params &t_params) {
|
|
return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params);
|
|
},
|
|
static_cast<int>(numparams),
|
|
m_body_node,
|
|
param_types,
|
|
guard)),
|
|
function_name);
|
|
|
|
} else {
|
|
// if the type is unknown, then this generates a function that looks up the type
|
|
// at runtime. Defining the type first before this is called is better
|
|
auto type = t_ss->get_type(class_name, false);
|
|
param_types.push_front(class_name, type);
|
|
|
|
t_ss->add(std::make_shared<dispatch::detail::Dynamic_Object_Function>(
|
|
class_name,
|
|
dispatch::make_dynamic_proxy_function(
|
|
[engine, t_param_names, node = m_body_node](const Function_Params &t_params) {
|
|
return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params);
|
|
},
|
|
static_cast<int>(numparams),
|
|
m_body_node,
|
|
param_types,
|
|
guard),
|
|
type),
|
|
function_name);
|
|
}
|
|
} catch (const exception::name_conflict_error &e) {
|
|
throw exception::eval_error("Method redefined '" + e.name() + "'");
|
|
}
|
|
return void_var();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Attr_Decl_AST_Node final : AST_Node_Impl<T> {
|
|
Attr_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Attr_Decl, std::move(t_loc), std::move(t_children)) {
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
std::string class_name = this->children[0]->text;
|
|
|
|
try {
|
|
std::string attr_name = this->children[1]->text;
|
|
|
|
t_ss->add(std::make_shared<dispatch::detail::Dynamic_Object_Function>(std::move(class_name),
|
|
fun([attr_name](dispatch::Dynamic_Object &t_obj) {
|
|
return t_obj.get_attr(attr_name);
|
|
}),
|
|
true
|
|
|
|
),
|
|
this->children[1]->text);
|
|
} catch (const exception::name_conflict_error &e) {
|
|
throw exception::eval_error("Attribute redefined '" + e.name() + "'");
|
|
}
|
|
return void_var();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Logical_And_AST_Node final : AST_Node_Impl<T> {
|
|
Logical_And_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Logical_And, std::move(t_loc), std::move(t_children)) {
|
|
assert(this->children.size() == 2);
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)
|
|
&& this->get_bool_condition(this->children[1]->eval(t_ss), t_ss));
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct Logical_Or_AST_Node final : AST_Node_Impl<T> {
|
|
Logical_Or_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children)
|
|
: AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Logical_Or, std::move(t_loc), std::move(t_children)) {
|
|
assert(this->children.size() == 2);
|
|
}
|
|
|
|
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
|
|
return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)
|
|
|| this->get_bool_condition(this->children[1]->eval(t_ss), t_ss));
|
|
}
|
|
};
|
|
} // namespace eval
|
|
|
|
} // namespace chaiscript
|
|
#endif /* CHAISCRIPT_EVAL_HPP_ */
|