diff --git a/CMakeLists.txt b/CMakeLists.txt index d1d78f09..3cfec822 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,13 +145,7 @@ endif() if(CMAKE_COMPILER_IS_GNUCC) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) - if(GCC_VERSION VERSION_LESS 4.9) - set(CPP14_FLAG "-std=c++1y") - else() - set(CPP14_FLAG "-std=c++1z") - endif() -else() - set(CPP14_FLAG "-std=c++1z") + set(CPP17_FLAG "-std=c++1z") endif() if(MSVC) @@ -178,7 +172,7 @@ if(MSVC) # how to workaround or fix the error. So I'm disabling it globally. add_definitions(/wd4503) else() - add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -Wno-noexcept-type -Wpedantic ${CPP14_FLAG}) + add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -Wno-noexcept-type -Wpedantic ${CPP17_FLAG}) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command -Wno-unused-template -Wno-undef ) @@ -196,12 +190,12 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") if(USE_LIBCXX) add_definitions(-stdlib=libc++) - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG} -stdlib=libc++") + set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP17_FLAG} -stdlib=libc++") else() - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}") + set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP17_FLAG}") endif() elseif(CMAKE_COMPILER_IS_GNUCC) - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}") + set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP17_FLAG}") endif() # limitations in MinGW require us to make an optimized build @@ -267,8 +261,8 @@ add_executable(chai src/main.cpp ${Chai_INCLUDES}) target_link_libraries(chai ${LIBS} ${CHAISCRIPT_LIBS}) if(BUILD_SAMPLES) - add_executable(example samples/example.cpp) - target_link_libraries(example ${LIBS}) + add_executable(sanity_checks src/sanity_checks.cpp) + target_link_libraries(sanity_checks ${LIBS}) add_executable(test_num_exceptions samples/test_num_exceptions.cpp) target_link_libraries(test_num_exceptions ${LIBS} ${CHAISCRIPT_LIBS}) add_executable(memory_leak_test samples/memory_leak_test.cpp) @@ -329,37 +323,6 @@ endif() if(BUILD_TESTING) - # Add catch tests macro - macro(ADD_CATCH_TESTS executable) - if (MSVC) - file(TO_NATIVE_PATH "${QT_LIBRARY_DIR}" QT_LIB_PATH) - set(NEWPATH "${QT_LIB_PATH};$ENV{PATH}") - else() - set(NEWPATH $ENV{PATH}) - endif() - - get_target_property(target_files ${executable} SOURCES) - - foreach(source ${target_files}) - if(NOT "${source}" MATCHES "/moc_.*cxx") - string(REGEX MATCH .*cpp source "${source}") - if(source) - file(READ "${source}" contents) - string(REGEX MATCHALL "TEST_CASE\\([ ]*\"[^\"]+\"" found_tests ${contents}) - foreach(hit ${found_tests}) - string(REGEX REPLACE "TEST_CASE\\([ ]*(\"[^\"]+\").*" "\\1" test_name ${hit}) - add_test(compiled.${test_name} "${executable}" ${test_name}) - set_tests_properties(compiled.${test_name} PROPERTIES TIMEOUT 660 ENVIRONMENT "PATH=${NEWPATH}") - endforeach() - endif() - endif() - endforeach() - endmacro() - - - - - option(UNIT_TEST_LIGHT "Unit tests light (expect module loading failures)" FALSE) add_test(version_check chai -c "if(\"\\\${ version() };\\\${version_major()};\\\${version_minor()};\\\${version_patch()}\" != \"${CHAI_VERSION};${CPACK_PACKAGE_VERSION_MAJOR};${CPACK_PACKAGE_VERSION_MINOR};${CPACK_PACKAGE_VERSION_PATCH}\") { exit(-1) }") diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 71a672b9..d5eb774a 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -105,47 +105,47 @@ namespace chaiscript { } struct Build_Info { - constexpr static int version_major() noexcept + [[nodiscard]] constexpr static int version_major() noexcept { return chaiscript::version_major; } - constexpr static int version_minor() noexcept + [[nodiscard]] constexpr static int version_minor() noexcept { return chaiscript::version_minor; } - constexpr static int version_patch() noexcept + [[nodiscard]] constexpr static int version_patch() noexcept { return chaiscript::version_patch; } - static std::string version() + [[nodiscard]] static std::string version() { return std::to_string(version_major()) + '.' + std::to_string(version_minor()) + '.' + std::to_string(version_patch()); } - static std::string compiler_id() + [[nodiscard]] static std::string compiler_id() { return compiler_name() + '-' + compiler_version(); } - static std::string build_id() + [[nodiscard]] static std::string build_id() { return compiler_id() + (debug_build()?"-Debug":"-Release"); } - static std::string compiler_version() + [[nodiscard]] static std::string compiler_version() { return chaiscript::compiler_version; } - static std::string compiler_name() + [[nodiscard]] static std::string compiler_name() { return chaiscript::compiler_name; } - constexpr static bool debug_build() noexcept + [[nodiscard]] constexpr static bool debug_build() noexcept { return chaiscript::debug_build; } @@ -153,7 +153,7 @@ namespace chaiscript { template - constexpr auto parse_num(const std::string_view t_str) noexcept -> typename std::enable_if::value, T>::type + [[nodiscard]] constexpr auto parse_num(const std::string_view t_str) noexcept -> typename std::enable_if::value, T>::type { T t = 0; for (const auto c : t_str) { @@ -168,7 +168,7 @@ namespace chaiscript { template - auto parse_num(const std::string_view t_str) -> typename std::enable_if::value, T>::type + [[nodiscard]] auto parse_num(const std::string_view t_str) -> typename std::enable_if::value, T>::type { T t = 0; T base{}; @@ -217,16 +217,26 @@ namespace chaiscript { } } return exponent ? base * std::pow(T(10), t * static_cast(exponent)) : t; - - } + struct str_equal { + [[nodiscard]] bool operator()(const std::string &t_lhs, const std::string &t_rhs) const noexcept { + return t_lhs == t_rhs; + } + template + [[nodiscard]] constexpr bool operator()(const LHS &t_lhs, const RHS &t_rhs) const noexcept { + return std::equal(t_lhs.begin(), t_lhs.end(), t_rhs.begin(), t_rhs.end()); + } + struct is_transparent{}; + }; + + struct str_less { - bool operator()(const std::string &t_lhs, const std::string &t_rhs) const noexcept { + [[nodiscard]] bool operator()(const std::string &t_lhs, const std::string &t_rhs) const noexcept { return t_lhs < t_rhs; } template - constexpr bool operator()(const LHS &t_lhs, const RHS &t_rhs) const noexcept { + [[nodiscard]] constexpr bool operator()(const LHS &t_lhs, const RHS &t_rhs) const noexcept { return std::lexicographical_compare(t_lhs.begin(), t_lhs.end(), t_rhs.begin(), t_rhs.end()); } struct is_transparent{}; @@ -240,6 +250,37 @@ namespace chaiscript { External_Scripts }; + template + struct is_nothrow_forward_constructible + : std::bool_constant()})> + { + }; + + template< class From, class To > + inline constexpr bool is_nothrow_forward_constructible_v + = is_nothrow_forward_constructible::value; + + template + [[nodiscard]] constexpr auto make_container(T && ... t) + { + Container c; + c.reserve(sizeof...(t)); + (c.push_back(std::forward(t)), ...); + return c; + } + + + template + [[nodiscard]] auto make_vector(T && ... t) + { + using container_type = + std::vector...>>; + + return make_container(std::forward(t)...); + } + + + static inline std::vector default_options() { #ifdef CHAISCRIPT_NO_DYNLOAD diff --git a/include/chaiscript/chaiscript_stdlib.hpp b/include/chaiscript/chaiscript_stdlib.hpp index ed56b234..2407fcf0 100644 --- a/include/chaiscript/chaiscript_stdlib.hpp +++ b/include/chaiscript/chaiscript_stdlib.hpp @@ -42,7 +42,7 @@ namespace chaiscript { public: - static ModulePtr library() + [[nodiscard]] static ModulePtr library() { auto lib = std::make_shared(); bootstrap::Bootstrap::bootstrap(*lib); diff --git a/include/chaiscript/dispatchkit/any.hpp b/include/chaiscript/dispatchkit/any.hpp index d13b653f..f8f94f26 100644 --- a/include/chaiscript/dispatchkit/any.hpp +++ b/include/chaiscript/dispatchkit/any.hpp @@ -83,7 +83,7 @@ namespace chaiscript { public: // construct/copy/destruct constexpr Any() noexcept = default; - Any(Any &&) = default; + Any(Any &&) noexcept = default; Any &operator=(Any &&t_any) = default; Any(const Any &t_any) @@ -128,7 +128,7 @@ namespace chaiscript { // queries bool empty() const noexcept { - return !bool(m_data); + return !static_cast(m_data); } const std::type_info & type() const noexcept diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 6178d4f6..a9c41d30 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -152,7 +152,7 @@ namespace chaiscript /// Specific version of shared_ptr_clone just for Proxy_Functions template - std::shared_ptr::type> shared_ptr_unconst_clone(const std::shared_ptr::type> &p) + std::shared_ptr> shared_ptr_unconst_clone(const std::shared_ptr> &p) { return std::const_pointer_cast::type>(p); } @@ -242,7 +242,7 @@ namespace chaiscript /// 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 std::vector ¶ms) + static Boxed_Value bind_function(const Function_Params ¶ms) { if (params.empty()) { throw exception::arity_error(0, 1); diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 2bdba55a..04ca84d0 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -178,13 +178,6 @@ namespace chaiscript detail::input_range_type_impl >(type,m); detail::input_range_type_impl >("Const_" + type,m); } - template - ModulePtr input_range_type(const std::string &type) - { - auto m = std::make_shared(); - input_range_type(type, *m); - return m; - } /// Add random_access_container concept to the given ContainerType @@ -197,7 +190,7 @@ namespace chaiscript m.add( fun( [](ContainerType &c, int index) -> typename ContainerType::reference { - /// \todo we are prefering to keep the key as 'int' to avoid runtime conversions + /// \todo we are preferring to keep the key as 'int' to avoid runtime conversions /// during dispatch. reevaluate return c.at(static_cast(index)); }), "[]"); @@ -205,19 +198,11 @@ namespace chaiscript m.add( fun( [](const ContainerType &c, int index) -> typename ContainerType::const_reference { - /// \todo we are prefering to keep the key as 'int' to avoid runtime conversions + /// \todo we are preferring to keep the key as 'int' to avoid runtime conversions /// during dispatch. reevaluate return c.at(static_cast(index)); }), "[]"); } - template - ModulePtr random_access_container_type(const std::string &type) - { - auto m = std::make_shared(); - random_access_container_type(type, *m); - return m; - } - /// Add assignable concept to the given ContainerType @@ -228,14 +213,6 @@ namespace chaiscript copy_constructor(type, m); operators::assign(m); } - template - ModulePtr assignable_type(const std::string &type) - { - auto m = std::make_shared(); - assignable_type(type, *m); - return m; - } - /// Add container resize concept to the given ContainerType /// http://www.cplusplus.com/reference/stl/ @@ -245,14 +222,6 @@ namespace chaiscript m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type& val) { return a->resize(n, val); } ), "resize"); m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->resize(n); } ), "resize"); } - template - ModulePtr resizable_type(const std::string &type) - { - auto m = std::make_shared(); - resizable_type(type, *m); - return m; - } - /// Add container reserve concept to the given ContainerType /// http://www.cplusplus.com/reference/stl/ @@ -262,14 +231,6 @@ namespace chaiscript m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->reserve(n); } ), "reserve"); m.add(fun([](const ContainerType *a) { return a->capacity(); } ), "capacity"); } - template - ModulePtr reservable_type(const std::string &type) - { - auto m = std::make_shared(); - reservable_type(type, *m); - return m; - } - /// Add container concept to the given ContainerType /// http://www.sgi.com/tech/stl/Container.html @@ -280,14 +241,6 @@ namespace chaiscript m.add(fun([](const ContainerType *a) { return a->empty(); } ), "empty"); m.add(fun([](ContainerType *a) { a->clear(); } ), "clear"); } - template - ModulePtr container_type(const std::string& type) - { - auto m = std::make_shared(); - container_type(type, *m); - return m; - } - /// Add default constructable concept to the given Type /// http://www.sgi.com/tech/stl/DefaultConstructible.html @@ -296,15 +249,6 @@ namespace chaiscript { m.add(constructor(), type); } - template - ModulePtr default_constructible_type(const std::string& type) - { - auto m = std::make_shared(); - default_constructible_type(type, *m); - return m; - } - - /// Add sequence concept to the given ContainerType /// http://www.sgi.com/tech/stl/Sequence.html @@ -322,13 +266,6 @@ namespace chaiscript m.add(fun(&detail::erase_at), "erase_at"); } - template - ModulePtr sequence_type(const std::string &type) - { - auto m = std::make_shared(); - sequence_type(type, *m); - return m; - } /// Add back insertion sequence concept to the given ContainerType /// http://www.sgi.com/tech/stl/BackInsertionSequence.html @@ -380,14 +317,6 @@ namespace chaiscript m.add(fun(&ContainerType::pop_back), "pop_back"); } - template - ModulePtr back_insertion_sequence_type(const std::string &type) - { - auto m = std::make_shared(); - back_insertion_sequence_type(type, *m); - return m; - } - /// Front insertion sequence @@ -442,14 +371,6 @@ namespace chaiscript m.add(fun(static_cast(&ContainerType::pop_front)), "pop_front"); } - template - ModulePtr front_insertion_sequence_type(const std::string &type) - { - auto m = std::make_shared(); - front_insertion_sequence_type(type, *m); - return m; - } - /// bootstrap a given PairType /// http://www.sgi.com/tech/stl/pair.html @@ -464,14 +385,6 @@ namespace chaiscript basic_constructors(type, m); m.add(constructor(), type); } - template - ModulePtr pair_type(const std::string &type) - { - auto m = std::make_shared(); - pair_type(type, *m); - return m; - } - /// Add pair associative container concept to the given ContainerType @@ -482,14 +395,6 @@ namespace chaiscript { pair_type(type + "_Pair", m); } - template - ModulePtr pair_associative_container_type(const std::string &type) - { - auto m = std::make_shared(); - pair_associative_container_type(type, *m); - return m; - } - /// Add unique associative container concept to the given ContainerType /// http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html @@ -513,14 +418,6 @@ namespace chaiscript } }()); } - template - ModulePtr unique_associative_container_type(const std::string &type) - { - auto m = std::make_shared(); - unique_associative_container_type(type, *m); - return m; - } - /// Add a MapType container /// http://www.sgi.com/tech/stl/Map.html @@ -568,14 +465,6 @@ namespace chaiscript pair_associative_container_type(type, m); input_range_type(type, m); } - template - ModulePtr map_type(const std::string &type) - { - auto m = std::make_shared(); - map_type(type, *m); - return m; - } - /// http://www.sgi.com/tech/stl/List.html template @@ -592,14 +481,6 @@ namespace chaiscript assignable_type(type, m); input_range_type(type, m); } - template - ModulePtr list_type(const std::string &type) - { - auto m = std::make_shared(); - list_type(type, m); - return m; - } - /// Create a vector type with associated concepts /// http://www.sgi.com/tech/stl/Vector.html @@ -665,13 +546,6 @@ namespace chaiscript ); } } - template - ModulePtr vector_type(const std::string &type) - { - auto m = std::make_shared(); - vector_type(type, *m); - return m; - } /// Add a String container /// http://www.sgi.com/tech/stl/basic_string.html @@ -717,14 +591,6 @@ namespace chaiscript m.add(fun([](const String *s) { return s->data(); } ), "data"); m.add(fun([](const String *s, size_t pos, size_t len) { return s->substr(pos, len); } ), "substr"); } - template - ModulePtr string_type(const std::string &type) - { - auto m = std::make_shared(); - string_type(type, *m); - return m; - } - /// Add a MapType container @@ -738,13 +604,6 @@ namespace chaiscript m.add(fun([](FutureType &t) { return t.get(); }), "get"); m.add(fun(&FutureType::wait), "wait"); } - template - ModulePtr future_type(const std::string &type) - { - auto m = std::make_shared(); - future_type(type, *m); - return m; - } } } } diff --git a/include/chaiscript/dispatchkit/boxed_value.hpp b/include/chaiscript/dispatchkit/boxed_value.hpp index c8ee236e..b979733d 100644 --- a/include/chaiscript/dispatchkit/boxed_value.hpp +++ b/include/chaiscript/dispatchkit/boxed_value.hpp @@ -194,7 +194,7 @@ namespace chaiscript public: /// Basic Boxed_Value constructor template::type>::value>::type> + typename = std::enable_if_t>>> explicit Boxed_Value(T &&t, bool t_return_value = false) : m_data(Object_Data::get(std::forward(t), t_return_value)) { diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 576b0f9d..3fcb38cd 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -36,6 +36,7 @@ #include "proxy_functions.hpp" #include "type_info.hpp" #include "short_alloc.hpp" +#include "../utility/quick_flat_map.hpp" namespace chaiscript { class Boxed_Number; @@ -313,14 +314,14 @@ namespace chaiscript return arity; } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const noexcept override + bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { return std::any_of(std::begin(m_funcs), std::end(m_funcs), [&vals, &t_conversions](const Proxy_Function &f){ return f->call_match(vals, t_conversions); }); } protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { return dispatch::dispatch(m_funcs, params, t_conversions); } @@ -388,7 +389,7 @@ namespace chaiscript using SmallVector = std::vector; - using Scope = SmallVector>; + using Scope = utility::QuickFlatMap; using StackData = SmallVector; using Stacks = SmallVector; using Call_Param_List = SmallVector; @@ -409,24 +410,13 @@ namespace chaiscript void push_stack() { stacks.emplace_back(1); -// stacks.emplace_back(StackData(1, Scope(scope_allocator), stack_data_allocator)); } void push_call_params() { call_params.emplace_back(); -// call_params.emplace_back(Call_Param_List(call_param_list_allocator)); } - //Scope::allocator_type::arena_type scope_allocator; - //StackData::allocator_type::arena_type stack_data_allocator; - //Stacks::allocator_type::arena_type stacks_allocator; - //Call_Param_List::allocator_type::arena_type call_param_list_allocator; - //Call_Params::allocator_type::arena_type call_params_allocator; - -// Stacks stacks = Stacks(stacks_allocator); -// Call_Params call_params = Call_Params(call_params_allocator); - Stacks stacks; Call_Params call_params; @@ -440,14 +430,14 @@ namespace chaiscript public: using Type_Name_Map = std::map; - using Scope = std::vector>; + using Scope = utility::QuickFlatMap; using StackData = Stack_Holder::StackData; struct State { - std::vector>>> m_functions; - std::vector> m_function_objects; - std::vector> m_boxed_functions; + utility::QuickFlatMap>, str_equal> m_functions; + utility::QuickFlatMap m_function_objects; + utility::QuickFlatMap m_boxed_functions; std::map m_global_objects; Type_Name_Map m_types; }; @@ -486,12 +476,7 @@ namespace chaiscript for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem) { - auto itr = std::find_if(stack_elem->begin(), stack_elem->end(), - [&](const std::pair &o) { - return o.first == name; - }); - - if (itr != stack_elem->end()) + if (auto itr = stack_elem->find(name); itr != stack_elem->end()) { itr->second = std::move(obj); return; @@ -504,38 +489,32 @@ namespace chaiscript /// Adds a named object to the current scope /// \warning This version does not check the validity of the name /// it is meant for internal use only - Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj, Stack_Holder &t_holder) + Boxed_Value &add_get_object(std::string t_name, Boxed_Value obj, Stack_Holder &t_holder) { auto &stack_elem = get_stack_data(t_holder).back(); - if (std::any_of(stack_elem.begin(), stack_elem.end(), - [&](const std::pair &o) { - return o.first == t_name; - })) + if (auto result = stack_elem.insert(std::pair{std::move(t_name), std::move(obj)}); result.second) { - throw chaiscript::exception::name_conflict_error(t_name); + return result.first->second; + } else { + //insert failed + throw chaiscript::exception::name_conflict_error(result.first->first); } - - return stack_elem.emplace_back(t_name, std::move(obj)).second; } /// Adds a named object to the current scope /// \warning This version does not check the validity of the name /// it is meant for internal use only - void add_object(const std::string &t_name, Boxed_Value obj, Stack_Holder &t_holder) + void add_object(std::string t_name, Boxed_Value obj, Stack_Holder &t_holder) { auto &stack_elem = get_stack_data(t_holder).back(); - if (std::any_of(stack_elem.begin(), stack_elem.end(), - [&](const std::pair &o) { - return o.first == t_name; - })) + if (auto result = stack_elem.insert(std::pair{std::move(t_name), std::move(obj)}); !result.second) { - throw chaiscript::exception::name_conflict_error(t_name); + //insert failed + throw chaiscript::exception::name_conflict_error(result.first->first); } - - stack_elem.emplace_back(t_name, std::move(obj)); } @@ -566,46 +545,30 @@ namespace chaiscript } /// Adds a new global (non-const) shared object, between all the threads - Boxed_Value add_global_no_throw(const Boxed_Value &obj, const std::string &name) + Boxed_Value add_global_no_throw(Boxed_Value obj, std::string name) { chaiscript::detail::threading::unique_lock l(m_mutex); - const auto itr = m_state.m_global_objects.find(name); - if (itr == m_state.m_global_objects.end()) - { - m_state.m_global_objects.insert(std::make_pair(name, obj)); - return obj; - } else { - return itr->second; - } + return m_state.m_global_objects.insert(std::pair{std::move(name), std::move(obj)}).first->second; } /// Adds a new global (non-const) shared object, between all the threads - void add_global(const Boxed_Value &obj, const std::string &name) + void add_global(Boxed_Value obj, std::string name) { chaiscript::detail::threading::unique_lock l(m_mutex); - if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) - { - throw chaiscript::exception::name_conflict_error(name); - } else { - m_state.m_global_objects.insert(std::make_pair(name, obj)); + if (auto result = m_state.m_global_objects.insert(std::pair{std::move(name), std::move(obj)}); !result.second) { + // insert failed + throw chaiscript::exception::name_conflict_error(result.first->first); } } /// Updates an existing global shared object or adds a new global shared object if not found - void set_global(const Boxed_Value &obj, const std::string &name) + void set_global(Boxed_Value obj, std::string name) { chaiscript::detail::threading::unique_lock l(m_mutex); - - const auto itr = m_state.m_global_objects.find(name); - if (itr != m_state.m_global_objects.end()) - { - itr->second.assign(obj); - } else { - m_state.m_global_objects.insert(std::make_pair(name, obj)); - } + m_state.m_global_objects.insert_or_assign(std::move(name), std::move(obj)); } /// Adds a new scope to the stack @@ -688,7 +651,7 @@ namespace chaiscript } else if ((loc & static_cast(Loc::is_local)) != 0u) { auto &stack = get_stack_data(t_holder); - return stack[stack.size() - 1 - ((loc & static_cast(Loc::stack_mask)) >> 16)][loc & static_cast(Loc::loc_mask)].second; + return stack[stack.size() - 1 - ((loc & static_cast(Loc::stack_mask)) >> 16)].at_index(loc & static_cast(Loc::loc_mask)); } // Is the value we are looking for a global or function? @@ -782,9 +745,7 @@ namespace chaiscript const auto &funs = get_functions_int(); - auto itr = find_keyed_value(funs, t_name, t_hint); - - if (itr != funs.end()) + if (const auto itr = funs.find(t_name, t_hint); itr != funs.end()) { return std::make_pair(std::distance(funs.begin(), itr), itr->second); } else { @@ -808,9 +769,7 @@ namespace chaiscript { const auto &funs = get_boxed_functions_int(); - auto itr = find_keyed_value(funs, t_name, t_hint); - - if (itr != funs.end()) + if (const auto itr = funs.find(t_name, t_hint); itr != funs.end()) { return std::make_pair(std::distance(funs.begin(), itr), itr->second); } else { @@ -824,8 +783,7 @@ namespace chaiscript { chaiscript::detail::threading::shared_lock l(m_mutex); - const auto &functions = get_functions_int(); - return find_keyed_value(functions, name) != functions.end(); + return get_functions_int().count(name) > 0; } /// \returns All values in the local thread state in the parent scope, or if it doesn't exist, @@ -935,7 +893,7 @@ namespace chaiscript return m_conversions; } - static bool is_attribute_call(const std::vector &t_funs, const std::vector &t_params, + static bool is_attribute_call(const std::vector &t_funs, const Function_Params &t_params, bool t_has_params, const Type_Conversions_State &t_conversions) noexcept { if (!t_has_params || t_params.empty()) { @@ -956,17 +914,17 @@ namespace chaiscript #pragma warning(push) #pragma warning(disable : 4715) #endif - Boxed_Value call_member(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector ¶ms, bool t_has_params, + Boxed_Value call_member(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const Function_Params ¶ms, bool t_has_params, const Type_Conversions_State &t_conversions) { uint_fast32_t loc = t_loc; const auto funs = get_function(t_name, loc); if (funs.first != loc) { t_loc = uint_fast32_t(funs.first); } - const auto do_attribute_call = - [this](int l_num_params, const std::vector &l_params, const std::vector &l_funs, const Type_Conversions_State &l_conversions)->Boxed_Value + const auto do_attribute_call = + [this](int l_num_params, Function_Params l_params, const std::vector &l_funs, const Type_Conversions_State &l_conversions)->Boxed_Value { - std::vector attr_params{l_params.begin(), l_params.begin() + l_num_params}; + Function_Params attr_params(l_params.begin(), l_params.begin() + l_num_params); Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions); if (l_num_params < int(l_params.size()) || bv.get_type_info().bare_equal(user_type())) { struct This_Foist { @@ -1049,11 +1007,12 @@ namespace chaiscript if (!functions.empty()) { try { if (is_no_param) { - auto tmp_params(params); + auto tmp_params = params.to_vector(); tmp_params.insert(tmp_params.begin() + 1, var(t_name)); - return do_attribute_call(2, tmp_params, functions, t_conversions); + return do_attribute_call(2, Function_Params(tmp_params), functions, t_conversions); } else { - return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector(params.begin()+1, params.end()))}, t_conversions); + std::array p{params[0], var(t_name), var(std::vector(params.begin()+1, params.end()))}; + return dispatch::dispatch(functions, Function_Params{p}, t_conversions); } } catch (const dispatch::option_explicit_set &e) { throw chaiscript::exception::dispatch_error(params, std::vector(funs.second->begin(), funs.second->end()), @@ -1076,7 +1035,7 @@ namespace chaiscript - Boxed_Value call_function(const std::string_view &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector ¶ms, + Boxed_Value call_function(const std::string_view &t_name, std::atomic_uint_fast32_t &t_loc, const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const { uint_fast32_t loc = t_loc; @@ -1124,7 +1083,7 @@ namespace chaiscript /// Returns true if a call can be made that consists of the first parameter /// (the function) with the remaining parameters as its arguments. - Boxed_Value call_exists(const std::vector ¶ms) const + Boxed_Value call_exists(const Function_Params ¶ms) const { if (params.empty()) { @@ -1134,7 +1093,7 @@ namespace chaiscript const auto &f = this->boxed_cast(params[0]); const Type_Conversions_State convs(m_conversions, m_conversions.conversion_saves()); - return const_var(f->call_match(std::vector(params.begin() + 1, params.end()), convs)); + return const_var(f->call_match(Function_Params(params.begin() + 1, params.end()), convs)); } /// Dump all system info to stdout @@ -1195,11 +1154,6 @@ namespace chaiscript m_state = t_state; } - static void save_function_params(Stack_Holder &t_s, std::initializer_list t_params) - { - t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params); - } - static void save_function_params(Stack_Holder &t_s, std::vector &&t_params) { for (auto &¶m : t_params) @@ -1208,22 +1162,17 @@ namespace chaiscript } } - static void save_function_params(Stack_Holder &t_s, const std::vector &t_params) + static void save_function_params(Stack_Holder &t_s, const Function_Params &t_params) { t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params.begin(), t_params.end()); } - void save_function_params(std::initializer_list t_params) - { - save_function_params(*m_stack_holder, t_params); - } - void save_function_params(std::vector &&t_params) { save_function_params(*m_stack_holder, std::move(t_params)); } - void save_function_params(const std::vector &t_params) + void save_function_params(const Function_Params &t_params) { save_function_params(*m_stack_holder, t_params); } @@ -1292,32 +1241,32 @@ namespace chaiscript private: - const std::vector> &get_boxed_functions_int() const noexcept + const decltype(State::m_boxed_functions) &get_boxed_functions_int() const noexcept { return m_state.m_boxed_functions; } - std::vector> &get_boxed_functions_int() noexcept + decltype(State::m_boxed_functions) &get_boxed_functions_int() noexcept { return m_state.m_boxed_functions; } - const std::vector> &get_function_objects_int() const noexcept + const decltype(State::m_function_objects) &get_function_objects_int() const noexcept { return m_state.m_function_objects; } - std::vector> &get_function_objects_int() noexcept + decltype(State::m_function_objects) &get_function_objects_int() noexcept { return m_state.m_function_objects; } - const std::vector>>> &get_functions_int() const noexcept + const decltype(State::m_functions) &get_functions_int() const noexcept { return m_state.m_functions; } - std::vector>>> &get_functions_int() noexcept + decltype(State::m_functions) &get_functions_int() noexcept { return m_state.m_functions; } @@ -1406,63 +1355,17 @@ namespace chaiscript return false; } - - - template - static void add_keyed_value(Container &t_c, const Key &t_key, Value &&t_value) - { - auto itr = find_keyed_value(t_c, t_key); - - if (itr == t_c.end()) { - t_c.reserve(t_c.size() + 1); // tightly control growth of memory usage here - t_c.emplace_back(t_key, std::forward(t_value)); - } else { - using value_type = typename Container::value_type; - *itr = value_type(t_key, std::forward(t_value)); - } - } - - template - static typename Container::iterator find_keyed_value(Container &t_c, const Key &t_key) noexcept - { - return std::find_if(t_c.begin(), t_c.end(), - [&t_key](const typename Container::value_type &o) { - return o.first == t_key; - }); - } - - template - static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key) noexcept - { - return std::find_if(t_c.begin(), t_c.end(), - [&t_key](const typename Container::value_type &o) { - return std::equal(o.first.begin(), o.first.end(), t_key.begin(), t_key.end()); - }); - } - - template - static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key, const size_t t_hint) noexcept - { - if (t_c.size() > t_hint && t_c[t_hint].first == t_key) { - return std::next(t_c.begin(), static_cast::difference_type>(t_hint)); - } else { - return find_keyed_value(t_c, t_key); - } - } - - /// Implementation detail for adding a function. /// \throws exception::name_conflict_error if there's a function matching the given one being added void add_function(const Proxy_Function &t_f, const std::string &t_name) { chaiscript::detail::threading::unique_lock l(m_mutex); - auto &funcs = get_functions_int(); - - auto itr = find_keyed_value(funcs, t_name); - Proxy_Function new_func = [&]() -> Proxy_Function { + auto &funcs = get_functions_int(); + auto itr = funcs.find(t_name); + if (itr != funcs.end()) { auto vec = *itr->second; @@ -1483,17 +1386,21 @@ namespace chaiscript // if the function is the only function but it also contains // arithmetic operators, we must wrap it in a dispatch function // to allow for automatic arithmetic type conversions - std::vector vec({t_f}); - funcs.emplace_back(t_name, std::make_shared>(vec)); + std::vector vec; + vec.push_back(t_f); + funcs.insert(std::pair{t_name, std::make_shared>(vec)}); return std::make_shared(std::move(vec)); } else { - funcs.emplace_back(t_name, std::make_shared>(std::initializer_list({t_f}))); + auto vec = std::make_shared>(); + vec->push_back(t_f); + funcs.insert(std::pair{t_name, vec}); return t_f; } }(); - add_keyed_value(get_boxed_functions_int(), t_name, const_var(new_func)); - add_keyed_value(get_function_objects_int(), t_name, std::move(new_func)); + + get_boxed_functions_int().insert_or_assign(t_name, const_var(new_func)); + get_function_objects_int().insert_or_assign(t_name, std::move(new_func)); } mutable chaiscript::detail::threading::shared_mutex m_mutex; diff --git a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp index a9034ed1..5f1c80ff 100644 --- a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp @@ -83,7 +83,7 @@ namespace chaiscript bool is_attribute_function() const noexcept override { return m_is_attribute; } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const noexcept override + bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions)) { @@ -99,7 +99,7 @@ namespace chaiscript } protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { if (dynamic_object_typename_match(params, m_type_name, m_ti, t_conversions)) { @@ -148,7 +148,7 @@ namespace chaiscript } - bool dynamic_object_typename_match(const std::vector &bvs, const std::string &name, + bool dynamic_object_typename_match(const Function_Params &bvs, const std::string &name, const std::unique_ptr &ti, const Type_Conversions_State &t_conversions) const noexcept { if (!bvs.empty()) @@ -205,22 +205,22 @@ namespace chaiscript return (dc != nullptr) && dc->m_type_name == m_type_name && (*dc->m_func) == (*m_func); } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override + bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const 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); + return m_func->call_match(Function_Params{new_vals}, t_conversions); } protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { auto bv = Boxed_Value(Dynamic_Object(m_type_name), true); std::vector new_params{bv}; new_params.insert(new_params.end(), params.begin(), params.end()); - (*m_func)(new_params, t_conversions); + (*m_func)(Function_Params{new_params}, t_conversions); return bv; } diff --git a/include/chaiscript/dispatchkit/function_call_detail.hpp b/include/chaiscript/dispatchkit/function_call_detail.hpp index e482a287..6071898b 100644 --- a/include/chaiscript/dispatchkit/function_call_detail.hpp +++ b/include/chaiscript/dispatchkit/function_call_detail.hpp @@ -29,66 +29,7 @@ namespace chaiscript { namespace detail { - /// Internal helper class for handling the return - /// value of a build_function_caller - template - struct Function_Caller_Ret - { - static Ret call(const std::vector &t_funcs, - const std::vector ¶ms, const Type_Conversions_State *t_conversions) - { - if (t_conversions != nullptr) { - return boxed_cast(dispatch::dispatch(t_funcs, params, *t_conversions), t_conversions); - } else { - Type_Conversions conv; - Type_Conversions_State state(conv, conv.conversion_saves()); - return boxed_cast(dispatch::dispatch(t_funcs, params, state), t_conversions); - } - } - }; - - /** - * Specialization for arithmetic return types - */ - template - struct Function_Caller_Ret - { - static Ret call(const std::vector &t_funcs, - const std::vector ¶ms, const Type_Conversions_State *t_conversions) - { - if (t_conversions != nullptr) { - return Boxed_Number(dispatch::dispatch(t_funcs, params, *t_conversions)).get_as(); - } else { - Type_Conversions conv; - Type_Conversions_State state(conv, conv.conversion_saves()); - return Boxed_Number(dispatch::dispatch(t_funcs, params, state)).get_as(); - } - } - }; - - - /** - * Specialization for void return types - */ - template<> - struct Function_Caller_Ret - { - static void call(const std::vector &t_funcs, - const std::vector ¶ms, const Type_Conversions_State *t_conversions) - { - if (t_conversions != nullptr) { - dispatch::dispatch(t_funcs, params, *t_conversions); - } else { - Type_Conversions conv; - Type_Conversions_State state(conv, conv.conversion_saves()); - dispatch::dispatch(t_funcs, params, state); - } - } - }; - - /** - * used internally for unwrapping a function call's types - */ + /// used internally for unwrapping a function call's types template struct Build_Function_Caller_Helper { @@ -98,41 +39,44 @@ namespace chaiscript { } + Ret call(const Function_Params ¶ms, const Type_Conversions_State &t_state) + { + if constexpr (std::is_arithmetic_v) { + return Boxed_Number(dispatch::dispatch(m_funcs, params, t_state)).get_as(); + } else if constexpr (std::is_same_v) { + dispatch::dispatch(m_funcs, params, t_state); + } else { + return boxed_cast(dispatch::dispatch(m_funcs, params, t_state), &t_state); + } + } + template Ret operator()(P&& ... param) { + std::array params{box

(std::forward

(param))...}; + if (m_conversions) { Type_Conversions_State state(*m_conversions, m_conversions->conversion_saves()); - return Function_Caller_Ret::value && !std::is_same::value>::call(m_funcs, { - box

(std::forward

(param))... - }, &state - ); + return call(Function_Params{params}, state); } else { - return Function_Caller_Ret::value && !std::is_same::value>::call(m_funcs, { - box

(std::forward

(param))... - }, nullptr - ); + Type_Conversions conv; + Type_Conversions_State state(conv, conv.conversion_saves()); + return call(Function_Params{params}, state); } } - template - static auto box(Q&& q) -> typename std::enable_if::value&&!std::is_same::type>::type>::value, Boxed_Value>::type - { - return Boxed_Value(std::ref(std::forward(q))); - } template - static auto box(Q&& q) -> typename std::enable_if::value&&!std::is_same::type>::type>::value, Boxed_Value>::type - { - return Boxed_Value(std::forward(q)); - } - - template - static Boxed_Value box(Boxed_Value bv) noexcept - { - return bv; - } + static Boxed_Value box(Q &&q) { + if constexpr (std::is_same_v>) { + return std::forward(q); + } else if constexpr (std::is_reference_v

) { + return Boxed_Value(std::ref(std::forward(q))); + } else { + return Boxed_Value(std::forward(q)); + } + } std::vector m_funcs; diff --git a/include/chaiscript/dispatchkit/function_params.hpp b/include/chaiscript/dispatchkit/function_params.hpp new file mode 100644 index 00000000..090f7004 --- /dev/null +++ b/include/chaiscript/dispatchkit/function_params.hpp @@ -0,0 +1,81 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2017, 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_FUNCTION_PARAMS_HPP +#define CHAISCRIPT_FUNCTION_PARAMS_HPP + + +#include "boxed_value.hpp" + +namespace chaiscript { + + class Function_Params + { + public: + constexpr Function_Params(const Boxed_Value * const t_begin, const Boxed_Value * const t_end) + : m_begin(t_begin), m_end(t_end) + { + } + + explicit Function_Params(const Boxed_Value &bv) + : m_begin(&bv), m_end(m_begin + 1) + { + } + + explicit Function_Params(const std::vector &vec) + : m_begin(&vec.front()), m_end(&vec.front() + vec.size()) + { + } + + template + constexpr explicit Function_Params(const std::array &a) + : m_begin(std::begin(a)), m_end(std::end(a)) + { + } + + [[nodiscard]] constexpr const Boxed_Value &operator[](const std::size_t t_i) const noexcept { + return m_begin[t_i]; + } + + [[nodiscard]] constexpr const Boxed_Value *begin() const noexcept { + return m_begin; + } + + [[nodiscard]] constexpr const Boxed_Value &front() const noexcept { + return *m_begin; + } + + [[nodiscard]] constexpr const Boxed_Value *end() const noexcept { + return m_end; + } + + [[nodiscard]] constexpr std::size_t size() const noexcept { + return m_end - m_begin; + } + + std::vector to_vector() const { + return std::vector{m_begin, m_end}; + } + + [[nodiscard]] constexpr bool empty() const noexcept { + return m_begin == m_end; + } + + private: + const Boxed_Value *m_begin = nullptr; + const Boxed_Value *m_end = nullptr; + + }; + +} + + +#endif + diff --git a/include/chaiscript/dispatchkit/handle_return.hpp b/include/chaiscript/dispatchkit/handle_return.hpp index 7f611a6f..4ae6dc5b 100644 --- a/include/chaiscript/dispatchkit/handle_return.hpp +++ b/include/chaiscript/dispatchkit/handle_return.hpp @@ -36,14 +36,14 @@ namespace chaiscript struct Handle_Return { template::type>::value>::type> + typename = std::enable_if_t>>> static Boxed_Value handle(T r) { return Boxed_Value(std::move(r), true); } template::type>::value>::type> + typename = std::enable_if_t>>> static Boxed_Value handle(T &&r) { return Boxed_Value(std::make_shared(std::forward(r)), true); diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index 48a74125..d3c751ee 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -27,6 +27,7 @@ #include "proxy_functions_detail.hpp" #include "type_info.hpp" #include "dynamic_object.hpp" +#include "function_params.hpp" namespace chaiscript { class Type_Conversions; @@ -73,8 +74,9 @@ namespace chaiscript return m_types == t_rhs.m_types; } - std::vector convert(std::vector vals, const Type_Conversions_State &t_conversions) const + std::vector convert(Function_Params t_params, const Type_Conversions_State &t_conversions) const { + auto vals = t_params.to_vector(); constexpr auto dynamic_object_type_info = user_type(); for (size_t i = 0; i < vals.size(); ++i) { @@ -113,7 +115,7 @@ namespace chaiscript // first result: is a match // second result: needs conversions - std::pair match(const std::vector &vals, const Type_Conversions_State &t_conversions) const noexcept + std::pair match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept { constexpr auto dynamic_object_type_info = user_type(); bool needs_conversion = false; @@ -136,7 +138,7 @@ namespace chaiscript } } catch (const std::bad_cast &) { return std::make_pair(false, false); - } + } } else { const auto &ti = m_types[i].second; if (!ti.is_undef()) @@ -196,7 +198,7 @@ namespace chaiscript public: virtual ~Proxy_Function_Base() = default; - Boxed_Value operator()(const std::vector ¶ms, const chaiscript::Type_Conversions_State &t_conversions) const + Boxed_Value operator()(const Function_Params ¶ms, const chaiscript::Type_Conversions_State &t_conversions) const { if (m_arity < 0 || size_t(m_arity) == params.size()) { return do_call(params, t_conversions); @@ -208,11 +210,11 @@ namespace chaiscript /// Returns a vector containing all of the types of the parameters the function returns/takes /// if the function is variadic or takes no arguments (arity of 0 or -1), the returned /// value contains exactly 1 Type_Info object: the return type - /// \returns the types of all parameters. + /// \returns the types of all parameters. const std::vector &get_param_types() const noexcept { return m_types; } virtual bool operator==(const Proxy_Function_Base &) const noexcept = 0; - virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const = 0; + virtual bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const = 0; virtual bool is_attribute_function() const noexcept { return false; } @@ -228,7 +230,7 @@ namespace chaiscript //! Return true if the function is a possible match //! to the passed in values - bool filter(const std::vector &vals, const Type_Conversions_State &t_conversions) const noexcept + bool filter(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept { assert(m_arity == -1 || (m_arity > 0 && static_cast(vals.size()) == m_arity)); @@ -254,13 +256,13 @@ namespace chaiscript constexpr auto boxed_number_ti = user_type(); constexpr auto function_ti = user_type>(); - if (ti.is_undef() + if (ti.is_undef() || ti.bare_equal(boxed_value_ti) || (!bv.get_type_info().is_undef() && ( (ti.bare_equal(boxed_number_ti) && bv.get_type_info().is_arithmetic()) || ti.bare_equal(bv.get_type_info()) || bv.get_type_info().bare_equal(function_ti) - || t_conversions->converts(ti, bv.get_type_info()) + || t_conversions->converts(ti, bv.get_type_info()) ) ) ) @@ -278,7 +280,7 @@ namespace chaiscript } protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const = 0; + virtual Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const = 0; Proxy_Function_Base(std::vector t_types, int t_arity) : m_types(std::move(t_types)), m_arity(t_arity), m_has_arithmetic_param(false) @@ -295,7 +297,7 @@ namespace chaiscript } - static bool compare_types(const std::vector &tis, const std::vector &bvs, + static bool compare_types(const std::vector &tis, const Function_Params &bvs, const Type_Conversions_State &t_conversions) noexcept { if (tis.size() - 1 != bvs.size()) @@ -342,10 +344,8 @@ namespace chaiscript namespace dispatch { - /** - * A Proxy_Function implementation that is not type safe, the called function - * is expecting a vector that it works with how it chooses. - */ + /// A Proxy_Function implementation that is not type safe, the called function + /// is expecting a vector that it works with how it chooses. class Dynamic_Proxy_Function : public Proxy_Function_Base { public: @@ -373,7 +373,7 @@ namespace chaiscript && this->m_param_types == prhs->m_param_types); } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override + bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const override { return call_match_internal(vals, t_conversions).first; } @@ -403,7 +403,7 @@ namespace chaiscript protected: - bool test_guard(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const + bool test_guard(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const { if (m_guard) { @@ -421,7 +421,7 @@ 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 + std::pair call_match_internal(const Function_Params &vals, const Type_Conversions_State &t_conversions) const { const auto comparison_result = [&](){ if (m_arity < 0) { @@ -434,7 +434,7 @@ namespace chaiscript }(); return std::make_pair( - comparison_result.first && test_guard(vals, t_conversions), + comparison_result.first && test_guard(vals, t_conversions), comparison_result.second ); } @@ -472,7 +472,7 @@ namespace chaiscript { public: Dynamic_Proxy_Function_Impl( - Callable t_f, + Callable t_f, int t_arity=-1, std::shared_ptr t_parsenode = AST_NodePtr(), Param_Types t_param_types = Param_Types(), @@ -489,13 +489,13 @@ namespace chaiscript protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { - const auto match_results = call_match_internal(params, t_conversions); - if (match_results.first) + const auto [is_a_match, needs_conversions] = call_match_internal(params, t_conversions); + if (is_a_match) { - if (match_results.second) { - return m_f(m_param_types.convert(params, t_conversions)); + if (needs_conversions) { + return m_f(Function_Params{m_param_types.convert(params, t_conversions)}); } else { return m_f(params); } @@ -528,7 +528,7 @@ namespace chaiscript class Bound_Function final : public Proxy_Function_Base { public: - Bound_Function(const Const_Proxy_Function &t_f, + Bound_Function(const Const_Proxy_Function &t_f, const std::vector &t_args) : Proxy_Function_Base(build_param_type_info(t_f, t_args), (t_f->get_arity()<0?-1:static_cast(build_param_type_info(t_f, t_args).size())-1)), m_f(t_f), m_args(t_args) @@ -542,9 +542,9 @@ namespace chaiscript } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override + bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const override { - return m_f->call_match(build_param_list(vals), t_conversions); + return m_f->call_match(Function_Params(build_param_list(vals)), t_conversions); } std::vector get_contained_functions() const override @@ -553,7 +553,7 @@ namespace chaiscript } - std::vector build_param_list(const std::vector ¶ms) const + std::vector build_param_list(const Function_Params ¶ms) const { auto parg = params.begin(); auto barg = m_args.begin(); @@ -562,7 +562,7 @@ namespace chaiscript while (!(parg == params.end() && barg == m_args.end())) { - while (barg != m_args.end() + while (barg != m_args.end() && !(barg->get_type_info() == chaiscript::detail::Get_Type_Info::get())) { args.push_back(*barg); @@ -575,18 +575,18 @@ namespace chaiscript ++parg; } - if (barg != m_args.end() + if (barg != m_args.end() && barg->get_type_info() == chaiscript::detail::Get_Type_Info::get()) { ++barg; - } + } } return args; } protected: - static std::vector build_param_type_info(const Const_Proxy_Function &t_f, + static std::vector build_param_type_info(const Const_Proxy_Function &t_f, const std::vector &t_args) { assert(t_f->get_arity() < 0 || t_f->get_arity() == static_cast(t_args.size())); @@ -610,9 +610,9 @@ namespace chaiscript return retval; } - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { - return (*m_f)(build_param_list(params), t_conversions); + return (*m_f)(Function_Params{build_param_list(params)}, t_conversions); } private: @@ -628,13 +628,13 @@ namespace chaiscript { } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const noexcept override + bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { - return static_cast(vals.size()) == get_arity() + return static_cast(vals.size()) == get_arity() && (compare_types(m_types, vals, t_conversions) && compare_types_with_cast(vals, t_conversions)); } - virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const noexcept = 0; + virtual bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept = 0; }; @@ -650,7 +650,7 @@ namespace chaiscript { } - bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const noexcept override + bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { return detail::compare_types_cast(static_cast(nullptr), vals, t_conversions); } @@ -662,7 +662,7 @@ namespace chaiscript protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { return detail::call_func(static_cast(nullptr), m_f, params, t_conversions); } @@ -694,7 +694,7 @@ namespace chaiscript assert(!m_shared_ptr_holder || m_shared_ptr_holder.get() == &m_f.get()); } - bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const noexcept override + bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { return detail::compare_types_cast(static_cast(nullptr), vals, t_conversions); } @@ -714,7 +714,7 @@ namespace chaiscript } protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { return detail::call_func(static_cast(nullptr), m_f.get(), params, t_conversions); } @@ -737,11 +737,11 @@ namespace chaiscript { } - bool is_attribute_function() const noexcept override { return true; } + bool is_attribute_function() const noexcept override { return true; } bool operator==(const Proxy_Function_Base &t_func) const noexcept override { - const Attribute_Access * aa + const Attribute_Access * aa = dynamic_cast *>(&t_func); if (aa) { @@ -751,7 +751,7 @@ namespace chaiscript } } - bool call_match(const std::vector &vals, const Type_Conversions_State &) const noexcept override + bool call_match(const Function_Params &vals, const Type_Conversions_State &) const noexcept override { if (vals.size() != 1) { @@ -762,7 +762,7 @@ namespace chaiscript } protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { const Boxed_Value &bv = params[0]; if (bv.is_const()) @@ -811,22 +811,22 @@ namespace chaiscript { /// \brief Exception thrown in the case that a method dispatch fails /// because no matching function was found - /// + /// /// May be thrown due to an arity_error, a guard_error or a bad_boxed_cast /// exception class dispatch_error : public std::runtime_error { public: - dispatch_error(std::vector t_parameters, + dispatch_error(const Function_Params &t_parameters, std::vector t_functions) - : std::runtime_error("Error with function dispatch"), parameters(std::move(t_parameters)), functions(std::move(t_functions)) + : std::runtime_error("Error with function dispatch"), parameters(t_parameters.to_vector()), functions(std::move(t_functions)) { } - dispatch_error(std::vector t_parameters, + dispatch_error(const Function_Params &t_parameters, std::vector t_functions, const std::string &t_desc) - : std::runtime_error(t_desc), parameters(std::move(t_parameters)), functions(std::move(t_functions)) + : std::runtime_error(t_desc), parameters(t_parameters.to_vector()), functions(std::move(t_functions)) { } @@ -837,14 +837,14 @@ namespace chaiscript std::vector parameters; std::vector functions; }; - } + } namespace dispatch { - namespace detail + namespace detail { template - bool types_match_except_for_arithmetic(const FuncType &t_func, const std::vector &plist, + bool types_match_except_for_arithmetic(const FuncType &t_func, const Function_Params &plist, const Type_Conversions_State &t_conversions) noexcept { const std::vector &types = t_func->get_param_types(); @@ -863,7 +863,7 @@ namespace chaiscript } template - Boxed_Value dispatch_with_conversions(InItr begin, const InItr &end, const std::vector &plist, + Boxed_Value dispatch_with_conversions(InItr begin, const InItr &end, const Function_Params &plist, const Type_Conversions_State &t_conversions, const Funcs &t_funcs) { InItr matching_func(end); @@ -919,7 +919,7 @@ namespace chaiscript ); try { - return (*(matching_func->second))(newplist, t_conversions); + return (*(matching_func->second))(Function_Params{newplist}, t_conversions); } catch (const exception::bad_boxed_cast &) { //parameter failed to cast } catch (const exception::arity_error &) { @@ -938,7 +938,7 @@ namespace chaiscript /// function is found or throw dispatch_error if no matching function is found template Boxed_Value dispatch(const Funcs &funcs, - const std::vector &plist, const Type_Conversions_State &t_conversions) + const Function_Params &plist, const Type_Conversions_State &t_conversions) { std::vector> ordered_funcs; ordered_funcs.reserve(funcs.size()); diff --git a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp index 5f5d3ac8..3b8a99bd 100644 --- a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp @@ -21,6 +21,7 @@ #include "boxed_value.hpp" #include "handle_return.hpp" #include "type_info.hpp" +#include "function_params.hpp" namespace chaiscript { class Type_Conversions_State; @@ -77,7 +78,7 @@ namespace chaiscript */ template bool compare_types_cast(Ret (*)(Params...), - const std::vector ¶ms, const Type_Conversions_State &t_conversions) noexcept + const Function_Params ¶ms, const Type_Conversions_State &t_conversions) noexcept { try { std::vector::size_type i = 0; @@ -92,7 +93,7 @@ namespace chaiscript template Ret call_func(Ret (*)(Params...), std::index_sequence, const Callable &f, - [[maybe_unused]] const std::vector ¶ms, + [[maybe_unused]] const Function_Params ¶ms, [[maybe_unused]] const Type_Conversions_State &t_conversions) { return f(boxed_cast(params[I], &t_conversions)...); @@ -105,7 +106,7 @@ namespace chaiscript /// the bad_boxed_cast is passed up to the caller. template Boxed_Value call_func(Ret (*sig)(Params...), const Callable &f, - const std::vector ¶ms, const Type_Conversions_State &t_conversions) + const Function_Params ¶ms, const Type_Conversions_State &t_conversions) { if constexpr (std::is_same_v) { call_func(sig, std::index_sequence_for{}, f, params, t_conversions); diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 06ad8e0b..784e7504 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -95,7 +95,7 @@ namespace chaiscript Logical_And, Logical_Or, Reference, Switch, Case, Default, Noop, Class, Binary, Arg, Global_Decl, Constant, Compiled }; - enum class Operator_Precidence { Ternary_Cond, Logical_Or, + enum class Operator_Precedence { Ternary_Cond, Logical_Or, Logical_And, Bitwise_Or, Bitwise_Xor, Bitwise_And, Equality, Comparison, Shift, Addition, Multiplication, Prefix }; @@ -732,12 +732,7 @@ namespace chaiscript m_ds->pop_function_call(m_ds.stack_holder(), m_ds.conversion_saves()); } - void save_params(const std::vector &t_params) - { - m_ds->save_function_params(t_params); - } - - void save_params(std::initializer_list t_params) + void save_params(const Function_Params &t_params) { m_ds->save_function_params(t_params); } diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 51d9f408..2a68341c 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -149,7 +149,7 @@ namespace chaiscript m_engine.add( dispatch::make_dynamic_proxy_function( - [this](const std::vector &t_params) { + [this](const Function_Params &t_params) { return m_engine.call_exists(t_params); }) , "call_exists"); @@ -158,7 +158,7 @@ namespace chaiscript m_engine.add(fun( [=](const dispatch::Proxy_Function_Base &t_fun, const std::vector &t_params) -> Boxed_Value { Type_Conversions_State s(this->m_engine.conversions(), this->m_engine.conversions().conversion_saves()); - return t_fun(t_params, s); + return t_fun(Function_Params{t_params}, s); }), "call"); @@ -200,6 +200,7 @@ namespace chaiscript m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global"); m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global"); + // why this unused parameter to Namespace? m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace([](Namespace& /*space*/) {}, t_namespace_name); import(t_namespace_name); }), "namespace"); m_engine.add(fun([this](const std::string& t_namespace_name) { import(t_namespace_name); }), "import"); } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index f9a3c9bb..52110bde 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -51,7 +51,7 @@ namespace chaiscript { /// Helper function that will set up the scope around a function call, including handling the named function parameters template - static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl &t_node, const std::vector &t_param_names, const std::vector &t_vals, const std::map *t_locals=nullptr, bool has_this_capture = false) { + static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl &t_node, const std::vector &t_param_names, const Function_Params &t_vals, const std::map *t_locals=nullptr, bool has_this_capture = false) { chaiscript::detail::Dispatch_State state(t_ss); const Boxed_Value *thisobj = [&]() -> const Boxed_Value *{ @@ -96,8 +96,11 @@ namespace chaiscript return Boxed_Number::clone(incoming); } else if (incoming.get_type_info().bare_equal_type_info(typeid(bool))) { return Boxed_Value(*static_cast(incoming.get_const_ptr())); + } else if (incoming.get_type_info().bare_equal_type_info(typeid(std::string))) { + return Boxed_Value(*static_cast(incoming.get_const_ptr())); } else { - return t_ss->call_function("clone", t_loc, {incoming}, t_ss.conversions()); + std::array params{std::move(incoming)}; + return t_ss->call_function("clone", t_loc, Function_Params{params}, t_ss.conversions()); } } else { incoming.reset_return_value(); @@ -200,8 +203,9 @@ namespace chaiscript } } else { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - fpp.save_params({t_lhs, m_rhs}); - return t_ss->call_function(t_oper_string, m_loc, {t_lhs, m_rhs}, t_ss.conversions()); + std::array params{t_lhs, m_rhs}; + fpp.save_params(Function_Params{params}); + return t_ss->call_function(t_oper_string, m_loc, Function_Params{params}, t_ss.conversions()); } } catch(const exception::dispatch_error &e){ @@ -246,8 +250,9 @@ namespace chaiscript } } else { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - fpp.save_params({t_lhs, t_rhs}); - return t_ss->call_function(t_oper_string, m_loc, {t_lhs, t_rhs}, t_ss.conversions()); + std::array params{t_lhs, t_rhs}; + fpp.save_params(Function_Params(params)); + return t_ss->call_function(t_oper_string, m_loc, Function_Params(params), t_ss.conversions()); } } catch(const exception::dispatch_error &e){ @@ -321,14 +326,14 @@ namespace chaiscript } if (Save_Params) { - fpp.save_params(params); + fpp.save_params(Function_Params{params}); } Boxed_Value fn(this->children[0]->eval(t_ss)); using ConstFunctionTypePtr = const dispatch::Proxy_Function_Base *; try { - return (*t_ss->boxed_cast(fn))(params, t_ss.conversions()); + return (*t_ss->boxed_cast(fn))(Function_Params{params}, t_ss.conversions()); } catch(const exception::dispatch_error &e){ throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'", e.parameters, e.functions, false, *t_ss); @@ -338,7 +343,7 @@ namespace chaiscript using ConstFunctionTypeRef = const Const_Proxy_Function &; Const_Proxy_Function f = t_ss->boxed_cast(fn); // handle the case where there is only 1 function to try to call and dispatch fails on it - throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, {f}, false, *t_ss); + throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, make_vector(f), false, *t_ss); } catch (const exception::bad_boxed_cast &) { throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function."); } @@ -444,28 +449,37 @@ namespace chaiscript Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - Boxed_Value rhs = this->children[1]->eval(t_ss); - Boxed_Value lhs = this->children[0]->eval(t_ss); - if (lhs.is_return_value()) { + auto params = [&](){ + // The RHS *must* be evaluated before the LHS + // consider `var range = range(x)` + // if we declare the variable in scope first, then the name lookup fails + // for the RHS + auto rhs = this->children[1]->eval(t_ss); + auto lhs = this->children[0]->eval(t_ss); + std::array p{std::move(lhs), std::move(rhs)}; + return p; + }(); + + if (params[0].is_return_value()) { throw exception::eval_error("Error, cannot assign to temporary value."); - } else if (lhs.is_const()) { + } else if (params[0].is_const()) { throw exception::eval_error("Error, cannot assign to constant value."); } - if (m_oper != Operators::Opers::invalid && lhs.get_type_info().is_arithmetic() && - rhs.get_type_info().is_arithmetic()) + if (m_oper != Operators::Opers::invalid && params[0].get_type_info().is_arithmetic() && + params[1].get_type_info().is_arithmetic()) { try { - return Boxed_Number::do_oper(m_oper, lhs, rhs); + return Boxed_Number::do_oper(m_oper, params[0], params[1]); } catch (const std::exception &) { throw exception::eval_error("Error with unsupported arithmetic assignment operation."); } } else if (m_oper == Operators::Opers::assign) { try { - if (lhs.is_undef()) { + if (params[0].is_undef()) { if (!this->children.empty() && ((this->children[0]->identifier == AST_Node_Type::Reference) || (!this->children[0]->children.empty() @@ -476,16 +490,16 @@ namespace chaiscript { /// \todo This does not handle the case of an unassigned reference variable /// being assigned outside of its declaration - lhs.assign(rhs); - lhs.reset_return_value(); - return rhs; + params[0].assign(params[1]); + params[0].reset_return_value(); + return params[1]; } else { - rhs = detail::clone_if_necessary(std::move(rhs), m_clone_loc, t_ss); + params[1] = detail::clone_if_necessary(std::move(params[1]), m_clone_loc, t_ss); } } try { - return t_ss->call_function(this->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions()); + return t_ss->call_function(this->text, m_loc, Function_Params{params}, t_ss.conversions()); } catch(const exception::dispatch_error &e){ throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss); @@ -496,22 +510,22 @@ namespace chaiscript } } else if (this->text == ":=") { - if (lhs.is_undef() || Boxed_Value::type_match(lhs, rhs)) { - lhs.assign(rhs); - lhs.reset_return_value(); + if (params[0].is_undef() || Boxed_Value::type_match(params[0], params[1])) { + params[0].assign(params[1]); + params[0].reset_return_value(); } else { throw exception::eval_error("Mismatched types in equation"); } } else { try { - return t_ss->call_function(this->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions()); + return t_ss->call_function(this->text, m_loc, Function_Params{params}, t_ss.conversions()); } catch(const exception::dispatch_error &e){ throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss); } } - return rhs; + return params[1]; } private: @@ -589,11 +603,11 @@ namespace chaiscript Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - const std::vector params{this->children[0]->eval(t_ss), this->children[1]->eval(t_ss)}; + std::array params{this->children[0]->eval(t_ss), this->children[1]->eval(t_ss)}; try { - fpp.save_params(params); - return t_ss->call_function("[]", m_loc, params, t_ss.conversions()); + fpp.save_params(Function_Params{params}); + return t_ss->call_function("[]", m_loc, Function_Params{params}, t_ss.conversions()); } catch(const exception::dispatch_error &e){ throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss ); @@ -618,7 +632,7 @@ namespace chaiscript Boxed_Value retval = this->children[0]->eval(t_ss); - std::vector params{retval}; + auto params = make_vector(retval); bool has_function_params = false; if (this->children[1]->children.size() > 1) { @@ -628,10 +642,10 @@ namespace chaiscript } } - fpp.save_params(params); + fpp.save_params(Function_Params{params}); try { - retval = t_ss->call_member(m_fun_name, m_loc, std::move(params), has_function_params, t_ss.conversions()); + retval = t_ss->call_member(m_fun_name, m_loc, Function_Params{params}, has_function_params, t_ss.conversions()); } catch(const exception::dispatch_error &e){ if (e.functions.empty()) @@ -647,7 +661,8 @@ namespace chaiscript if (this->children[1]->identifier == AST_Node_Type::Array_Call) { try { - retval = t_ss->call_function("[]", m_array_loc, {retval, this->children[1]->children[1]->eval(t_ss)}, t_ss.conversions()); + std::array p{retval, this->children[1]->children[1]->eval(t_ss)}; + retval = t_ss->call_function("[]", m_array_loc, Function_Params{p}, t_ss.conversions()); } catch(const exception::dispatch_error &e){ throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss); @@ -696,7 +711,7 @@ namespace chaiscript return Boxed_Value( dispatch::make_dynamic_proxy_function( [engine, lambda_node = this->m_lambda_node, param_names = this->m_param_names, captures, - this_capture = this->m_this_capture] (const std::vector &t_params) + this_capture = this->m_this_capture] (const Function_Params &t_params) { return detail::eval_function(engine, *lambda_node, param_names, t_params, &captures, this_capture); }, @@ -812,7 +827,7 @@ namespace chaiscript std::shared_ptr guard; if (m_guard_node) { guard = dispatch::make_dynamic_proxy_function( - [engine, guardnode = m_guard_node, t_param_names](const std::vector &t_params) + [engine, guardnode = m_guard_node, t_param_names](const Function_Params &t_params) { return detail::eval_function(engine, *guardnode, t_param_names, t_params); }, @@ -823,7 +838,7 @@ namespace chaiscript const std::string & l_function_name = this->children[0]->text; t_ss->add( dispatch::make_dynamic_proxy_function( - [engine, func_node = m_body_node, t_param_names](const std::vector &t_params) + [engine, func_node = m_body_node, t_param_names](const Function_Params &t_params) { return detail::eval_function(engine, *func_node, t_param_names, t_params); }, @@ -914,7 +929,7 @@ namespace chaiscript }; const auto call_function = [&t_ss](const auto &t_funcs, const Boxed_Value &t_param) { - return dispatch::dispatch(*t_funcs, {t_param}, t_ss.conversions()); + return dispatch::dispatch(*t_funcs, Function_Params{t_param}, t_ss.conversions()); }; @@ -963,6 +978,7 @@ namespace chaiscript try { this->children[2]->eval(t_ss); } catch (detail::Continue_Loop &) { + // continue statement hit } call_function(pop_front_funcs, range_obj); } @@ -1034,7 +1050,8 @@ namespace chaiscript if (this->children[currentCase]->identifier == AST_Node_Type::Case) { //This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here. try { - if (hasMatched || boxed_cast(t_ss->call_function("==", m_loc, {match_value, this->children[currentCase]->children[0]->eval(t_ss)}, t_ss.conversions()))) { + std::array p{match_value, this->children[currentCase]->children[0]->eval(t_ss)}; + if (hasMatched || boxed_cast(t_ss->call_function("==", m_loc, Function_Params{p}, t_ss.conversions()))) { this->children[currentCase]->eval(t_ss); hasMatched = true; } @@ -1215,8 +1232,8 @@ namespace chaiscript return Boxed_Number::do_oper(m_oper, bv); } else { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - fpp.save_params({bv}); - return t_ss->call_function(this->text, m_loc, {std::move(bv)}, t_ss.conversions()); + fpp.save_params(Function_Params{bv}); + return t_ss->call_function(this->text, m_loc, Function_Params{bv}, t_ss.conversions()); } } catch (const exception::dispatch_error &e) { throw exception::eval_error("Error with prefix operator evaluation: '" + this->text + "'", e.parameters, e.functions, false, *t_ss); @@ -1281,9 +1298,12 @@ namespace chaiscript Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ try { - auto oper1 = this->children[0]->children[0]->children[0]->eval(t_ss); - auto oper2 = this->children[0]->children[0]->children[1]->eval(t_ss); - return t_ss->call_function("generate_range", m_loc, {oper1, oper2}, t_ss.conversions()); + std::array params{ + this->children[0]->children[0]->children[0]->eval(t_ss), + this->children[0]->children[0]->children[1]->eval(t_ss) + }; + + return t_ss->call_function("generate_range", m_loc, Function_Params{params}, t_ss.conversions()); } catch (const exception::dispatch_error &e) { throw exception::eval_error("Unable to generate range vector, while calling 'generate_range'", e.parameters, e.functions, false, *t_ss); @@ -1313,7 +1333,7 @@ namespace chaiscript auto &catch_block = *this->children[i]; if (catch_block.children.size() == 1) { - //No variable capture, no guards + //No variable capture retval = catch_block.children[0]->eval(t_ss); break; } else if (catch_block.children.size() == 2 || catch_block.children.size() == 3) { @@ -1321,32 +1341,15 @@ 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()).first) + ).match(Function_Params{t_except}, t_ss.conversions()).first) { t_ss.add_object(name, t_except); if (catch_block.children.size() == 2) { - //Variable capture, no guards + //Variable capture retval = catch_block.children[1]->eval(t_ss); break; } - else if (catch_block.children.size() == 3) { - //Variable capture, guards - - bool guard = false; - try { - guard = boxed_cast(catch_block.children[1]->eval(t_ss)); - } catch (const exception::bad_boxed_cast &) { - if (this->children.back()->identifier == AST_Node_Type::Finally) { - this->children.back()->children[0]->eval(t_ss); - } - throw exception::eval_error("Guard condition not boolean"); - } - if (guard) { - retval = catch_block.children[2]->eval(t_ss); - break; - } - } } } else { @@ -1451,7 +1454,7 @@ namespace chaiscript std::reference_wrapper engine(*t_ss); if (m_guard_node) { guard = dispatch::make_dynamic_proxy_function( - [engine, t_param_names, guardnode = m_guard_node](const std::vector &t_params) { + [engine, t_param_names, guardnode = m_guard_node](const Function_Params &t_params) { return chaiscript::eval::detail::eval_function(engine, *guardnode, t_param_names, t_params); }, static_cast(numparams), m_guard_node); @@ -1466,7 +1469,7 @@ namespace chaiscript t_ss->add( std::make_shared(class_name, dispatch::make_dynamic_proxy_function( - [engine, t_param_names, node = m_body_node](const std::vector &t_params) { + [engine, t_param_names, node = m_body_node](const Function_Params &t_params) { return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params); }, static_cast(numparams), m_body_node, param_types, guard @@ -1483,7 +1486,7 @@ namespace chaiscript t_ss->add( std::make_shared(class_name, dispatch::make_dynamic_proxy_function( - [engine, t_param_names, node = m_body_node](const std::vector &t_params) { + [engine, t_param_names, node = m_body_node](const Function_Params &t_params) { return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params); }, static_cast(numparams), m_body_node, param_types, guard), type), diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index daf9782f..ddab2c90 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -285,20 +285,20 @@ namespace chaiscript }; - constexpr static std::array create_operators() noexcept { - std::array operators = { { - Operator_Precidence::Ternary_Cond, - Operator_Precidence::Logical_Or, - Operator_Precidence::Logical_And, - Operator_Precidence::Bitwise_Or, - Operator_Precidence::Bitwise_Xor, - Operator_Precidence::Bitwise_And, - Operator_Precidence::Equality, - Operator_Precidence::Comparison, - Operator_Precidence::Shift, - Operator_Precidence::Addition, - Operator_Precidence::Multiplication, - Operator_Precidence::Prefix + constexpr static std::array create_operators() noexcept { + std::array operators = { { + Operator_Precedence::Ternary_Cond, + Operator_Precedence::Logical_Or, + Operator_Precedence::Logical_And, + Operator_Precedence::Bitwise_Or, + Operator_Precedence::Bitwise_Xor, + Operator_Precedence::Bitwise_And, + Operator_Precedence::Equality, + Operator_Precedence::Comparison, + Operator_Precedence::Shift, + Operator_Precedence::Addition, + Operator_Precedence::Multiplication, + Operator_Precedence::Prefix } }; return operators; } @@ -1791,11 +1791,6 @@ namespace chaiscript if (!(Arg() && Char(')'))) { throw exception::eval_error("Incomplete 'catch' expression", File_Position(m_position.line, m_position.col), *m_filename); } - if (Char(':')) { - if (!Operator()) { - throw exception::eval_error("Missing guard expression for catch", File_Position(m_position.line, m_position.col), *m_filename); - } - } } while (Eol()) {} @@ -2471,7 +2466,7 @@ namespace chaiscript bool retval = false; const auto prev_stack_top = m_match_stack.size(); - if (m_operators[t_precedence] != Operator_Precidence::Prefix) { + if (m_operators[t_precedence] != Operator_Precedence::Prefix) { if (Operator(t_precedence+1)) { retval = true; std::string oper; @@ -2483,7 +2478,7 @@ namespace chaiscript } switch (m_operators[t_precedence]) { - case(Operator_Precidence::Ternary_Cond) : + case(Operator_Precedence::Ternary_Cond) : if (Symbol(":")) { if (!Operator(t_precedence+1)) { throw exception::eval_error("Incomplete '" + oper + "' expression", @@ -2497,24 +2492,24 @@ namespace chaiscript } break; - case(Operator_Precidence::Addition) : - case(Operator_Precidence::Multiplication) : - case(Operator_Precidence::Shift) : - case(Operator_Precidence::Equality) : - case(Operator_Precidence::Bitwise_And) : - case(Operator_Precidence::Bitwise_Xor) : - case(Operator_Precidence::Bitwise_Or) : - case(Operator_Precidence::Comparison) : + case(Operator_Precedence::Addition) : + case(Operator_Precedence::Multiplication) : + case(Operator_Precedence::Shift) : + case(Operator_Precedence::Equality) : + case(Operator_Precedence::Bitwise_And) : + case(Operator_Precedence::Bitwise_Xor) : + case(Operator_Precedence::Bitwise_Or) : + case(Operator_Precedence::Comparison) : build_match>(prev_stack_top, oper); break; - case(Operator_Precidence::Logical_And) : + case(Operator_Precedence::Logical_And) : build_match>(prev_stack_top, oper); break; - case(Operator_Precidence::Logical_Or) : + case(Operator_Precedence::Logical_Or) : build_match>(prev_stack_top, oper); break; - case(Operator_Precidence::Prefix) : + case(Operator_Precedence::Prefix) : assert(false); // cannot reach here because of if() statement at the top break; diff --git a/include/chaiscript/language/chaiscript_posix.hpp b/include/chaiscript/language/chaiscript_posix.hpp index d26c8aa2..b909347a 100644 --- a/include/chaiscript/language/chaiscript_posix.hpp +++ b/include/chaiscript/language/chaiscript_posix.hpp @@ -49,19 +49,6 @@ namespace chaiscript } } - static T cast_symbol(void *p) noexcept - { - union cast_union - { - T func_ptr; - void *in_ptr; - }; - - cast_union c; - c.in_ptr = p; - return c.func_ptr; - } - T m_symbol; }; diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 139cd665..4943bcb4 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -96,7 +96,7 @@ class JSON template auto &get_set_type() { set_type(ClassValue); - return std::get(ClassValue)>(d); + return (std::get(ClassValue)>(d)); } auto &Map() { diff --git a/include/chaiscript/utility/quick_flat_map.hpp b/include/chaiscript/utility/quick_flat_map.hpp index 1d3be2bb..35bc089d 100644 --- a/include/chaiscript/utility/quick_flat_map.hpp +++ b/include/chaiscript/utility/quick_flat_map.hpp @@ -3,15 +3,29 @@ namespace chaiscript::utility { - template + template> struct QuickFlatMap { - auto find(const Key &s) noexcept { - return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); + Comparator comparator; + + template + auto find(const Lookup &s) noexcept { + return std::find_if(std::begin(data), std::end(data), [&s, this](const auto &d) { return comparator(d.first, s); }); } - auto find(const Key &s) const noexcept { - return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); + template + auto find(const Lookup &s) const noexcept { + return std::find_if(std::cbegin(data), std::cend(data), [&s, this](const auto &d) { return comparator(d.first, s); }); + } + + template + auto find(const Lookup &s, const std::size_t t_hint) const noexcept { + if (data.size() > t_hint && comparator(data[t_hint].first, s)) { + const auto begin = std::cbegin(data); + return std::next(begin, static_cast>::difference_type>(t_hint)); + } else { + return find(s); + } } auto size() const noexcept { @@ -35,16 +49,46 @@ namespace chaiscript::utility { return data.end(); } + auto &back() noexcept { + return data.back(); + } + + const auto &back() const noexcept { + return data.back(); + } + Value &operator[](const Key &s) { const auto itr = find(s); if (itr != data.end()) { return itr->second; } else { + grow(); return data.emplace_back(s, Value()).second; } } + Value &at_index(const std::size_t idx) noexcept + { + return data[idx].second; + } + + const Value &at_index(const std::size_t idx) const noexcept + { + return data[idx].second; + } + + bool empty() const noexcept + { + return data.empty(); + } + + template + void assign(Itr begin, Itr end) + { + data.assign(begin, end); + } + Value &at(const Key &s) { const auto itr = find(s); if (itr != data.end()) { @@ -54,6 +98,32 @@ namespace chaiscript::utility { } } + + template + auto insert_or_assign(Key &&key, M &&m) + { + if (auto itr = find(key); itr != data.end()) { + *itr = std::forward(m); + return std::pair{itr, false}; + } else { + grow(); + return std::pair{data.emplace(data.end(), std::move(key), std::forward(m)), true}; + } + } + + + template + auto insert_or_assign(const Key &key, M &&m) + { + if (auto itr = find(key); itr != data.end()) { + itr->second = std::forward(m); + return std::pair{itr, false}; + } else { + grow(); + return std::pair{data.emplace(data.end(), key, std::forward(m)), true}; + } + } + const Value &at(const Key &s) const { const auto itr = find(s); if (itr != data.end()) { @@ -63,15 +133,33 @@ namespace chaiscript::utility { } } - size_t count(const Key &s) const noexcept { + template + size_t count(const Lookup &s) const noexcept { return (find(s) != data.end())?1:0; } std::vector> data; + using value_type = std::pair; using iterator = typename decltype(data)::iterator; using const_iterator = typename decltype(data)::const_iterator; + std::pair insert( value_type&& value ) + { + if (const auto itr = find(value.first); itr != data.end()) { + return std::pair{itr, false}; + } else { + grow(); + return std::pair{data.insert(data.end(), std::move(value)), true}; + } + } + + void grow() { + if ((data.capacity() - data.size()) == 0) { + data.reserve(data.size() + 2); + } + } + }; diff --git a/include/chaiscript/utility/stack_vector.hpp b/include/chaiscript/utility/stack_vector.hpp new file mode 100644 index 00000000..706d276a --- /dev/null +++ b/include/chaiscript/utility/stack_vector.hpp @@ -0,0 +1,63 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2017, 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_STACK_VECTOR_HPP_ +#define CHAISCRIPT_STACK_VECTOR_HPP_ + +#include +#include +#include + + + +template +struct Stack_Vector +{ + constexpr static auto aligned_size = sizeof(T) + (sizeof(T) & std::alignment_of_v) > 0 ? std::alignment_of_v : 0; + + alignas(std::alignment_of_v) char data[aligned_size * MaxSize]; + + [[nodiscard]] T & operator[](const std::size_t idx) noexcept { + return *reinterpret_cast(&data + aligned_size * idx); + } + + [[nodiscard]] const T & operator[](const std::size_t idx) const noexcept { + return *reinterpret_cast(&data + aligned_size * idx); + } + + template + T& emplace_back(Param && ... param) { + auto *p = new(&(*this)[m_size++]) T(std::forward(param)...); + return *p; + }; + + auto size() const noexcept { + return m_size; + }; + + void pop_back() noexcept(std::is_nothrow_destructible_v) { + (*this)[m_size--].~T(); + } + + ~Stack_Vector() noexcept(std::is_nothrow_destructible_v) + { + auto loc = m_size - 1; + for (std::size_t pos = 0; pos < m_size; ++pos) { + (*this)[loc--].~T(); + } + } + + std::size_t m_size{0}; + +}; + +#endif CHAISCRIPT_STACK_VECTOR_HPP_ + + diff --git a/samples/fun_call_performance.cpp b/samples/fun_call_performance.cpp index e2476cb5..e68ee0d6 100644 --- a/samples/fun_call_performance.cpp +++ b/samples/fun_call_performance.cpp @@ -145,20 +145,20 @@ std::vector default_search_paths() void help(int n) { if (n >= 0) { - std::cout << "ChaiScript evaluator. To evaluate an expression, type it and press ." << std::endl; - std::cout << "Additionally, you can inspect the runtime system using:" << std::endl; - std::cout << " dump_system() - outputs all functions registered to the system" << std::endl; - std::cout << " dump_object(x) - dumps information about the given symbol" << std::endl; + std::cout << "ChaiScript evaluator. To evaluate an expression, type it and press .\n"; + std::cout << "Additionally, you can inspect the runtime system using:\n"; + std::cout << " dump_system() - outputs all functions registered to the system\n"; + std::cout << " dump_object(x) - dumps information about the given symbol\n"; } else { - std::cout << "usage : chai [option]+" << std::endl; - std::cout << "option:" << std::endl; - std::cout << " -h | --help" << std::endl; - std::cout << " -i | --interactive" << std::endl; - std::cout << " -c | --command cmd" << std::endl; - std::cout << " -v | --version" << std::endl; - std::cout << " - --stdin" << std::endl; - std::cout << " filepath" << std::endl; + std::cout << "usage : chai [option]+\n"; + std::cout << "option:\n"; + std::cout << " -h | --help\n"; + std::cout << " -i | --interactive\n"; + std::cout << " -c | --command cmd\n"; + std::cout << " -v | --version\n"; + std::cout << " - --stdin\n"; + std::cout << " filepath\n"; } } @@ -244,7 +244,7 @@ void interactive(chaiscript::ChaiScript& chai) //Then, we try to print the result of the evaluation to the user if (!val.get_type_info().bare_equal(chaiscript::user_type())) { try { - std::cout << chai.eval >("to_string")(val) << std::endl; + std::cout << chai.eval >("to_string")(val) << '\n'; } catch (...) {} //If we can't, do nothing } @@ -254,7 +254,7 @@ void interactive(chaiscript::ChaiScript& chai) if (ee.call_stack.size() > 0) { std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")"; } - std::cout << std::endl; + std::cout << '\n'; } catch (const std::exception &e) { std::cout << e.what(); diff --git a/samples/example.cpp b/src/sanity_checks.cpp similarity index 97% rename from samples/example.cpp rename to src/sanity_checks.cpp index 4810a127..1a0922bc 100644 --- a/samples/example.cpp +++ b/src/sanity_checks.cpp @@ -4,6 +4,11 @@ // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com + +// NOT TO BE USED AS A SOURCE OF BEST PRACTICES +// FOR CHAISCRIPT + + #include #include @@ -156,7 +161,7 @@ int main(int /*argc*/, char * /*argv*/[]) { //Ability to create our own container types when needed. std::vector and std::map are //mostly supported currently - chai.add(bootstrap::standard_library::vector_type >("IntVector")); + //chai.add(bootstrap::standard_library::vector_type >("IntVector")); // Test ability to register a function that excepts a shared_ptr version of a type diff --git a/unittests/3.x/exception_guards.chai b/unittests/3.x/exception_guards.chai deleted file mode 100644 index 99cd9018..00000000 --- a/unittests/3.x/exception_guards.chai +++ /dev/null @@ -1,34 +0,0 @@ -var results = []; - -for (var i = 2; i < 6; ++i) { - try { - throw(i) - } - catch(e) : e < 2 { - results.push_back("c1: " + e.to_string()); - } - catch(e) : e < 4 { - results.push_back("c2: " + e.to_string()); - } - catch(e) { - results.push_back("c3: " + e.to_string()); - } - catch { - // Should never get called - assert_equal(false, true) - } -} - -try { - throw(3) -} -catch(e) : e < 3 -{ - // Should never get called - assert_equal(false, true); -} -catch { - results.push_back("defaultcatch"); -} - -assert_equal(["c2: 2", "c2: 3", "c3: 4", "c3: 5", "defaultcatch"], results); diff --git a/unittests/clone_object.chai b/unittests/clone_object.chai new file mode 100644 index 00000000..8864d0ce --- /dev/null +++ b/unittests/clone_object.chai @@ -0,0 +1,41 @@ + +/* +global clone_count = 0; + +class Cloneable +{ + def Cloneable() { + } + +} + + +def clone(Cloneable c) +{ + print("Clone called"); + ++clone_count; + return c; +} + + +class MyObject +{ + def MyObject() { + this.data = Cloneable(); + } + + var data; +} + + +assert_equal(0, clone_count); + +var o = MyObject(); + +assert_equal(0, clone_count); + +var p = o; + +assert_equal(1, clone_count); +*/ + diff --git a/unittests/exception_guards.chai b/unittests/exception_guards.chai deleted file mode 100644 index 12792985..00000000 --- a/unittests/exception_guards.chai +++ /dev/null @@ -1,34 +0,0 @@ -auto results = []; - -for (auto i = 2; i < 6; ++i) { - try { - throw(i) - } - catch(e) : e < 2 { - results.push_back("c1: " + e.to_string()); - } - catch(e) : e < 4 { - results.push_back("c2: " + e.to_string()); - } - catch(e) { - results.push_back("c3: " + e.to_string()); - } - catch { - // Should never get called - assert_equal(false, true) - } -} - -try { - throw(3) -} -catch(e) : e < 3 -{ - // Should never get called - assert_equal(false, true); -} -catch { - results.push_back("defaultcatch"); -} - -assert_equal(["c2: 2", "c2: 3", "c3: 4", "c3: 5", "defaultcatch"], results); diff --git a/unittests/exception_typed_2.chai b/unittests/exception_typed_2.chai deleted file mode 100644 index cb294c78..00000000 --- a/unittests/exception_typed_2.chai +++ /dev/null @@ -1,34 +0,0 @@ -auto results = []; - -for (auto i = 2; i < 6; ++i) { - try { - throw(i) - } - catch(int e) : e < 2 { - results.push_back("c1: " + e.to_string()); - } - catch(int e) : e < 4 { - results.push_back("c2: " + e.to_string()); - } - catch(e) { - results.push_back("c3: " + e.to_string()); - } - catch { - // Should never get called - assert_equal(false, true) - } -} - -try { - throw(3) -} -catch(int e) : e < 3 -{ - // Should never get called - assert_equal(false, true); -} -catch { - results.push_back("defaultcatch"); -} - -assert_equal(["c2: 2", "c2: 3", "c3: 4", "c3: 5", "defaultcatch"], results);