// 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_BOOTSTRAP_HPP_ #define CHAISCRIPT_BOOTSTRAP_HPP_ #include "../utility/utility.hpp" #include "register_function.hpp" /// \brief Classes and functions useful for bootstrapping of ChaiScript and adding of new types namespace chaiscript::bootstrap { template::value>::type> void array(const std::string &type, Module &m) { using ReturnType = typename std::remove_extent::type; m.add(user_type(), type); m.add(fun([](T &t, size_t index) -> ReturnType & { constexpr const auto extent = std::extent::value; if (extent > 0 && index >= extent) { throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent)); } else { return t[index]; } }), "[]"); m.add(fun([](const T &t, size_t index) -> const ReturnType & { constexpr const auto extent = std::extent::value; if (extent > 0 && index >= extent) { throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent)); } else { return t[index]; } }), "[]"); m.add(fun([](const T &) { return std::extent::value; }), "size"); } /// \brief Adds a copy constructor for the given type to the given Model /// \param[in] type The name of the type. The copy constructor will be named "type". /// \param[in,out] m The Module to add the copy constructor to /// \tparam T The type to add a copy constructor for /// \returns The passed in Module template void copy_constructor(const std::string &type, Module &m) { m.add(constructor(), type); } /// \brief Add all comparison operators for the templated type. Used during bootstrap, also available to users. /// \tparam T Type to create comparison operators for /// \param[in,out] m module to add comparison operators to /// \returns the passed in Module. template void opers_comparison(Module &m) { operators::equal(m); operators::greater_than(m); operators::greater_than_equal(m); operators::less_than(m); operators::less_than_equal(m); operators::not_equal(m); } /// \brief Adds default and copy constructors for the given type /// \param[in] type The name of the type to add the constructors for. /// \param[in,out] m The Module to add the basic constructors to /// \tparam T Type to generate basic constructors for /// \returns The passed in Module /// \sa copy_constructor /// \sa constructor template void basic_constructors(const std::string &type, Module &m) { m.add(constructor(), type); copy_constructor(type, m); } /// \brief Adds a constructor for a POD type /// \tparam T The type to add the constructor for /// \param[in] type The name of the type /// \param[in,out] m The Module to add the constructor to template void construct_pod(const std::string &type, Module &m) { m.add(fun([](const Boxed_Number &bn) { return bn.get_as(); }), type); } /// Internal function for converting from a string to a value /// uses ostream operator >> to perform the conversion template Input parse_string(const std::string &i) { if constexpr (!std::is_same::value && !std::is_same::value && !std::is_same::value) { std::stringstream ss(i); Input t; ss >> t; return t; } else { throw std::runtime_error("Parsing of wide characters is not yet supported"); } } /// Add all common functions for a POD type. All operators, and /// common conversions template void bootstrap_pod_type(const std::string &name, Module &m) { m.add(user_type(), name); m.add(constructor(), name); construct_pod(name, m); m.add(fun(&parse_string), "to_" + name); m.add(fun([](const T t) { return t; }), "to_" + name); } /// "clone" function for a shared_ptr type. This is used in the case /// where you do not want to make a deep copy of an object during cloning /// but want to instead maintain the shared_ptr. It is needed internally /// for handling of Proxy_Function object (that is, /// function variables. template auto shared_ptr_clone(const std::shared_ptr &p) { return p; } /// Specific version of shared_ptr_clone just for Proxy_Functions template std::shared_ptr> shared_ptr_unconst_clone(const std::shared_ptr> &p) { return std::const_pointer_cast::type>(p); } /// Assignment function for shared_ptr objects, does not perform a copy of the /// object pointed to, instead maintains the shared_ptr concept. /// Similar to shared_ptr_clone. Used for Proxy_Function. template Boxed_Value ptr_assign(Boxed_Value lhs, const std::shared_ptr &rhs) { if (lhs.is_undef() || (!lhs.get_type_info().is_const() && lhs.get_type_info().bare_equal(chaiscript::detail::Get_Type_Info::get()))) { lhs.assign(Boxed_Value(rhs)); return lhs; } else { throw exception::bad_boxed_cast("type mismatch in pointer assignment"); } } /// Class consisting of only static functions. All default bootstrapping occurs /// from this class. class Bootstrap { private: /// Function allowing for assignment of an unknown type to any other value static Boxed_Value unknown_assign(Boxed_Value lhs, Boxed_Value rhs) { if (lhs.is_undef()) { return (lhs.assign(rhs)); } else { throw exception::bad_boxed_cast("boxed_value has a set type already"); } } static void print(const std::string &s) noexcept { fwrite(s.c_str(), 1, s.size(), stdout); } static void println(const std::string &s) noexcept { puts(s.c_str()); } /// Add all arithmetic operators for PODs static void opers_arithmetic_pod(Module &m) { m.add(fun(&Boxed_Number::equals), "=="); m.add(fun(&Boxed_Number::less_than), "<"); m.add(fun(&Boxed_Number::greater_than), ">"); m.add(fun(&Boxed_Number::greater_than_equal), ">="); m.add(fun(&Boxed_Number::less_than_equal), "<="); m.add(fun(&Boxed_Number::not_equal), "!="); m.add(fun(&Boxed_Number::pre_decrement), "--"); m.add(fun(&Boxed_Number::pre_increment), "++"); m.add(fun(&Boxed_Number::sum), "+"); m.add(fun(&Boxed_Number::unary_plus), "+"); m.add(fun(&Boxed_Number::unary_minus), "-"); m.add(fun(&Boxed_Number::difference), "-"); m.add(fun(&Boxed_Number::assign_bitwise_and), "&="); m.add(fun(&Boxed_Number::assign), "="); m.add(fun(&Boxed_Number::assign_bitwise_or), "|="); m.add(fun(&Boxed_Number::assign_bitwise_xor), "^="); m.add(fun(&Boxed_Number::assign_remainder), "%="); m.add(fun(&Boxed_Number::assign_shift_left), "<<="); m.add(fun(&Boxed_Number::assign_shift_right), ">>="); m.add(fun(&Boxed_Number::bitwise_and), "&"); m.add(fun(&Boxed_Number::bitwise_complement), "~"); m.add(fun(&Boxed_Number::bitwise_xor), "^"); m.add(fun(&Boxed_Number::bitwise_or), "|"); m.add(fun(&Boxed_Number::assign_product), "*="); m.add(fun(&Boxed_Number::assign_quotient), "/="); m.add(fun(&Boxed_Number::assign_sum), "+="); m.add(fun(&Boxed_Number::assign_difference), "-="); m.add(fun(&Boxed_Number::quotient), "/"); m.add(fun(&Boxed_Number::shift_left), "<<"); m.add(fun(&Boxed_Number::product), "*"); m.add(fun(&Boxed_Number::remainder), "%"); m.add(fun(&Boxed_Number::shift_right), ">>"); } /// Create a bound function object. The first param is the function to bind /// the remaining parameters are the args to bind into the result static Boxed_Value bind_function(const Function_Params ¶ms) { if (params.empty()) { throw exception::arity_error(0, 1); } Const_Proxy_Function f = boxed_cast(params[0]); if (f->get_arity() != -1 && size_t(f->get_arity()) != params.size() - 1) { throw exception::arity_error(static_cast(params.size()), f->get_arity()); } return Boxed_Value(Const_Proxy_Function( std::make_shared(std::move(f), std::vector(params.begin() + 1, params.end())))); } static bool has_guard(const Const_Proxy_Function &t_pf) noexcept { auto pf = std::dynamic_pointer_cast(t_pf); return pf && pf->has_guard(); } static Const_Proxy_Function get_guard(const Const_Proxy_Function &t_pf) { const auto pf = std::dynamic_pointer_cast(t_pf); if (pf && pf->get_guard()) { return pf->get_guard(); } else { throw std::runtime_error("Function does not have a guard"); } } template static std::vector do_return_boxed_value_vector(FunctionType f, const dispatch::Proxy_Function_Base *b) { auto v = (b->*f)(); std::vector vbv; for (const auto &o : v) { vbv.push_back(const_var(o)); } return vbv; } static bool has_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) noexcept { const auto pf = std::dynamic_pointer_cast(t_pf); return bool(pf); } static const chaiscript::AST_Node &get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) { const auto pf = std::dynamic_pointer_cast(t_pf); if (pf) { return pf->get_parse_tree(); } else { throw std::runtime_error("Function does not have a parse tree"); } } template static auto return_boxed_value_vector(const Function &f) { return [f](const dispatch::Proxy_Function_Base *b) { return do_return_boxed_value_vector(f, b); }; } public: /// \brief perform all common bootstrap functions for std::string, void and POD types /// \param[in,out] m Module to add bootstrapped functions to /// \returns passed in Module static void bootstrap(Module &m) { m.add(user_type(), "void"); m.add(user_type(), "bool"); m.add(user_type(), "Object"); m.add(user_type(), "Number"); m.add(user_type(), "Function"); m.add(user_type(), "Assignable_Function"); m.add(user_type(), "exception"); m.add(fun(&dispatch::Proxy_Function_Base::get_arity), "get_arity"); m.add(fun(&dispatch::Proxy_Function_Base::operator==), "=="); m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_param_types)), "get_param_types"); m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_contained_functions)), "get_contained_functions"); m.add(fun([](const std::exception &e) { return std::string(e.what()); }), "what"); m.add(user_type(), "out_of_range"); m.add(user_type(), "logic_error"); m.add(chaiscript::base_class()); m.add(chaiscript::base_class()); m.add(chaiscript::base_class()); m.add(user_type(), "runtime_error"); m.add(chaiscript::base_class()); m.add(constructor(), "runtime_error"); 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::set_explicit), "set_explicit"); m.add(fun(&dispatch::Dynamic_Object::is_explicit), "is_explicit"); m.add(fun(&dispatch::Dynamic_Object::has_attr), "has_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(R"chaiscript( def Dynamic_Object::clone() { auto &new_o = Dynamic_Object(this.get_type_name()); for_each(this.get_attrs(), fun[new_o](x) { new_o.get_attr(x.first) = x.second; } ); new_o; } def `=`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name() { for_each(rhs.get_attrs(), fun[lhs](x) { lhs.get_attr(x.first) = clone(x.second); } ); } def `!=`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name() { var rhs_attrs := rhs.get_attrs(); var lhs_attrs := lhs.get_attrs(); if (rhs_attrs.size() != lhs_attrs.size()) { true; } else { return any_of(rhs_attrs, fun[lhs](x) { !lhs.has_attr(x.first) || lhs.get_attr(x.first) != x.second; } ); } } def `==`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name() { var rhs_attrs := rhs.get_attrs(); var lhs_attrs := lhs.get_attrs(); if (rhs_attrs.size() != lhs_attrs.size()) { false; } else { return all_of(rhs_attrs, fun[lhs](x) { lhs.has_attr(x.first) && lhs.get_attr(x.first) == x.second; } ); } } )chaiscript"); m.add(fun(&has_guard), "has_guard"); m.add(fun(&get_guard), "get_guard"); m.add(fun(&Boxed_Value::is_undef), "is_var_undef"); m.add(fun(&Boxed_Value::is_null), "is_var_null"); m.add(fun(&Boxed_Value::is_const), "is_var_const"); m.add(fun(&Boxed_Value::is_ref), "is_var_reference"); m.add(fun(&Boxed_Value::is_pointer), "is_var_pointer"); m.add(fun(&Boxed_Value::is_return_value), "is_var_return_value"); m.add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value"); m.add(fun(&Boxed_Value::is_type), "is_type"); m.add(fun(&Boxed_Value::get_attr), "get_var_attr"); m.add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs"); m.add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs"); m.add(fun(&Boxed_Value::get_type_info), "get_type_info"); m.add(user_type(), "Type_Info"); m.add(constructor(), "Type_Info"); operators::equal(m); m.add(fun(&Type_Info::is_const), "is_type_const"); m.add(fun(&Type_Info::is_reference), "is_type_reference"); m.add(fun(&Type_Info::is_void), "is_type_void"); m.add(fun(&Type_Info::is_undef), "is_type_undef"); m.add(fun(&Type_Info::is_pointer), "is_type_pointer"); m.add(fun(&Type_Info::is_arithmetic), "is_type_arithmetic"); m.add(fun(&Type_Info::name), "cpp_name"); m.add(fun(&Type_Info::bare_name), "cpp_bare_name"); m.add(fun(&Type_Info::bare_equal), "bare_equal"); basic_constructors("bool", m); operators::assign(m); operators::equal(m); operators::not_equal(m); m.add(fun([](const std::string &s) { return s; }), "to_string"); m.add(fun([](const bool b) { return std::string(b ? "true" : "false"); }), "to_string"); m.add(fun(&unknown_assign), "="); m.add(fun([](const Boxed_Value &bv) { throw bv; }), "throw"); m.add(fun([](const char c) { return std::string(1, c); }), "to_string"); m.add(fun(&Boxed_Number::to_string), "to_string"); bootstrap_pod_type("double", m); bootstrap_pod_type("long_double", m); bootstrap_pod_type("float", m); bootstrap_pod_type("int", m); bootstrap_pod_type("long", m); bootstrap_pod_type("unsigned_int", m); bootstrap_pod_type("unsigned_long", m); bootstrap_pod_type("long_long", m); bootstrap_pod_type("unsigned_long_long", m); bootstrap_pod_type("size_t", m); bootstrap_pod_type("char", m); bootstrap_pod_type("wchar_t", m); bootstrap_pod_type("char16_t", m); bootstrap_pod_type("char32_t", m); bootstrap_pod_type("int8_t", m); bootstrap_pod_type("int16_t", m); bootstrap_pod_type("int32_t", m); bootstrap_pod_type("int64_t", m); bootstrap_pod_type("uint8_t", m); bootstrap_pod_type("uint16_t", m); bootstrap_pod_type("uint32_t", m); bootstrap_pod_type("uint64_t", m); operators::logical_compliment(m); opers_arithmetic_pod(m); m.add(fun(&Build_Info::version_major), "version_major"); m.add(fun(&Build_Info::version_minor), "version_minor"); m.add(fun(&Build_Info::version_patch), "version_patch"); m.add(fun(&Build_Info::version), "version"); m.add(fun(&Build_Info::compiler_version), "compiler_version"); m.add(fun(&Build_Info::compiler_name), "compiler_name"); m.add(fun(&Build_Info::compiler_id), "compiler_id"); m.add(fun(&Build_Info::debug_build), "debug_build"); m.add(fun(&print), "print_string"); m.add(fun(&println), "println_string"); m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind"); m.add(fun(&shared_ptr_unconst_clone), "clone"); m.add(fun(&ptr_assign::type>), "="); m.add(fun(&ptr_assign::type>), "="); m.add(chaiscript::base_class()); m.add(fun([](dispatch::Assignable_Proxy_Function &t_lhs, const std::shared_ptr &t_rhs) { t_lhs.assign(t_rhs); }), "="); m.add(fun(&Boxed_Value::type_match), "type_match"); m.add(chaiscript::fun(&has_parse_tree), "has_parse_tree"); m.add(chaiscript::fun(&get_parse_tree), "get_parse_tree"); m.add(chaiscript::base_class()); m.add(chaiscript::base_class()); m.add(chaiscript::user_type(), "arithmetic_error"); m.add(chaiscript::base_class()); m.add(chaiscript::base_class()); // chaiscript::bootstrap::standard_library::vector_type > // >("AST_NodeVector", m); chaiscript::utility::add_class(m, "eval_error", {}, {{fun(&chaiscript::exception::eval_error::reason), "reason"}, {fun(&chaiscript::exception::eval_error::pretty_print), "pretty_print"}, {fun([](const chaiscript::exception::eval_error &t_eval_error) { std::vector retval; std::transform(t_eval_error.call_stack.begin(), t_eval_error.call_stack.end(), std::back_inserter(retval), &chaiscript::var); return retval; }), "call_stack"}}); chaiscript::utility::add_class(m, "File_Position", {constructor(), constructor()}, {{fun(&File_Position::line), "line"}, {fun(&File_Position::column), "column"}}); chaiscript::utility::add_class(m, "AST_Node", {}, {{fun(&AST_Node::text), "text"}, {fun(&AST_Node::identifier), "identifier"}, {fun(&AST_Node::filename), "filename"}, {fun(&AST_Node::start), "start"}, {fun(&AST_Node::end), "end"}, {fun(&AST_Node::to_string), "to_string"}, {fun([](const chaiscript::AST_Node &t_node) -> std::vector { std::vector retval; const auto children = t_node.get_children(); std::transform(children.begin(), children.end(), std::back_inserter(retval), &chaiscript::var &>); return retval; }), "children"}}); } }; } // namespace chaiscript::bootstrap #endif