diff --git a/include/chaiscript/dispatchkit/dynamic_object.hpp b/include/chaiscript/dispatchkit/dynamic_object.hpp index 69c24d5..d90fe1b 100644 --- a/include/chaiscript/dispatchkit/dynamic_object.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object.hpp @@ -19,7 +19,6 @@ #include "boxed_cast.hpp" #include "boxed_cast_helper.hpp" #include "boxed_value.hpp" -#include "proxy_functions.hpp" #include "type_info.hpp" namespace chaiscript { @@ -62,217 +61,6 @@ namespace chaiscript std::map m_attrs; }; - namespace detail - { - /// A Proxy_Function implementation designed for calling a function - /// that is automatically guarded based on the first param based on the - /// param's type name - class Dynamic_Object_Function : public Proxy_Function_Base - { - public: - Dynamic_Object_Function( - std::string t_type_name, - const Proxy_Function &t_func) - : Proxy_Function_Base(t_func->get_param_types(), t_func->get_arity()), - m_type_name(std::move(t_type_name)), m_func(t_func), m_doti(user_type()) - { - assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) - && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); - } - - Dynamic_Object_Function( - std::string t_type_name, - const Proxy_Function &t_func, - const Type_Info &t_ti) - : Proxy_Function_Base(build_param_types(t_func->get_param_types(), t_ti), t_func->get_arity()), - m_type_name(std::move(t_type_name)), m_func(t_func), m_ti(new Type_Info(t_ti)), m_doti(user_type()) - { - assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) - && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); - } - - virtual ~Dynamic_Object_Function() {} - - Dynamic_Object_Function &operator=(const Dynamic_Object_Function) = delete; - Dynamic_Object_Function(Dynamic_Object_Function &) = delete; - - virtual bool operator==(const Proxy_Function_Base &f) const CHAISCRIPT_OVERRIDE - { - if (const auto *df = dynamic_cast(&f)) - { - return df->m_type_name == m_type_name && (*df->m_func) == (*m_func); - } else { - return false; - } - } - - virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE - { - if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions)) - { - return m_func->call_match(vals, t_conversions); - } else { - return false; - } - } - - virtual std::vector get_contained_functions() const CHAISCRIPT_OVERRIDE - { - return {m_func}; - } - - virtual std::string annotation() const CHAISCRIPT_OVERRIDE - { - return m_func->annotation(); - } - - - protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE - { - if (dynamic_object_typename_match(params, m_type_name, m_ti, t_conversions)) - { - return (*m_func)(params, t_conversions); - } else { - throw exception::guard_error(); - } - } - - virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE - { - return dynamic_object_typename_match(bv, m_type_name, m_ti, t_conversions); - } - - private: - static std::vector build_param_types( - const std::vector &t_inner_types, const Type_Info& t_objectti) - { - std::vector types(t_inner_types); - - assert(types.size() > 1); - assert(types[1].bare_equal(user_type())); - types[1] = t_objectti; - return types; - } - - bool dynamic_object_typename_match(const Boxed_Value &bv, const std::string &name, - const std::unique_ptr &ti, const Type_Conversions &t_conversions) const - { - if (bv.get_type_info().bare_equal(m_doti)) - { - try { - const Dynamic_Object &d = boxed_cast(bv, &t_conversions); - return name == "Dynamic_Object" || d.get_type_name() == name; - } catch (const std::bad_cast &) { - return false; - } - } else { - if (ti) - { - return bv.get_type_info().bare_equal(*ti); - } else { - return false; - } - } - - } - - bool dynamic_object_typename_match(const std::vector &bvs, const std::string &name, - const std::unique_ptr &ti, const Type_Conversions &t_conversions) const - { - if (bvs.size() > 0) - { - return dynamic_object_typename_match(bvs[0], name, ti, t_conversions); - } else { - return false; - } - } - - std::string m_type_name; - Proxy_Function m_func; - std::unique_ptr m_ti; - const Type_Info m_doti; - - - }; - - - /** - * A Proxy_Function implementation designed for creating a new - * Dynamic_Object - * that is automatically guarded based on the first param based on the - * param's type name - */ - class Dynamic_Object_Constructor : public Proxy_Function_Base - { - public: - Dynamic_Object_Constructor( - std::string t_type_name, - const Proxy_Function &t_func) - : Proxy_Function_Base(build_type_list(t_func->get_param_types()), t_func->get_arity() - 1), - m_type_name(std::move(t_type_name)), m_func(t_func) - { - assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) - && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); - } - - static std::vector build_type_list(const std::vector &tl) - { - auto begin = tl.begin(); - auto end = tl.end(); - - if (begin != end) - { - ++begin; - } - - return std::vector(begin, end); - } - - virtual ~Dynamic_Object_Constructor() {} - - virtual bool operator==(const Proxy_Function_Base &f) const CHAISCRIPT_OVERRIDE - { - const Dynamic_Object_Constructor *dc = dynamic_cast(&f); - if (dc) - { - return dc->m_type_name == m_type_name && (*dc->m_func) == (*m_func); - } else { - return false; - } - } - - virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE - { - std::vector new_vals{Boxed_Value(Dynamic_Object(m_type_name))}; - new_vals.insert(new_vals.end(), vals.begin(), vals.end()); - - return m_func->call_match(new_vals, t_conversions); - } - - virtual std::string annotation() const CHAISCRIPT_OVERRIDE - { - return m_func->annotation(); - } - - protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE - { - auto bv = var(Dynamic_Object(m_type_name)); - std::vector new_params{bv}; - new_params.insert(new_params.end(), params.begin(), params.end()); - - (*m_func)(new_params, t_conversions); - - return bv; - } - - private: - std::string m_type_name; - Proxy_Function m_func; - - }; - } } } #endif diff --git a/include/chaiscript/dispatchkit/proxy_constructors.hpp b/include/chaiscript/dispatchkit/proxy_constructors.hpp index 64d2a93..291fa43 100644 --- a/include/chaiscript/dispatchkit/proxy_constructors.hpp +++ b/include/chaiscript/dispatchkit/proxy_constructors.hpp @@ -8,6 +8,8 @@ #ifndef CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_ #define CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_ +#include "proxy_functions.hpp" + namespace chaiscript { namespace dispatch diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index fba9b4f..1c7cea2 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -24,6 +24,7 @@ #include "boxed_value.hpp" #include "proxy_functions_detail.hpp" #include "type_info.hpp" +#include "dynamic_object.hpp" namespace chaiscript { class Type_Conversions; @@ -42,6 +43,91 @@ namespace chaiscript namespace dispatch { + class Param_Types + { + public: + Param_Types() + {} + + Param_Types(std::vector> t_types) + : m_types(std::move(t_types)) + { + update_has_types(); + } + + void push_front(std::string t_name, Type_Info t_ti) + { + m_types.emplace(m_types.begin(), std::move(t_name), std::move(t_ti)); + update_has_types(); + } + + bool operator==(const Param_Types &t_rhs) const + { + return m_types == t_rhs.m_types; + } + + bool match(const std::vector &vals, const Type_Conversions &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)) + { + try { + const Dynamic_Object &d = boxed_cast(bv, &t_conversions); + return name == "Dynamic_Object" || d.get_type_name() == name; + } catch (const std::bad_cast &) { + return false; + } + } else { + const auto &ti = m_types[i].second; + if (!ti.is_undef()) + { + if (!bv.get_type_info().bare_equal(ti)) { + return false; + } + } else { + return false; + } + } + } + } + + return true; + } + + const std::vector> &types() const + { + return m_types; + } + + private: + void update_has_types() + { + for (const auto &type : m_types) + { + if (!type.first.empty()) + { + m_has_types = true; + return; + } + } + + m_has_types = false; + } + + std::vector> m_types; + bool m_has_types = false; + Type_Info m_doti = user_type(); + + }; + /** * Pure virtual base class for all Proxy_Function implementations * Proxy_Functions are a type erasure of type safe C++ @@ -215,10 +301,12 @@ namespace chaiscript std::function &)> t_f, int t_arity=-1, AST_NodePtr t_parsenode = AST_NodePtr(), + Param_Types t_param_types = Param_Types(), std::string t_description = "", Proxy_Function t_guard = Proxy_Function()) - : Proxy_Function_Base(build_param_type_list(t_arity), t_arity), - m_f(std::move(t_f)), m_arity(t_arity), m_description(std::move(t_description)), m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode)) + : Proxy_Function_Base(build_param_type_list(t_param_types), t_arity), + m_f(std::move(t_f)), m_arity(t_arity), m_param_types(std::move(t_param_types)), + m_description(std::move(t_description)), m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode)) { } @@ -231,14 +319,15 @@ namespace chaiscript return this == &rhs || (prhs && this->m_arity == prhs->m_arity - && !this->m_guard && !prhs->m_guard); + && !this->m_guard && !prhs->m_guard + && this->m_param_types == prhs->m_param_types); } virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE { - return (m_arity < 0 || vals.size() == size_t(m_arity)) + return (m_arity < 0 || (vals.size() == size_t(m_arity) && m_param_types.match(vals, t_conversions))) && test_guard(vals, t_conversions); - } + } Proxy_Function get_guard() const @@ -261,8 +350,7 @@ namespace chaiscript { if (m_arity < 0 || params.size() == size_t(m_arity)) { - - if (test_guard(params, t_conversions)) + if (call_match(params, t_conversions) && test_guard(params, t_conversions)) { return m_f(params); } else { @@ -291,18 +379,19 @@ namespace chaiscript } } - static std::vector build_param_type_list(int arity) + static std::vector build_param_type_list(const Param_Types &t_types) { std::vector types; // For the return type types.push_back(chaiscript::detail::Get_Type_Info::get()); - if (arity > 0) + for (const auto &t : t_types.types()) { - for (int i = 0; i < arity; ++i) - { + if (t.second.is_undef()) { types.push_back(chaiscript::detail::Get_Type_Info::get()); + } else { + types.push_back(t.second); } } @@ -311,6 +400,7 @@ namespace chaiscript std::function &)> m_f; int m_arity; + Param_Types m_param_types; std::string m_description; Proxy_Function m_guard; AST_NodePtr m_parsenode; diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 288b0cc..c784505 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -37,7 +37,7 @@ namespace chaiscript Comparison, Addition, Subtraction, Multiplication, Division, Modulus, Array_Call, Dot_Access, Quoted_String, Single_Quoted_String, Lambda, Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range, Inline_Range, Annotation, Try, Catch, Finally, Method, Attr_Decl, Shift, Equality, Bitwise_And, Bitwise_Xor, Bitwise_Or, - Logical_And, Logical_Or, Reference, Switch, Case, Default, Ternary_Cond, Noop, Class, Binary + Logical_And, Logical_Or, Reference, Switch, Case, Default, Ternary_Cond, Noop, Class, Binary, Arg }; }; @@ -50,7 +50,7 @@ namespace chaiscript "Comparison", "Addition", "Subtraction", "Multiplication", "Division", "Modulus", "Array_Call", "Dot_Access", "Quoted_String", "Single_Quoted_String", "Lambda", "Block", "Def", "While", "If", "For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", "Inline_Range", "Annotation", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Shift", "Equality", "Bitwise_And", "Bitwise_Xor", "Bitwise_Or", - "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Ternary Condition", "Noop", "Class", "Binary"}; + "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Ternary Condition", "Noop", "Class", "Binary", "Arg"}; return ast_node_types[ast_node_type]; } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index be9aabc..6323a6e 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -25,6 +25,7 @@ #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" @@ -349,6 +350,28 @@ namespace chaiscript }; + struct Arg_AST_Node : public AST_Node { + public: + Arg_AST_Node(std::string t_ast_node_text = "", const std::shared_ptr &t_fname=std::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Arg_List, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { } + virtual ~Arg_AST_Node() {} + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + std::ostringstream oss; + for (size_t j = 0; j < this->children.size(); ++j) { + if (j != 0) + { + oss << " "; + } + + oss << this->children[j]->pretty_print(); + } + + return oss.str(); + } + }; + struct Arg_List_AST_Node : public AST_Node { public: Arg_List_AST_Node(std::string t_ast_node_text = "", const std::shared_ptr &t_fname=std::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : @@ -369,6 +392,46 @@ namespace chaiscript return oss.str(); } + + static std::vector get_arg_names(const AST_NodePtr &t_node) { + std::vector retval; + + for (const auto &child : t_node->children) + { + if (child->children.empty()) + { + retval.push_back(child->text); + } else if (child->children.size() == 1) { + retval.push_back(child->children[0]->text); + } else { + retval.push_back(child->children[1]->text); + } + } + + return retval; + } + + static dispatch::Param_Types get_arg_types(const AST_NodePtr &t_node, chaiscript::detail::Dispatch_Engine &t_ss) { + std::vector> retval; + + for (const auto &child : t_node->children) + { + if (child->children.empty()) + { + retval.emplace_back("", Type_Info()); + } else if (child->children.size() == 1) { + retval.emplace_back("", Type_Info()); + } else { + try { + retval.emplace_back(child->children[0]->text, t_ss.get_type(child->children[0]->text)); + } catch (const std::range_error &t_err) { + retval.emplace_back(child->children[0]->text, Type_Info()); + } + } + } + + return dispatch::Param_Types(std::move(retval)); + } }; struct Equation_AST_Node : public AST_Node { @@ -636,13 +699,12 @@ namespace chaiscript size_t numparams = 0; + dispatch::Param_Types param_types; + if (!this->children.empty() && (this->children[0]->identifier == AST_Node_Type::Arg_List)) { numparams = this->children[0]->children.size(); - - for (const auto &child : this->children[0]->children) - { - t_param_names.push_back(child->text); - } + t_param_names = Arg_List_AST_Node::get_arg_names(this->children[0]); + param_types = Arg_List_AST_Node::get_arg_types(this->children[0], t_ss); } const auto &lambda_node = this->children.back(); @@ -652,7 +714,7 @@ namespace chaiscript { return detail::eval_function(t_ss, lambda_node, t_param_names, t_params); }, - static_cast(numparams), lambda_node))); + static_cast(numparams), lambda_node, param_types))); } }; @@ -696,13 +758,12 @@ namespace chaiscript size_t numparams = 0; AST_NodePtr guardnode; + dispatch::Param_Types param_types; + if ((this->children.size() > 2) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) { numparams = this->children[1]->children.size(); - - for (const auto &child : this->children[1]->children) - { - t_param_names.push_back(child->text); - } + t_param_names = Arg_List_AST_Node::get_arg_names(this->children[1]); + param_types = Arg_List_AST_Node::get_arg_types(this->children[1], t_ss); if (this->children.size() > 3) { guardnode = this->children[2]; @@ -735,7 +796,7 @@ namespace chaiscript { return detail::eval_function(t_ss, func_node, t_param_names, t_params); }, static_cast(numparams), this->children.back(), - l_annotation, guard)), l_function_name); + param_types, l_annotation, guard)), l_function_name); } catch (const exception::reserved_word_error &e) { throw exception::eval_error("Reserved word used as function name '" + e.word() + "'"); @@ -1341,11 +1402,12 @@ namespace chaiscript //The first param of a method is always the implied this ptr. std::vector t_param_names{"this"}; + dispatch::Param_Types param_types; if ((this->children.size() > static_cast(3 + class_offset)) && (this->children[(2 + class_offset)]->identifier == AST_Node_Type::Arg_List)) { - for (const auto &child : this->children[(2 + class_offset)]->children) { - t_param_names.push_back(child->text); - } + auto args = Arg_List_AST_Node::get_arg_names(this->children[(2 + class_offset)]); + t_param_names.insert(t_param_names.end(), args.begin(), args.end()); + param_types = Arg_List_AST_Node::get_arg_types(this->children[(2 + class_offset)], t_ss); if (this->children.size() > static_cast(4 + class_offset)) { guardnode = this->children[(3 + class_offset)]; @@ -1375,29 +1437,35 @@ namespace chaiscript const std::string & function_name = this->children[(1 + class_offset)]->text; if (function_name == class_name) { + param_types.push_front(class_name, Type_Info()); t_ss.add(std::make_shared(class_name, std::make_shared(std::bind(chaiscript::eval::detail::eval_function, std::ref(t_ss), this->children.back(), t_param_names, std::placeholders::_1), - static_cast(numparams), this->children.back(), l_annotation, guard)), + static_cast(numparams), this->children.back(), param_types, l_annotation, guard)), function_name); } else { try { - // Do know type name + // Do know type name (if this line fails, the catch block is called and the + // other version is called, with no Type_Info object known) + auto type = t_ss.get_type(class_name); + param_types.push_front(class_name, type); + t_ss.add( std::make_shared(class_name, std::make_shared(std::bind(chaiscript::eval::detail::eval_function, std::ref(t_ss), this->children.back(), t_param_names, std::placeholders::_1), static_cast(numparams), this->children.back(), - l_annotation, guard), t_ss.get_type(class_name)), function_name); + param_types, l_annotation, guard), type), function_name); } catch (const std::range_error &) { + param_types.push_front(class_name, Type_Info()); // Do not know type name t_ss.add( std::make_shared(class_name, std::make_shared(std::bind(chaiscript::eval::detail::eval_function, std::ref(t_ss), this->children.back(), t_param_names, std::placeholders::_1), static_cast(numparams), this->children.back(), - l_annotation, guard)), function_name); + param_types, l_annotation, guard)), function_name); } } } diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index e9c40aa..de18959 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -699,6 +699,25 @@ namespace chaiscript } } + /// Reads an argument from input + bool Arg() { + const auto prev_stack_top = m_match_stack.size(); + SkipWS(); + + if (!Id(true)) { + return false; + } + + SkipWS(); + Id(true); + + build_match(std::make_shared(), prev_stack_top); + + return true; + } + + + /// Checks for a node annotation of the form "#" bool Annotation() { SkipWS(); @@ -1109,6 +1128,33 @@ namespace chaiscript } } + /// Reads a comma-separated list of values from input, for function declarations + bool Decl_Arg_List() { + SkipWS(true); + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Arg()) { + retval = true; + while (Eol()) {} + if (Char(',')) { + do { + while (Eol()) {} + if (!Arg()) { + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_line, m_col), *m_filename); + } + } while (Char(',')); + } + build_match(std::make_shared(), prev_stack_top); + } + + SkipWS(true); + + return retval; + } + + /// Reads a comma-separated list of values from input bool Arg_List() { SkipWS(true); @@ -1186,7 +1232,7 @@ namespace chaiscript retval = true; if (Char('(')) { - Arg_List(); + Decl_Arg_List(); if (!Char(')')) { throw exception::eval_error("Incomplete anonymous function", File_Position(m_line, m_col), *m_filename); } @@ -1236,7 +1282,7 @@ namespace chaiscript } if (Char('(')) { - Arg_List(); + Decl_Arg_List(); if (!Char(')')) { throw exception::eval_error("Incomplete function definition", File_Position(m_line, m_col), *m_filename); }