diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index 3b1b9fd..218ba80 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -72,10 +72,51 @@ namespace chaiscript return m_types == t_rhs.m_types; } - bool match(const std::vector &vals, const Type_Conversions_State &t_conversions) const + std::vector convert(std::vector vals, const Type_Conversions_State &t_conversions) const { - if (!m_has_types) { return true; } - if (vals.size() != m_types.size()) { return false; } + for (size_t i = 0; i < vals.size(); ++i) + { + const auto &name = m_types[i].first; + if (!name.empty()) { + const auto &bv = vals[i]; + + if (!bv.get_type_info().bare_equal(m_doti)) + { + const auto &ti = m_types[i].second; + if (!ti.is_undef()) + { + if (!bv.get_type_info().bare_equal(ti)) { + if (t_conversions->converts(ti, bv.get_type_info())) { + try { + // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it + // either way, we are not responsible if it doesn't work + vals[i] = t_conversions->boxed_type_conversion(m_types[i].second, t_conversions.saves(), vals[i]); + } catch (...) { + try { + // try going the other way + vals[i] = t_conversions->boxed_type_down_conversion(m_types[i].second, t_conversions.saves(), vals[i]); + } catch (const chaiscript::detail::exception::bad_any_cast &) { + throw exception::bad_boxed_cast(bv.get_type_info(), *m_types[i].second.bare_type_info()); + } + } + } + } + } + } + } + } + + return vals; + } + + // first result: is a match + // second result: needs conversions + std::pair match(const std::vector &vals, const Type_Conversions_State &t_conversions) const + { + bool needs_conversion = false; + + if (!m_has_types) { return std::make_pair(true, needs_conversion); } + if (vals.size() != m_types.size()) { return std::make_pair(false, needs_conversion); } for (size_t i = 0; i < vals.size(); ++i) { @@ -87,25 +128,31 @@ namespace chaiscript { try { const Dynamic_Object &d = boxed_cast(bv, &t_conversions); - return name == "Dynamic_Object" || d.get_type_name() == name; + if (!(name == "Dynamic_Object" || d.get_type_name() == name)) { + return std::make_pair(false, false); + } } catch (const std::bad_cast &) { - return false; + return std::make_pair(false, false); } } else { const auto &ti = m_types[i].second; if (!ti.is_undef()) { if (!bv.get_type_info().bare_equal(ti)) { - return false; + if (!t_conversions->converts(ti, bv.get_type_info())) { + return std::make_pair(false, false); + } else { + needs_conversion = true; + } } } else { - return false; + return std::make_pair(false, false); } } } } - return true; + return std::make_pair(true, needs_conversion); } const std::vector> &types() const @@ -320,8 +367,7 @@ namespace chaiscript bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override { - return (m_arity < 0 || (vals.size() == size_t(m_arity) && m_param_types.match(vals, t_conversions))) - && test_guard(vals, t_conversions); + return call_match_internal(vals, t_conversions).first; } @@ -353,6 +399,26 @@ namespace chaiscript } } + // first result: is a match + // second result: needs conversions + std::pair call_match_internal(const std::vector &vals, const Type_Conversions_State &t_conversions) const + { + const auto comparison_result = [&](){ + if (m_arity < 0) { + return std::make_pair(true, false); + } else if (vals.size() == size_t(m_arity)) { + return m_param_types.match(vals, t_conversions); + } else { + return std::make_pair(false, false); + } + }(); + + return std::make_pair( + comparison_result.first && test_guard(vals, t_conversions), + comparison_result.second + ); + } + private: static std::vector build_param_type_list(const Param_Types &t_types) { @@ -371,7 +437,10 @@ namespace chaiscript return types; } + protected: Param_Types m_param_types; + + private: Proxy_Function m_guard; AST_NodePtr m_parsenode; }; @@ -402,9 +471,14 @@ namespace chaiscript protected: Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override { - if (call_match(params, t_conversions) && test_guard(params, t_conversions)) + const auto match_results = call_match_internal(params, t_conversions); + if (match_results.first) { - return m_f(params); + if (match_results.second) { + return m_f(m_param_types.convert(params, t_conversions)); + } else { + return m_f(params); + } } else { throw exception::guard_error(); } diff --git a/include/chaiscript/dispatchkit/type_conversions.hpp b/include/chaiscript/dispatchkit/type_conversions.hpp index 273544f..3a546c6 100644 --- a/include/chaiscript/dispatchkit/type_conversions.hpp +++ b/include/chaiscript/dispatchkit/type_conversions.hpp @@ -397,28 +397,39 @@ namespace chaiscript template Boxed_Value boxed_type_conversion(Conversion_Saves &t_saves, const Boxed_Value &from) const { - try { - Boxed_Value ret = get_conversion(user_type(), from.get_type_info())->convert(from); - if (t_saves.enabled) { t_saves.saves.push_back(ret); } - return ret; - } catch (const std::out_of_range &) { - throw exception::bad_boxed_dynamic_cast(from.get_type_info(), typeid(To), "No known conversion"); - } catch (const std::bad_cast &) { - throw exception::bad_boxed_dynamic_cast(from.get_type_info(), typeid(To), "Unable to perform dynamic_cast operation"); - } + return boxed_type_conversion(user_type(), t_saves, from); } template Boxed_Value boxed_type_down_conversion(Conversion_Saves &t_saves, const Boxed_Value &to) const + { + return boxed_type_down_conversion(user_type(), t_saves, to); + } + + + Boxed_Value boxed_type_conversion(const Type_Info &to, Conversion_Saves &t_saves, const Boxed_Value &from) const { try { - Boxed_Value ret = get_conversion(to.get_type_info(), user_type())->convert_down(to); + Boxed_Value ret = get_conversion(to, from.get_type_info())->convert(from); if (t_saves.enabled) { t_saves.saves.push_back(ret); } return ret; } catch (const std::out_of_range &) { - throw exception::bad_boxed_dynamic_cast(to.get_type_info(), typeid(From), "No known conversion"); + throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "No known conversion"); } catch (const std::bad_cast &) { - throw exception::bad_boxed_dynamic_cast(to.get_type_info(), typeid(From), "Unable to perform dynamic_cast operation"); + throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "Unable to perform dynamic_cast operation"); + } + } + + Boxed_Value boxed_type_down_conversion(const Type_Info &from, Conversion_Saves &t_saves, const Boxed_Value &to) const + { + try { + Boxed_Value ret = get_conversion(to.get_type_info(), from)->convert_down(to); + if (t_saves.enabled) { t_saves.saves.push_back(ret); } + return ret; + } catch (const std::out_of_range &) { + throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "No known conversion"); + } catch (const std::bad_cast &) { + throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "Unable to perform dynamic_cast operation"); } } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index adc86ed..8116c83 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -1226,7 +1226,7 @@ namespace chaiscript if (dispatch::Param_Types( std::vector>{Arg_List_AST_Node::get_arg_type(catch_block->children[0], t_ss)} - ).match(std::vector{t_except}, t_ss.conversions())) + ).match(std::vector{t_except}, t_ss.conversions()).first) { t_ss.add_object(name, t_except);