diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 5b4b4ec..21ad6e7 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -431,9 +431,18 @@ namespace chaiscript m->add(user_type(), "Dynamic_Object"); m->add(constructor(), "Dynamic_Object"); + m->add(constructor(), "Dynamic_Object"); m->add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name"); m->add(fun(&dispatch::Dynamic_Object::get_attrs), "get_attrs"); - m->add(fun(&dispatch::Dynamic_Object::get_attr), "get_attr"); + + m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); + m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); + + m->add(fun(static_cast(&dispatch::Dynamic_Object::method_missing)), "method_missing"); + m->add(fun(static_cast(&dispatch::Dynamic_Object::method_missing)), "method_missing"); + + m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "[]"); + m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "[]"); m->eval("def Dynamic_Object::clone() { auto &new_o = Dynamic_Object(this.get_type_name()); for_each(this.get_attrs(), bind(fun(new_o, x) { new_o.get_attr(x.first) = x.second; }, new_o, _) ); return new_o; }"); diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 3b9478d..a1e8345 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -813,19 +813,65 @@ namespace chaiscript Boxed_Value call_member(const std::string &t_name, const std::vector ¶ms, bool t_has_params) const { - auto funs = get_function(t_name); + const auto funs = get_function(t_name); + + const auto do_attribute_call = + [this](int l_num_params, const std::vector &l_params, const std::vector &l_funs, const Type_Conversions &l_conversions)->Boxed_Value + { + std::vector attr_params{l_params.begin(), l_params.begin() + l_num_params}; + std::vector remaining_params{l_params.begin() + l_num_params, l_params.end()}; + Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions); + if (!remaining_params.empty() || bv.get_type_info().bare_equal(user_type())) { + return (*boxed_cast(bv))(remaining_params, l_conversions); + } else { + return bv; + } + }; if (is_attribute_call(funs, params, t_has_params)) { - std::vector attr_params{params[0]}; - std::vector remaining_params{params.begin() + 1, params.end()}; - Boxed_Value bv = dispatch::dispatch(funs, attr_params, m_conversions); - if (!remaining_params.empty() || bv.get_type_info().bare_equal(user_type())) { - return (*boxed_cast(bv))(remaining_params, m_conversions); - } else { - return bv; - } + return do_attribute_call(1, params, funs, m_conversions); } else { - return dispatch::dispatch(funs, params, m_conversions); + std::exception_ptr except; + + if (!funs.empty()) { + try { + return dispatch::dispatch(funs, params, m_conversions); + } catch(chaiscript::exception::dispatch_error&) { + except = std::current_exception(); + } + } + + // If we get here we know that either there was no method with that name, + // or there was no matching method + + const auto functions = get_function("method_missing"); + + const bool is_no_param = [&]()->bool{ + for (const auto &f : functions) { + if (f->get_arity() != 2) { + return false; + } + } + return true; + }(); + + if (!functions.empty()) { + std::vector tmp_params(params); + tmp_params.insert(tmp_params.begin() + 1, var(t_name)); + if (is_no_param) { + return do_attribute_call(2, tmp_params, functions, m_conversions); + } else { + return dispatch::dispatch(functions, tmp_params, m_conversions); + } + } + + // If we get all the way down here we know there was no "method_missing" + // method at all. + if (except) { + std::rethrow_exception(except); + } else { + throw chaiscript::exception::dispatch_error(params, std::vector(funs.begin(), funs.end())); + } } } diff --git a/include/chaiscript/dispatchkit/dynamic_object.hpp b/include/chaiscript/dispatchkit/dynamic_object.hpp index c300a8d..bd6c843 100644 --- a/include/chaiscript/dispatchkit/dynamic_object.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object.hpp @@ -40,16 +40,42 @@ namespace chaiscript { } + Dynamic_Object() : m_type_name("") + { + } + std::string get_type_name() const { return m_type_name; } - Boxed_Value get_attr(const std::string &t_attr_name) + const Boxed_Value &get_attr(const std::string &t_attr_name) const + { + auto a = m_attrs.find(t_attr_name); + + if (a != m_attrs.end()) { + return a->second; + } else { + throw std::range_error("Attr not found '" + t_attr_name + "' and cannot be added to const obj"); + } + } + + Boxed_Value &get_attr(const std::string &t_attr_name) { return m_attrs[t_attr_name]; } + Boxed_Value &method_missing(const std::string &t_method_name) + { + return get_attr(t_method_name); + } + + const Boxed_Value &method_missing(const std::string &t_method_name) const + { + return get_attr(t_method_name); + } + + std::map get_attrs() const { return m_attrs; diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 595eb2d..5b2b495 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -726,6 +726,7 @@ namespace chaiscript try { chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); + t_ss.add_object("this", retval); retval = t_ss.call_member(fun_name, std::move(params), has_function_params); } catch(const exception::dispatch_error &e){ @@ -1533,10 +1534,11 @@ namespace chaiscript t_ss.add( std::make_shared( std::move(class_name), - fun(std::function(std::bind(&dispatch::Dynamic_Object::get_attr, - std::placeholders::_1, - this->children[static_cast(1 + class_offset)]->text - )) + fun(std::function( + std::bind(static_cast(&dispatch::Dynamic_Object::get_attr), + std::placeholders::_1, + this->children[static_cast(1 + class_offset)]->text + )) ), true diff --git a/unittests/dynamic_object_dynamic_attrs.chai b/unittests/dynamic_object_dynamic_attrs.chai new file mode 100644 index 0000000..d8a1664 --- /dev/null +++ b/unittests/dynamic_object_dynamic_attrs.chai @@ -0,0 +1,36 @@ + +class MyClass { + def MyClass() + { + } + + def mult(double d) { + this.y * d + } +}; + +var o = MyClass(); +o.f = fun(x,y) { x * y; } +assert_true(o.f(3,4) == 12); + +o.f2 = fun(x) { x * 3; } +assert_true(o.f2(3) == 9); + +o.y = 15; +o.f3 = fun(x) { x * this.y; } +assert_true(o.f3(4) == 60); + +assert_true(o.mult(3.0) == 45.0); + +def method_missing(int i, string method_name, x, y) { + "method_missing called : " + to_string(i) + "." + method_name + "(" + to_string(x) + ", " + to_string(y) + ")"; +} + + +assert_true(5.bob(3,4) == "method_missing called : 5.bob(3, 4)" ) + +var o2 = Dynamic_Object(); +o2.a = 15 +assert_true(o2.a == 15) + +assert_true(o2["a"] == 15)