From c15e0174c9721b8da8b8d8ede71dabad49d53f5d Mon Sep 17 00:00:00 2001 From: Andreas Reischuck Date: Sat, 21 Mar 2015 22:29:16 +0100 Subject: [PATCH 1/5] added "method_missing" feature --- include/chaiscript/dispatchkit/dispatchkit.hpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index f882b0d..dff8a22 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -782,7 +782,19 @@ namespace chaiscript Boxed_Value call_function(const std::string &t_name, const std::vector ¶ms) const { - return dispatch::dispatch(get_function(t_name), params, m_conversions); + try { + return dispatch::dispatch(get_function(t_name), params, m_conversions); + } + catch(chaiscript::exception::dispatch_error&) { + auto functions = get_function("method_missing"); + if (!functions.empty()) { + std::vector tmp_params; + tmp_params.push_back(var(t_name)); + tmp_params.insert(tmp_params.end(), params.begin(), params.end()); + return dispatch::dispatch(functions, tmp_params, m_conversions); + } + throw; + } } Boxed_Value call_function(const std::string &t_name) const From d0e763d77ee4c735e2ade23f80b7e1fe33819811 Mon Sep 17 00:00:00 2001 From: Andreas Reischuck Date: Sun, 22 Mar 2015 00:17:53 +0100 Subject: [PATCH 2/5] fixed method_missing parameter order --- include/chaiscript/dispatchkit/dispatchkit.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index dff8a22..9c5687b 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -789,8 +789,8 @@ namespace chaiscript auto functions = get_function("method_missing"); if (!functions.empty()) { std::vector tmp_params; - tmp_params.push_back(var(t_name)); tmp_params.insert(tmp_params.end(), params.begin(), params.end()); + tmp_params.push_back(var(t_name)); return dispatch::dispatch(functions, tmp_params, m_conversions); } throw; From 4e614729dc08c07a1333a9e1d83dc63eb04e32a9 Mon Sep 17 00:00:00 2001 From: Andreas Reischuck Date: Sun, 22 Mar 2015 00:23:49 +0100 Subject: [PATCH 3/5] using copy construction --- include/chaiscript/dispatchkit/dispatchkit.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 9c5687b..e109e06 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -788,8 +788,7 @@ namespace chaiscript catch(chaiscript::exception::dispatch_error&) { auto functions = get_function("method_missing"); if (!functions.empty()) { - std::vector tmp_params; - tmp_params.insert(tmp_params.end(), params.begin(), params.end()); + std::vector tmp_params(params); tmp_params.push_back(var(t_name)); return dispatch::dispatch(functions, tmp_params, m_conversions); } From 90102cebd700164fe3d4c3fde16effc955194f93 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 21 Apr 2015 22:36:48 -0600 Subject: [PATCH 4/5] Full dynamic object system built on method_missing working --- include/chaiscript/dispatchkit/bootstrap.hpp | 11 ++++- .../chaiscript/dispatchkit/dispatchkit.hpp | 43 ++++++++++++++----- .../chaiscript/dispatchkit/dynamic_object.hpp | 28 +++++++++++- .../chaiscript/language/chaiscript_eval.hpp | 10 +++-- unittests/dynamic_object_dynamic_attrs.chai | 36 ++++++++++++++++ 5 files changed, 111 insertions(+), 17 deletions(-) create mode 100644 unittests/dynamic_object_dynamic_attrs.chai diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index e695d0a..3b7c037 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -429,9 +429,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 41ca035..c8fdd08 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -813,27 +813,48 @@ 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 { try { return dispatch::dispatch(funs, params, m_conversions); } catch(chaiscript::exception::dispatch_error&) { - auto functions = get_function("method_missing"); + 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)); - return dispatch::dispatch(functions, tmp_params, m_conversions); + if (is_no_param) { + return do_attribute_call(2, tmp_params, functions, m_conversions); + } else { + return dispatch::dispatch(functions, tmp_params, m_conversions); + } } + throw; } } 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) From a542ec01f695ab89118da537ca7d4917340966e0 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 22 Apr 2015 12:15:15 -0600 Subject: [PATCH 5/5] Update method_missing support to reduce exceptions --- .../chaiscript/dispatchkit/dispatchkit.hpp | 55 ++++++++++++------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index c8fdd08..a1e8345 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -831,31 +831,46 @@ namespace chaiscript if (is_attribute_call(funs, params, t_has_params)) { return do_attribute_call(1, params, funs, m_conversions); } else { - try { - return dispatch::dispatch(funs, params, m_conversions); - } catch(chaiscript::exception::dispatch_error&) { - const auto functions = get_function("method_missing"); + std::exception_ptr except; - const bool is_no_param = [&]()->bool{ - for (const auto &f : functions) { - if (f->get_arity() != 2) { - return false; - } - } - return true; - }(); + if (!funs.empty()) { + try { + return dispatch::dispatch(funs, params, m_conversions); + } catch(chaiscript::exception::dispatch_error&) { + except = std::current_exception(); + } + } - 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 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; + }(); - throw; + 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())); } } }