diff --git a/CMakeLists.txt b/CMakeLists.txt index f1b3108d..fbba252b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,27 +249,30 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") +add_library(stdlib STATIC static_libs/chaiscript_stdlib.cpp) +add_library(parser STATIC static_libs/chaiscript_parser.cpp) -add_library(chaiscript_stdlib-${CHAI_VERSION} MODULE src/chaiscript_stdlib.cpp) +add_library(chaiscript_stdlib-${CHAI_VERSION} MODULE src/chaiscript_stdlib_module.cpp) target_link_libraries(chaiscript_stdlib-${CHAI_VERSION} ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) +set(CHAISCRIPT_LIBS stdlib parser) + add_executable(chai src/main.cpp ${Chai_INCLUDES}) -target_link_libraries(chai ${LIBS}) -add_dependencies(chai chaiscript_stdlib-${CHAI_VERSION}) +target_link_libraries(chai ${LIBS} ${CHAISCRIPT_LIBS}) if(BUILD_SAMPLES) add_executable(example samples/example.cpp) target_link_libraries(example ${LIBS}) add_executable(test_num_exceptions samples/test_num_exceptions.cpp) - target_link_libraries(test_num_exceptions ${LIBS}) + target_link_libraries(test_num_exceptions ${LIBS} ${CHAISCRIPT_LIBS}) add_executable(memory_leak_test samples/memory_leak_test.cpp) - target_link_libraries(memory_leak_test ${LIBS}) + target_link_libraries(memory_leak_test ${LIBS} ${CHAISCRIPT_LIBS}) add_executable(inheritance samples/inheritance.cpp) - target_link_libraries(inheritance ${LIBS}) + target_link_libraries(inheritance ${LIBS} ${CHAISCRIPT_LIBS}) add_executable(factory samples/factory.cpp) - target_link_libraries(factory ${LIBS}) + target_link_libraries(factory ${LIBS} ${CHAISCRIPT_LIBS}) add_executable(fun_call_performance samples/fun_call_performance.cpp) - target_link_libraries(fun_call_performance ${LIBS}) + target_link_libraries(fun_call_performance ${LIBS} ${CHAISCRIPT_LIBS}) endif() @@ -295,7 +298,7 @@ if (RUN_FUZZY_TESTS) file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/unittests") execute_process( - COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2015-07-16.tar.bz2 + COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2016-06-29.tar.bz2 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests ) @@ -421,7 +424,7 @@ if(BUILD_TESTING) if(NOT UNIT_TEST_LIGHT) add_executable(compiled_tests unittests/compiled_tests.cpp) - target_link_libraries(compiled_tests ${LIBS}) + target_link_libraries(compiled_tests ${LIBS} ${CHAISCRIPT_LIBS}) ADD_CATCH_TESTS(compiled_tests) @@ -434,11 +437,11 @@ if(BUILD_TESTING) add_test(NAME Type_Info_Test COMMAND type_info_test) add_executable(c_linkage_test unittests/c_linkage_test.cpp) - target_link_libraries(c_linkage_test ${LIBS}) + target_link_libraries(c_linkage_test ${LIBS} ${CHAISCRIPT_LIBS}) add_test(NAME C_Linkage_Test COMMAND c_linkage_test) add_executable(integer_literal_test unittests/integer_literal_test.cpp) - target_link_libraries(integer_literal_test ${LIBS}) + target_link_libraries(integer_literal_test ${LIBS} ${CHAISCRIPT_LIBS}) add_test(NAME Integer_Literal_Test COMMAND integer_literal_test) if(MULTITHREAD_SUPPORT_ENABLED) diff --git a/DesignGoals.md b/DesignGoals.md new file mode 100644 index 00000000..c9971556 --- /dev/null +++ b/DesignGoals.md @@ -0,0 +1,28 @@ +# Introduction + +This document outlines the principles that drive the development of ChaiScript. ChaiScript does not intent to be the perfect tool for *every* situation, but it does intend to be a good general purpose tool for *most* situations. + +# Goals + +1. Trivially easy to integrate with C++ projects +2. 0 external depenencies +3. "Perfect" integration with C++ + * Direct mapping between ChaiScript objects and C++ objects + * Direct mapping between ChaiScript functions and C++ functions + * Direct mapping between ChaiScript exceptions and C++ exceptions +3. Never surprise the C++ developer + * Object lifetimes managed by the stack + * Familiar syntax to C++ developers +4. Perform "well enough" to not get in the way + + +# Alternatives + +## Sol2 + +If you are looking for the fastest performing scripting language and don't mind Lua, you might want to consider [sol2](https://github.com/ThePhD/sol2). + +## SWIG + +If you are looking for the most flexible solution to be able to support multiple target languages, consider [SWIG](http://swig.org) + diff --git a/cheatsheet.md b/cheatsheet.md index c37045f7..3a22508e 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -27,6 +27,13 @@ chai.add(chaiscript::fun(&Class::method_name), "method_name"); chai.add(chaiscript::fun(&Class::member_name), "member_name"); ``` +### Bound Member Functions + +``` +chai.add(chaiscript::fun(&Class::method_name, Class_instance_ptr), "method_name"); +chai.add(chaiscript::fun(&Class::member_name, Class_instance_ptr), "member_name"); +``` + ### With Overloads #### Preferred @@ -84,6 +91,33 @@ chai.add(chaiscript::user_type(), "MyClass"); User defined type conversions are possible, defined in either script or in C++. + +### ChaiScript Defined Conversions + +Function objects (including lambdas) can be used to add type conversions +from inside of ChaiScript: + +``` +add_type_conversion(type("string"), type("Type_Info"), fun(s) { return type(s); }); +``` + +### C++ Defined Conversions + +Invoking a C++ type conversion possible with `static_cast` + +``` +chai.add(chaiscript::type_conversion()); +``` + +Calling a user defined type conversion that takes a lambda + +``` +chai.add(chaiscript::type_conversion([](const TestBaseType &t_bt) { /* return converted thing */ })); +``` + + +### Helpers + A helper function exists for strongly typed and ChaiScript `Vector` function conversion definition: ``` @@ -97,6 +131,7 @@ chai.add(chaiscript::map_conversion>()); ``` + This allows you to pass a ChaiScript function to a function requiring `std::vector` ## Adding Objects @@ -111,6 +146,7 @@ chai.add_global_const(chaiscript::const_var(somevar), "somevar"); // global cons chai.add_global(chaiscript::var(somevar), "somevar"); // global non-const, throws if object exists chai.set_global(chaiscript::var(somevar), "somevar"); // global non-const, overwrites existing object ``` + # Using STL ChaiScript recognize many types from STL, but you have to add specific instantiation yourself. @@ -260,6 +296,35 @@ if (g2.is_var_undef()) { g2 = 4; } // only initialize g2 once, if global decl hi GLOBAL g3; // all upper case version also accepted ``` +## Looping + +``` +// c-style for loops +for (var i = 0; i < 100; ++i) { print(i); } +``` + +``` +// while +while (some_condition()) { /* do something */ } +``` + +``` +// ranged for +for (x : [1,2,3]) { print(i); } +``` + +## Conditionals + +``` +if (expression) { } +``` + +``` +// C++17-style init-if blocks +// Value of 'statement' is scoped for entire `if` block +if (statement; expression) { } +``` + ## Built in Types ``` @@ -418,9 +483,33 @@ the contained function. If both a 2 parameter and a 3 parameter signature match, the 3 parameter function always wins. +## Context + + * `__LINE__` Current file line number + * `__FILE__` Full path of current file + * `__CLASS__` Name of current class + * `__FUNC__` Mame of current function + # Built In Functions +## Disabling Built-Ins + +When constructing a ChaiScript object, a vector of parameters can be passed in to disable or enable various built-in methods. + +Current options: + +``` +enum class Options +{ + Load_Modules, + No_Load_Modules, + External_Scripts, + No_External_Scripts +}; +``` + + ## Evaluation ``` @@ -432,4 +521,7 @@ use("filename") // evals file exactly once and returns value of last statement Both `use` and `eval_file` search the 'usepaths' passed to the ChaiScript constructor +## JSON + * `from_json` converts a JSON string into its strongly typed (map, vector, int, double, string) representations + * `to_json` converts a ChaiScript object (either a `Object` or one of map, vector, int, double, string) tree into its JSON string representation diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 79ef8cf4..f8e512e1 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -695,11 +695,10 @@ /// Begins a function or method definition /// /// ~~~~~~~~ -/// Function Definition ::= [annotation + CR/LF] "def" identifier "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block -/// Method Definition ::= [annotation + CR/LF] "def" class_name "::" method_name "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block +/// Function Definition ::= "def" identifier "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block +/// Method Definition ::= "def" class_name "::" method_name "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block /// ~~~~~~~~ /// -/// annotation: meta-annotation on function, currently used as documentation. Optional. /// identifier: name of function. Required. /// args: comma-delimited list of parameter names with optional type specifiers. Optional. /// guards: guarding statement that act as a prerequisite for the function. Optional. @@ -817,16 +816,26 @@ /// @namespace chaiscript::detail /// @brief Classes and functions reserved for internal use. Items in this namespace are not supported. -#include "chaiscript_defines.hpp" - -#include "dispatchkit/dispatchkit.hpp" -#include "dispatchkit/function_call.hpp" -#include "dispatchkit/dynamic_object.hpp" -#include "dispatchkit/boxed_number.hpp" - -#include "language/chaiscript_eval.hpp" -#include "language/chaiscript_engine.hpp" +#include "chaiscript_basic.hpp" +#include "language/chaiscript_parser.hpp" +#include "chaiscript_stdlib.hpp" +namespace chaiscript +{ + class ChaiScript : public ChaiScript_Basic + { + public: + ChaiScript(std::vector t_modulepaths = {}, + std::vector t_usepaths = {}, + const std::vector &t_opts = {}) + : ChaiScript_Basic( + chaiscript::Std_Lib::library(), + std::make_unique>(), + t_modulepaths, t_usepaths, t_opts) + { + } + }; +} #endif /* CHAISCRIPT_HPP_ */ diff --git a/include/chaiscript/chaiscript_basic.hpp b/include/chaiscript/chaiscript_basic.hpp new file mode 100644 index 00000000..28f26d79 --- /dev/null +++ b/include/chaiscript/chaiscript_basic.hpp @@ -0,0 +1,39 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_BASIC_HPP_ +#define CHAISCRIPT_BASIC_HPP_ + +#include "chaiscript_defines.hpp" + +#include "dispatchkit/dispatchkit.hpp" +#include "dispatchkit/function_call.hpp" +#include "dispatchkit/dynamic_object.hpp" +#include "dispatchkit/boxed_number.hpp" + +#include "language/chaiscript_eval.hpp" +#include "language/chaiscript_engine.hpp" + +// This file includes all of the basic requirements for ChaiScript, +// to use, you might do something like: +// + +/* + +#include "chaiscript_stdlib.hpp" +#include "language/chaiscript_parser.hpp" + +ChaiScript_Basic chai( + chaiscript::Std_Lib::library(), + std::make_unique>()); + +*/ + +// If you want a fully packaged ready to go ChaiScript, use chaiscript.hpp + + + +#endif /* CHAISCRIPT_BASIC_HPP_ */ diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 4049381a..0a76a822 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -12,6 +12,9 @@ #define CHAISCRIPT_COMPILER_VERSION CHAISCRIPT_STRINGIZE(_MSC_FULL_VER) #define CHAISCRIPT_MSVC _MSC_VER #define CHAISCRIPT_HAS_DECLSPEC + +static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later required"); + #else #define CHAISCRIPT_COMPILER_VERSION __VERSION__ #endif @@ -60,6 +63,10 @@ #define CHAISCRIPT_MODULE_EXPORT extern "C" #endif +#if defined(CHAISCRIPT_MSVC) || (defined(__GNUC__) && __GNUC__ >= 5) || defined(CHAISCRIPT_CLANG) +#define CHAISCRIPT_UTF16_UTF32 +#endif + #ifdef _DEBUG #define CHAISCRIPT_DEBUG true #else @@ -89,18 +96,59 @@ namespace chaiscript { #endif } - template - Iter advance_copy(Iter iter, Distance distance) { - std::advance(iter, static_cast::difference_type>(distance)); - return iter; + struct Build_Info { + static int version_major() + { + return chaiscript::version_major; } + static int version_minor() + { + return chaiscript::version_minor; + } + + static int version_patch() + { + return chaiscript::version_patch; + } + + 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() + { + return compiler_name() + '-' + compiler_version(); + } + + static std::string build_id() + { + return compiler_id() + (debug_build()?"-Debug":"-Release"); + } + + static std::string compiler_version() + { + return chaiscript::compiler_version; + } + + static std::string compiler_name() + { + return chaiscript::compiler_name; + } + + static bool debug_build() + { + return chaiscript::debug_build; + } + }; + template auto parse_num(const char *t_str) -> typename std::enable_if::value, T>::type { T t = 0; - for (char c = *t_str; (c = *t_str); ++t_str) { + for (char c = *t_str; (c = *t_str) != 0; ++t_str) { if (c < '0' || c > '9') { return t; } @@ -161,6 +209,18 @@ namespace chaiscript { return parse_num(t_str.c_str()); } + enum class Options + { + No_Load_Modules, + Load_Modules, + No_External_Scripts, + External_Scripts + }; + + static inline std::vector default_options() + { + return {Options::Load_Modules, Options::External_Scripts}; + } } #endif diff --git a/include/chaiscript/chaiscript_stdlib.hpp b/include/chaiscript/chaiscript_stdlib.hpp index 50aa087a..ed56b234 100644 --- a/include/chaiscript/chaiscript_stdlib.hpp +++ b/include/chaiscript/chaiscript_stdlib.hpp @@ -14,11 +14,17 @@ #include #include "chaiscript_defines.hpp" -#include "dispatchkit/dispatchkit.hpp" +#include "language/chaiscript_common.hpp" + +#include "dispatchkit/function_call.hpp" + +//#include "dispatchkit/dispatchkit.hpp" +#include "dispatchkit/operators.hpp" #include "dispatchkit/bootstrap.hpp" #include "dispatchkit/bootstrap_stl.hpp" -#include "dispatchkit/boxed_value.hpp" +//#include "dispatchkit/boxed_value.hpp" #include "language/chaiscript_prelude.hpp" +#include "dispatchkit/register_function.hpp" #include "utility/json_wrap.hpp" #ifndef CHAISCRIPT_NO_THREADS @@ -38,18 +44,16 @@ namespace chaiscript static ModulePtr library() { - using namespace bootstrap; - auto lib = std::make_shared(); - Bootstrap::bootstrap(*lib); + bootstrap::Bootstrap::bootstrap(*lib); - standard_library::vector_type >("Vector", *lib); - standard_library::string_type("string", *lib); - standard_library::map_type >("Map", *lib); - standard_library::pair_type >("Pair", *lib); + bootstrap::standard_library::vector_type >("Vector", *lib); + bootstrap::standard_library::string_type("string", *lib); + bootstrap::standard_library::map_type >("Map", *lib); + bootstrap::standard_library::pair_type >("Pair", *lib); #ifndef CHAISCRIPT_NO_THREADS - standard_library::future_type>("future", *lib); + bootstrap::standard_library::future_type>("future", *lib); lib->add(chaiscript::fun([](const std::function &t_func){ return std::async(std::launch::async, t_func);}), "async"); #endif diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index 22506936..0d2825f7 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -108,6 +108,9 @@ namespace chaiscript #else +#pragma message ("Threading without thread_local support is not well supported.") + + /// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If /// threading is not enabled, the class always returns the same data, regardless of which thread it is called from. /// diff --git a/include/chaiscript/dispatchkit/any.hpp b/include/chaiscript/dispatchkit/any.hpp index e4920319..4d47b88f 100644 --- a/include/chaiscript/dispatchkit/any.hpp +++ b/include/chaiscript/dispatchkit/any.hpp @@ -21,10 +21,7 @@ namespace chaiscript { class bad_any_cast : public std::bad_cast { public: - bad_any_cast() noexcept - : m_what("bad any cast") - { - } + bad_any_cast() = default; bad_any_cast(const bad_any_cast &) = default; @@ -37,7 +34,7 @@ namespace chaiscript { } private: - std::string m_what; + std::string m_what = "bad any cast"; }; } @@ -151,8 +148,7 @@ namespace chaiscript { const std::type_info & type() const { - if (m_data) - { + if (m_data) { return m_data->type(); } else { return typeid(void); diff --git a/include/chaiscript/dispatchkit/bad_boxed_cast.hpp b/include/chaiscript/dispatchkit/bad_boxed_cast.hpp index ee2be60f..d0b7c94b 100644 --- a/include/chaiscript/dispatchkit/bad_boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/bad_boxed_cast.hpp @@ -41,7 +41,7 @@ namespace chaiscript } explicit bad_boxed_cast(std::string t_what) noexcept - : to(nullptr), m_what(std::move(t_what)) + : m_what(std::move(t_what)) { } @@ -55,7 +55,7 @@ namespace chaiscript } Type_Info from; ///< Type_Info contained in the Boxed_Value - const std::type_info *to; ///< std::type_info of the desired (but failed) result type + const std::type_info *to = nullptr; ///< std::type_info of the desired (but failed) result type private: std::string m_what; diff --git a/include/chaiscript/dispatchkit/bind_first.hpp b/include/chaiscript/dispatchkit/bind_first.hpp index fcba5348..98da1deb 100644 --- a/include/chaiscript/dispatchkit/bind_first.hpp +++ b/include/chaiscript/dispatchkit/bind_first.hpp @@ -27,45 +27,52 @@ namespace chaiscript } template - std::function bind_first(Ret (*f)(P1, Param...), O&& o) + auto bind_first(Ret (*f)(P1, Param...), O&& o) { - return std::function( - [f, o](Param...param) -> Ret { - return f(std::forward(o), std::forward(param)...); - } - ); + return [f, o](Param...param) -> Ret { + return f(std::forward(o), std::forward(param)...); + }; } template - std::function bind_first(Ret (Class::*f)(Param...), O&& o) + auto bind_first(Ret (Class::*f)(Param...), O&& o) { - return std::function( - [f, o](Param...param) -> Ret { - return (get_pointer(o)->*f)(std::forward(param)...); - } - ); + return [f, o](Param...param) -> Ret { + return (get_pointer(o)->*f)(std::forward(param)...); + }; } template - std::function bind_first(Ret (Class::*f)(Param...) const, O&& o) + auto bind_first(Ret (Class::*f)(Param...) const, O&& o) { - return std::function( - [f, o](Param...param) -> Ret { - return (get_pointer(o)->*f)(std::forward(param)...); - } - ); + return [f, o](Param...param) -> Ret { + return (get_pointer(o)->*f)(std::forward(param)...); + }; } template - std::function bind_first(const std::function &f, O&& o) + auto bind_first(const std::function &f, O&& o) { - return std::function( - [f, o](Param...param) -> Ret { - return f(o, std::forward(param)...); - }); + return [f, o](Param...param) -> Ret { + return f(o, std::forward(param)...); + }; } + template + auto bind_first(const F &fo, O&& o, Ret (Class::*f)(P1, Param...) const) + { + return [fo, o, f](Param ...param) -> Ret { + return (fo.*f)(o, std::forward(param)...); + }; + + } + + template + auto bind_first(const F &f, O&& o) + { + return bind_first(f, std::forward(o), &F::operator()); + } } } diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 4bee74d3..45143636 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -7,50 +7,14 @@ #ifndef CHAISCRIPT_BOOTSTRAP_HPP_ #define CHAISCRIPT_BOOTSTRAP_HPP_ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bad_boxed_cast.hpp" -#include "boxed_cast.hpp" -#include "boxed_number.hpp" -#include "boxed_value.hpp" -#include "dispatchkit.hpp" -#include "type_conversions.hpp" -#include "dynamic_object.hpp" -#include "operators.hpp" -#include "proxy_constructors.hpp" -#include "proxy_functions.hpp" -#include "proxy_functions_detail.hpp" -#include "register_function.hpp" -#include "type_info.hpp" #include "../utility/utility.hpp" -#include "../language/chaiscript_common.hpp" +#include "register_function.hpp" namespace chaiscript { /// \brief Classes and functions useful for bootstrapping of ChaiScript and adding of new types namespace bootstrap { - namespace detail - { - /// \brief Constructs a new POD value object from a Boxed_Number - /// \param[in] v Boxed_Number to copy into the new object - /// \returns The newly created object. - template - std::shared_ptr construct_pod(const Boxed_Number &v) - { - return std::make_shared(v.get_as()); - } - } - template::value>::type > void array(const std::string &type, Module& m) { @@ -134,19 +98,10 @@ namespace chaiscript template void construct_pod(const std::string &type, Module& m) { - m.add(fun(&detail::construct_pod), type); + m.add(fun([](const Boxed_Number &bn){ return bn.get_as(); }), type); } - /// to_string function for internal use. Uses ostream operator<< - template - std::string to_string(Input i) - { - std::stringstream ss; - ss << i; - return ss.str(); - } - /// Internal function for converting from a string to a value /// uses ostream operator >> to perform the conversion template @@ -194,15 +149,14 @@ namespace chaiscript /// for handling of Proxy_Function object (that is, /// function variables. template - std::shared_ptr shared_ptr_clone(const std::shared_ptr &p) + auto shared_ptr_clone(const std::shared_ptr &p) { return p; } /// Specific version of shared_ptr_clone just for Proxy_Functions template - std::shared_ptr::type> - shared_ptr_unconst_clone(const std::shared_ptr::type> &p) + std::shared_ptr::type> shared_ptr_unconst_clone(const std::shared_ptr::type> &p) { return std::const_pointer_cast::type>(p); } @@ -288,8 +242,6 @@ namespace chaiscript m.add(fun(&Boxed_Number::product), "*"); m.add(fun(&Boxed_Number::remainder), "%"); m.add(fun(&Boxed_Number::shift_right), ">>"); - - } /// Create a bound function object. The first param is the function to bind @@ -329,26 +281,6 @@ namespace chaiscript } } - static void throw_exception(const Boxed_Value &bv) { - throw bv; - } - - static std::string what(const std::exception &e) - { - return e.what(); - } - - /// Boolean specialization of internal to_string function - static std::string bool_to_string(bool b) - { - if (b) - { - return "true"; - } else { - return "false"; - } - } - template static std::vector do_return_boxed_value_vector(FunctionType f, const dispatch::Proxy_Function_Base *b) @@ -384,7 +316,7 @@ namespace chaiscript } template - static std::function (const dispatch::Proxy_Function_Base*)> return_boxed_value_vector(const Function &f) + static auto return_boxed_value_vector(const Function &f) { return [f](const dispatch::Proxy_Function_Base *b) { return do_return_boxed_value_vector(f, b); @@ -407,13 +339,13 @@ namespace chaiscript m.add(user_type(), "exception"); m.add(fun(&dispatch::Proxy_Function_Base::get_arity), "get_arity"); - m.add(fun(&dispatch::Proxy_Function_Base::annotation), "get_annotation"); m.add(fun(&dispatch::Proxy_Function_Base::operator==), "=="); m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_param_types)), "get_param_types"); m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_contained_functions)), "get_contained_functions"); + m.add(fun([](const std::exception &e){ return std::string(e.what()); }), "what"); m.add(user_type(), "out_of_range"); m.add(user_type(), "logic_error"); @@ -425,7 +357,6 @@ namespace chaiscript m.add(chaiscript::base_class()); m.add(constructor(), "runtime_error"); - m.add(fun(std::function(&what)), "what"); m.add(user_type(), "Dynamic_Object"); m.add(constructor(), "Dynamic_Object"); @@ -520,15 +451,15 @@ namespace chaiscript operators::equal(m); operators::not_equal(m); - m.add(fun([](const std::string &s) -> std::string { return s; }), "to_string"); - m.add(fun(&Bootstrap::bool_to_string), "to_string"); + m.add(fun([](const std::string &s) { return s; }), "to_string"); + m.add(fun([](const bool b) { return std::string(b?"true":"false"); }), "to_string"); m.add(fun(&unknown_assign), "="); - m.add(fun(&throw_exception), "throw"); - m.add(fun(&what), "what"); + m.add(fun([](const Boxed_Value &bv) { throw bv; }), "throw"); - m.add(fun(&to_string), "to_string"); + m.add(fun([](const char c) { return std::string(1, c); }), "to_string"); m.add(fun(&Boxed_Number::to_string), "to_string"); + bootstrap_pod_type("double", m); bootstrap_pod_type("long_double", m); bootstrap_pod_type("float", m); @@ -552,10 +483,21 @@ namespace chaiscript bootstrap_pod_type("uint32_t", m); bootstrap_pod_type("uint64_t", m); + operators::logical_compliment(m); opers_arithmetic_pod(m); + + m.add(fun(&Build_Info::version_major), "version_major"); + m.add(fun(&Build_Info::version_minor), "version_minor"); + m.add(fun(&Build_Info::version_patch), "version_patch"); + m.add(fun(&Build_Info::version), "version"); + m.add(fun(&Build_Info::compiler_version), "compiler_version"); + m.add(fun(&Build_Info::compiler_name), "compiler_name"); + m.add(fun(&Build_Info::compiler_id), "compiler_id"); + m.add(fun(&Build_Info::debug_build), "debug_build"); + m.add(fun(&print), "print_string"); m.add(fun(&println), "println_string"); @@ -580,9 +522,11 @@ namespace chaiscript m.add(chaiscript::fun(&get_parse_tree), "get_parse_tree"); m.add(chaiscript::base_class()); + m.add(chaiscript::base_class()); m.add(chaiscript::user_type(), "arithmetic_error"); m.add(chaiscript::base_class()); + m.add(chaiscript::base_class()); // chaiscript::bootstrap::standard_library::vector_type > >("AST_NodeVector", m); @@ -593,13 +537,13 @@ namespace chaiscript { }, { {fun(&chaiscript::exception::eval_error::reason), "reason"}, {fun(&chaiscript::exception::eval_error::pretty_print), "pretty_print"}, - {fun(std::function (const chaiscript::exception::eval_error &t_eval_error)>([](const chaiscript::exception::eval_error &t_eval_error) -> std::vector { + {fun([](const chaiscript::exception::eval_error &t_eval_error) { std::vector retval; std::transform(t_eval_error.call_stack.begin(), t_eval_error.call_stack.end(), std::back_inserter(retval), &chaiscript::var &>); return retval; - })), "call_stack"} } + }), "call_stack"} } ); @@ -621,24 +565,17 @@ namespace chaiscript {fun(&AST_Node::start), "start"}, {fun(&AST_Node::end), "end"}, {fun(&AST_Node::to_string), "to_string"}, - {fun(std::function (const chaiscript::AST_Node &t_node)>([](const chaiscript::AST_Node &t_node) -> std::vector { + {fun([](const chaiscript::AST_Node &t_node) -> std::vector { std::vector retval; - std::transform(t_node.children.begin(), t_node.children.end(), + const auto children = t_node.get_children(); + std::transform(children.begin(), children.end(), std::back_inserter(retval), &chaiscript::var &>); return retval; - })), "children"}, - {fun(&AST_Node::replace_child), "replace_child"} + }), "children"} } ); - - chaiscript::utility::add_class(m, - "ChaiScript_Parser", - { constructor() }, - { {fun(&parser::ChaiScript_Parser::parse), "parse"}, - {fun(&parser::ChaiScript_Parser::ast), "ast"} } - ); } }; } diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index b9e0a9f5..6f59ff07 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -41,7 +41,6 @@ namespace chaiscript struct Bidir_Range { typedef Container container_type; - typedef typename std::iterator_traits::reference reference_type; Bidir_Range(Container &c) : m_begin(c.begin()), m_end(c.end()) @@ -71,16 +70,16 @@ namespace chaiscript --m_end; } - reference_type front() const + decltype(auto) front() const { if (empty()) { throw std::range_error("Range empty"); } - return *m_begin; + return (*m_begin); } - reference_type back() const + decltype(auto) back() const { if (empty()) { @@ -88,7 +87,7 @@ namespace chaiscript } typename Container::iterator pos = m_end; --pos; - return *(pos); + return (*(pos)); } typename Container::iterator m_begin; @@ -129,24 +128,24 @@ namespace chaiscript --m_end; } - const_reference_type front() const + decltype(auto) front() const { if (empty()) { throw std::range_error("Range empty"); } - return *m_begin; + return (*m_begin); } - const_reference_type back() const + decltype(auto) back() const { if (empty()) { throw std::range_error("Range empty"); } - typename Container::const_iterator pos = m_end; + auto pos = m_end; --pos; - return *(pos); + return (*(pos)); } typename Container::const_iterator m_begin; @@ -482,12 +481,8 @@ namespace chaiscript { m.add(user_type(), type); - - typename PairType::first_type PairType::* f = &PairType::first; - typename PairType::second_type PairType::* s = &PairType::second; - - m.add(fun(f), "first"); - m.add(fun(s), "second"); + m.add(fun(&PairType::first), "first"); + m.add(fun(&PairType::second), "second"); basic_constructors(type, m); m.add(constructor(), type); @@ -605,7 +600,6 @@ namespace chaiscript } - /// hopefully working List type /// http://www.sgi.com/tech/stl/List.html template void list_type(const std::string &type, Module& m) diff --git a/include/chaiscript/dispatchkit/boxed_cast.hpp b/include/chaiscript/dispatchkit/boxed_cast.hpp index 5a11ee9d..7fbe78d3 100644 --- a/include/chaiscript/dispatchkit/boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast.hpp @@ -69,11 +69,11 @@ namespace chaiscript /// assert(i == 5); /// \endcode template - typename detail::Cast_Helper::Result_Type boxed_cast(const Boxed_Value &bv, const Type_Conversions_State *t_conversions = nullptr) + decltype(auto) boxed_cast(const Boxed_Value &bv, const Type_Conversions_State *t_conversions = nullptr) { if (!t_conversions || bv.get_type_info().bare_equal(user_type()) || (t_conversions && !(*t_conversions)->convertable_type())) { try { - return detail::Cast_Helper::cast(bv, t_conversions); + return(detail::Cast_Helper::cast(bv, t_conversions)); } catch (const chaiscript::detail::exception::bad_any_cast &) { } } @@ -84,11 +84,11 @@ namespace chaiscript try { // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it // either way, we are not responsible if it doesn't work - return detail::Cast_Helper::cast((*t_conversions)->boxed_type_conversion(t_conversions->saves(), bv), t_conversions); + return(detail::Cast_Helper::cast((*t_conversions)->boxed_type_conversion(t_conversions->saves(), bv), t_conversions)); } catch (...) { try { // try going the other way - return detail::Cast_Helper::cast((*t_conversions)->boxed_type_down_conversion(t_conversions->saves(), bv), t_conversions); + return(detail::Cast_Helper::cast((*t_conversions)->boxed_type_down_conversion(t_conversions->saves(), bv), t_conversions)); } catch (const chaiscript::detail::exception::bad_any_cast &) { throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); } diff --git a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp index da1e222e..6347437e 100644 --- a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp @@ -29,21 +29,50 @@ namespace chaiscript throw std::runtime_error("Attempted to dereference null Boxed_Value"); } + template + static const T *verify_type_no_throw(const Boxed_Value &ob, const std::type_info &ti, const T *ptr) { + if (ob.get_type_info() == ti) { + return ptr; + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static T *verify_type_no_throw(const Boxed_Value &ob, const std::type_info &ti, T *ptr) { + if (!ob.is_const() && ob.get_type_info() == ti) { + return ptr; + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + + template + static const T *verify_type(const Boxed_Value &ob, const std::type_info &ti, const T *ptr) { + if (ob.get_type_info().bare_equal_type_info(ti)) { + return throw_if_null(ptr); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static T *verify_type(const Boxed_Value &ob, const std::type_info &ti, T *ptr) { + if (!ob.is_const() && ob.get_type_info().bare_equal_type_info(ti)) { + return throw_if_null(ptr); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + /// Generic Cast_Helper_Inner, for casting to any type template struct Cast_Helper_Inner { - typedef typename std::add_const::type Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static Result cast(const Boxed_Value &ob, const Type_Conversions_State *) { - if (ob.get_type_info().bare_equal_type_info(typeid(Result))) - { - auto p = throw_if_null(ob.get_const_ptr()); - return *static_cast(p); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } + return *static_cast(verify_type(ob, typeid(Result), ob.get_const_ptr())); } }; @@ -57,15 +86,9 @@ namespace chaiscript template struct Cast_Helper_Inner { - typedef const Result * Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static const Result * cast(const Boxed_Value &ob, const Type_Conversions_State *) { - if (ob.get_type_info().bare_equal_type_info(typeid(Result))) - { - return static_cast(ob.get_const_ptr()); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } + return static_cast(verify_type_no_throw(ob, typeid(Result), ob.get_const_ptr())); } }; @@ -73,15 +96,9 @@ namespace chaiscript template struct Cast_Helper_Inner { - typedef Result * Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static Result * cast(const Boxed_Value &ob, const Type_Conversions_State *) { - if (!ob.get_type_info().is_const() && ob.get_type_info() == typeid(Result)) - { - return static_cast(ob.get_ptr()); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } + return static_cast(verify_type_no_throw(ob, typeid(Result), ob.get_ptr())); } }; @@ -100,17 +117,9 @@ namespace chaiscript template struct Cast_Helper_Inner { - typedef const Result& Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static const Result & cast(const Boxed_Value &ob, const Type_Conversions_State *) { - if (ob.get_type_info().bare_equal_type_info(typeid(Result))) - { - auto p = throw_if_null(ob.get_const_ptr()); - return *static_cast(p); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } + return *static_cast(verify_type(ob, typeid(Result), ob.get_const_ptr())); } }; @@ -120,16 +129,9 @@ namespace chaiscript template struct Cast_Helper_Inner { - typedef Result& Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static Result& cast(const Boxed_Value &ob, const Type_Conversions_State *) { - if (!ob.get_type_info().is_const() && ob.get_type_info().bare_equal_type_info(typeid(Result))) - { - return *(static_cast(throw_if_null(ob.get_ptr()))); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } + return *static_cast(verify_type(ob, typeid(Result), ob.get_ptr())); } }; @@ -137,9 +139,7 @@ namespace chaiscript template struct Cast_Helper_Inner > { - typedef std::shared_ptr Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { return ob.get().cast >(); } @@ -149,9 +149,7 @@ namespace chaiscript template struct Cast_Helper_Inner > { - typedef std::shared_ptr Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { if (!ob.get_type_info().is_const()) { @@ -177,10 +175,7 @@ namespace chaiscript struct Cast_Helper_Inner &> { static_assert(!std::is_const::value, "Non-const reference to std::shared_ptr is not supported"); - - typedef Boxed_Value::Sentinel Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { std::shared_ptr &res = ob.get().cast >(); return ob.pointer_sentinel(res); @@ -204,9 +199,7 @@ namespace chaiscript template<> struct Cast_Helper_Inner { - typedef Boxed_Value Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static Boxed_Value cast(const Boxed_Value &ob, const Type_Conversions_State *) { return ob; } @@ -216,9 +209,7 @@ namespace chaiscript template<> struct Cast_Helper_Inner { - typedef std::reference_wrapper Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static std::reference_wrapper cast(const Boxed_Value &ob, const Type_Conversions_State *) { return std::ref(const_cast(ob)); } @@ -272,11 +263,9 @@ namespace chaiscript template struct Cast_Helper { - typedef typename Cast_Helper_Inner::Result_Type Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) + static decltype(auto) cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { - return Cast_Helper_Inner::cast(ob, t_conversions); + return(Cast_Helper_Inner::cast(ob, t_conversions)); } }; } diff --git a/include/chaiscript/dispatchkit/boxed_number.hpp b/include/chaiscript/dispatchkit/boxed_number.hpp index 7eefe991..4517de28 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -162,17 +162,17 @@ namespace chaiscript { switch (t_oper) { - case Operators::equals: + case Operators::Opers::equals: return const_var(t == u); - case Operators::less_than: + case Operators::Opers::less_than: return const_var(t < u); - case Operators::greater_than: + case Operators::Opers::greater_than: return const_var(t > u); - case Operators::less_than_equal: + case Operators::Opers::less_than_equal: return const_var(t <= u); - case Operators::greater_than_equal: + case Operators::Opers::greater_than_equal: return const_var(t >= u); - case Operators::not_equal: + case Operators::Opers::not_equal: return const_var(t != u); default: throw chaiscript::detail::exception::bad_any_cast(); @@ -184,10 +184,10 @@ namespace chaiscript { switch (t_oper) { - case Operators::pre_increment: + case Operators::Opers::pre_increment: ++t; break; - case Operators::pre_decrement: + case Operators::Opers::pre_decrement: --t; break; default: @@ -202,20 +202,20 @@ namespace chaiscript { switch (t_oper) { - case Operators::assign: + case Operators::Opers::assign: t = u; break; - case Operators::assign_product: + case Operators::Opers::assign_product: t *= u; break; - case Operators::assign_sum: + case Operators::Opers::assign_sum: t += u; break; - case Operators::assign_quotient: + case Operators::Opers::assign_quotient: check_divide_by_zero(u); t /= u; break; - case Operators::assign_difference: + case Operators::Opers::assign_difference: t -= u; break; default: @@ -230,23 +230,23 @@ namespace chaiscript { switch (t_oper) { - case Operators::assign_bitwise_and: + case Operators::Opers::assign_bitwise_and: t &= u; break; - case Operators::assign_bitwise_or: + case Operators::Opers::assign_bitwise_or: t |= u; break; - case Operators::assign_shift_left: + case Operators::Opers::assign_shift_left: t <<= u; break; - case Operators::assign_shift_right: + case Operators::Opers::assign_shift_right: t >>= u; break; - case Operators::assign_remainder: + case Operators::Opers::assign_remainder: check_divide_by_zero(u); t %= u; break; - case Operators::assign_bitwise_xor: + case Operators::Opers::assign_bitwise_xor: t ^= u; break; default: @@ -260,7 +260,7 @@ namespace chaiscript { switch (t_oper) { - case Operators::bitwise_complement: + case Operators::Opers::bitwise_complement: return const_var(~t); default: throw chaiscript::detail::exception::bad_any_cast(); @@ -272,18 +272,18 @@ namespace chaiscript { switch (t_oper) { - case Operators::shift_left: + case Operators::Opers::shift_left: return const_var(t << u); - case Operators::shift_right: + case Operators::Opers::shift_right: return const_var(t >> u); - case Operators::remainder: + case Operators::Opers::remainder: check_divide_by_zero(u); return const_var(t % u); - case Operators::bitwise_and: + case Operators::Opers::bitwise_and: return const_var(t & u); - case Operators::bitwise_or: + case Operators::Opers::bitwise_or: return const_var(t | u); - case Operators::bitwise_xor: + case Operators::Opers::bitwise_xor: return const_var(t ^ u); default: throw chaiscript::detail::exception::bad_any_cast(); @@ -295,9 +295,9 @@ namespace chaiscript { switch (t_oper) { - case Operators::unary_minus: + case Operators::Opers::unary_minus: return const_var(-t); - case Operators::unary_plus: + case Operators::Opers::unary_plus: return const_var(+t); default: throw chaiscript::detail::exception::bad_any_cast(); @@ -309,14 +309,14 @@ namespace chaiscript { switch (t_oper) { - case Operators::sum: + case Operators::Opers::sum: return const_var(t + u); - case Operators::quotient: + case Operators::Opers::quotient: check_divide_by_zero(u); return const_var(t / u); - case Operators::product: + case Operators::Opers::product: return const_var(t * u); - case Operators::difference: + case Operators::Opers::difference: return const_var(t - u); default: throw chaiscript::detail::exception::bad_any_cast(); @@ -328,16 +328,16 @@ namespace chaiscript -> typename std::enable_if::value && !std::is_floating_point::value, Boxed_Value>::type { typedef typename std::common_type::type common_type; - if (t_oper > Operators::boolean_flag && t_oper < Operators::non_const_flag) + if (t_oper > Operators::Opers::boolean_flag && t_oper < Operators::Opers::non_const_flag) { return boolean_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); - } else if (t_oper > Operators::non_const_flag && t_oper < Operators::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { + } else if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { return binary_go(t_oper, *static_cast(t_lhs.get_ptr()), get_as_aux(t_rhs), t_lhs); - } else if (t_oper > Operators::non_const_int_flag && t_oper < Operators::const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { + } else if (t_oper > Operators::Opers::non_const_int_flag && t_oper < Operators::Opers::const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { return binary_int_go(t_oper, *static_cast(t_lhs.get_ptr()), get_as_aux(t_rhs), t_lhs); - } else if (t_oper > Operators::const_int_flag && t_oper < Operators::const_flag) { + } else if (t_oper > Operators::Opers::const_int_flag && t_oper < Operators::Opers::const_flag) { return const_binary_int_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); - } else if (t_oper > Operators::const_flag) { + } else if (t_oper > Operators::Opers::const_flag) { return const_binary_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); } else { throw chaiscript::detail::exception::bad_any_cast(); @@ -349,12 +349,12 @@ namespace chaiscript -> typename std::enable_if::value || std::is_floating_point::value, Boxed_Value>::type { typedef typename std::common_type::type common_type; - if (t_oper > Operators::boolean_flag && t_oper < Operators::non_const_flag) + if (t_oper > Operators::Opers::boolean_flag && t_oper < Operators::Opers::non_const_flag) { return boolean_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); - } else if (t_oper > Operators::non_const_flag && t_oper < Operators::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { + } else if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { return binary_go(t_oper, *static_cast(t_lhs.get_ptr()), get_as_aux(t_rhs), t_lhs); - } else if (t_oper > Operators::const_flag) { + } else if (t_oper > Operators::Opers::const_flag) { return const_binary_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); } else { throw chaiscript::detail::exception::bad_any_cast(); @@ -366,11 +366,11 @@ namespace chaiscript static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs) -> typename std::enable_if::value, Boxed_Value>::type { - if (t_oper > Operators::non_const_flag && t_oper < Operators::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { + if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { return unary_go(t_oper, *static_cast(t_lhs.get_ptr()), t_lhs); - } else if (t_oper > Operators::const_int_flag && t_oper < Operators::const_flag) { + } else if (t_oper > Operators::Opers::const_int_flag && t_oper < Operators::Opers::const_flag) { return const_unary_int_go(t_oper, *static_cast(t_lhs.get_const_ptr())); - } else if (t_oper > Operators::const_flag) { + } else if (t_oper > Operators::Opers::const_flag) { return const_unary_go(t_oper, *static_cast(t_lhs.get_const_ptr())); } else { throw chaiscript::detail::exception::bad_any_cast(); @@ -381,9 +381,9 @@ namespace chaiscript static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs) -> typename std::enable_if::value, Boxed_Value>::type { - if (t_oper > Operators::non_const_flag && t_oper < Operators::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { + if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { return unary_go(t_oper, *static_cast(t_lhs.get_ptr()), t_lhs); - } else if (t_oper > Operators::const_flag) { + } else if (t_oper > Operators::Opers::const_flag) { return const_unary_go(t_oper, *static_cast(t_lhs.get_const_ptr())); } else { throw chaiscript::detail::exception::bad_any_cast(); @@ -502,7 +502,7 @@ namespace chaiscript { } - Boxed_Number(Boxed_Value v) + explicit Boxed_Number(Boxed_Value v) : bv(std::move(v)) { validate_boxed_number(bv); @@ -645,71 +645,6 @@ namespace chaiscript throw chaiscript::detail::exception::bad_any_cast(); } - bool operator==(const Boxed_Number &t_rhs) const - { - return boxed_cast(oper(Operators::equals, this->bv, t_rhs.bv)); - } - - bool operator<(const Boxed_Number &t_rhs) const - { - return boxed_cast(oper(Operators::less_than, this->bv, t_rhs.bv)); - } - - bool operator>(const Boxed_Number &t_rhs) const - { - return boxed_cast(oper(Operators::greater_than, this->bv, t_rhs.bv)); - } - - bool operator>=(const Boxed_Number &t_rhs) const - { - return boxed_cast(oper(Operators::greater_than_equal, this->bv, t_rhs.bv)); - } - - bool operator<=(const Boxed_Number &t_rhs) const - { - return boxed_cast(oper(Operators::less_than_equal, this->bv, t_rhs.bv)); - } - - bool operator!=(const Boxed_Number &t_rhs) const - { - return boxed_cast(oper(Operators::not_equal, this->bv, t_rhs.bv)); - } - - Boxed_Number operator--() - { - return oper(Operators::pre_decrement, this->bv); - } - - Boxed_Number operator++() - { - return oper(Operators::pre_increment, this->bv); - } - - Boxed_Number operator+(const Boxed_Number &t_rhs) const - { - return oper(Operators::sum, this->bv, t_rhs.bv); - } - - Boxed_Number operator+() const - { - return oper(Operators::unary_plus, this->bv); - } - - Boxed_Number operator-() const - { - return oper(Operators::unary_minus, this->bv); - } - - Boxed_Number operator-(const Boxed_Number &t_rhs) const - { - return oper(Operators::difference, this->bv, t_rhs.bv); - } - - Boxed_Number operator&=(const Boxed_Number &t_rhs) - { - return oper(Operators::assign_bitwise_and, this->bv, t_rhs.bv); - } - static void validate_boxed_number(const Boxed_Value &v) { const Type_Info &inp_ = v.get_type_info(); @@ -724,266 +659,165 @@ namespace chaiscript } } - // cppcheck-suppress operatorEq - Boxed_Number operator=(const Boxed_Value &v) - { - validate_boxed_number(v); - bv = v; - return *this; - } - - // cppcheck-suppress operatorEq - Boxed_Number operator=(const Boxed_Number &t_rhs) const - { - return oper(Operators::assign, this->bv, t_rhs.bv); - } - - Boxed_Number operator|=(const Boxed_Number &t_rhs) - { - return oper(Operators::assign_bitwise_or, this->bv, t_rhs.bv); - } - - Boxed_Number operator^=(const Boxed_Number &t_rhs) - { - return oper(Operators::assign_bitwise_xor, this->bv, t_rhs.bv); - } - - Boxed_Number operator%=(const Boxed_Number &t_rhs) - { - return oper(Operators::assign_remainder, this->bv, t_rhs.bv); - } - - Boxed_Number operator<<=(const Boxed_Number &t_rhs) - { - return oper(Operators::assign_shift_left, this->bv, t_rhs.bv); - } - - Boxed_Number operator>>=(const Boxed_Number &t_rhs) - { - return oper(Operators::assign_shift_right, this->bv, t_rhs.bv); - } - - Boxed_Number operator&(const Boxed_Number &t_rhs) const - { - return oper(Operators::bitwise_and, this->bv, t_rhs.bv); - } - - Boxed_Number operator~() const - { - return oper(Operators::bitwise_complement, this->bv); - } - - Boxed_Number operator^(const Boxed_Number &t_rhs) const - { - return oper(Operators::bitwise_xor, this->bv, t_rhs.bv); - } - - Boxed_Number operator|(const Boxed_Number &t_rhs) const - { - return oper(Operators::bitwise_or, this->bv, t_rhs.bv); - } - - Boxed_Number operator*=(const Boxed_Number &t_rhs) - { - return oper(Operators::assign_product, this->bv, t_rhs.bv); - } - Boxed_Number operator/=(const Boxed_Number &t_rhs) - { - return oper(Operators::assign_quotient, this->bv, t_rhs.bv); - } - Boxed_Number operator+=(const Boxed_Number &t_rhs) - { - return oper(Operators::assign_sum, this->bv, t_rhs.bv); - } - Boxed_Number operator-=(const Boxed_Number &t_rhs) - { - return oper(Operators::assign_difference, this->bv, t_rhs.bv); - } - - Boxed_Number operator/(const Boxed_Number &t_rhs) const - { - return oper(Operators::quotient, this->bv, t_rhs.bv); - } - - Boxed_Number operator<<(const Boxed_Number &t_rhs) const - { - return oper(Operators::shift_left, this->bv, t_rhs.bv); - } - - Boxed_Number operator*(const Boxed_Number &t_rhs) const - { - return oper(Operators::product, this->bv, t_rhs.bv); - } - - Boxed_Number operator%(const Boxed_Number &t_rhs) const - { - return oper(Operators::remainder, this->bv, t_rhs.bv); - } - - Boxed_Number operator>>(const Boxed_Number &t_rhs) const - { - return oper(Operators::shift_right, this->bv, t_rhs.bv); - } - static bool equals(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return boxed_cast(oper(Operators::equals, t_lhs.bv, t_rhs.bv)); + return boxed_cast(oper(Operators::Opers::equals, t_lhs.bv, t_rhs.bv)); } static bool less_than(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return boxed_cast(oper(Operators::less_than, t_lhs.bv, t_rhs.bv)); + return boxed_cast(oper(Operators::Opers::less_than, t_lhs.bv, t_rhs.bv)); } static bool greater_than(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return boxed_cast(oper(Operators::greater_than, t_lhs.bv, t_rhs.bv)); + return boxed_cast(oper(Operators::Opers::greater_than, t_lhs.bv, t_rhs.bv)); } static bool greater_than_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return boxed_cast(oper(Operators::greater_than_equal, t_lhs.bv, t_rhs.bv)); + return boxed_cast(oper(Operators::Opers::greater_than_equal, t_lhs.bv, t_rhs.bv)); } static bool less_than_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return boxed_cast(oper(Operators::less_than_equal, t_lhs.bv, t_rhs.bv)); + return boxed_cast(oper(Operators::Opers::less_than_equal, t_lhs.bv, t_rhs.bv)); } static bool not_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return boxed_cast(oper(Operators::not_equal, t_lhs.bv, t_rhs.bv)); + return boxed_cast(oper(Operators::Opers::not_equal, t_lhs.bv, t_rhs.bv)); } static Boxed_Number pre_decrement(Boxed_Number t_lhs) { - return oper(Operators::pre_decrement, t_lhs.bv); + return Boxed_Number(oper(Operators::Opers::pre_decrement, t_lhs.bv)); } static Boxed_Number pre_increment(Boxed_Number t_lhs) { - return oper(Operators::pre_increment, t_lhs.bv); + return Boxed_Number(oper(Operators::Opers::pre_increment, t_lhs.bv)); } static const Boxed_Number sum(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::sum, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::sum, t_lhs.bv, t_rhs.bv)); } static const Boxed_Number unary_plus(const Boxed_Number &t_lhs) { - return oper(Operators::unary_plus, t_lhs.bv); + return Boxed_Number(oper(Operators::Opers::unary_plus, t_lhs.bv)); } static const Boxed_Number unary_minus(const Boxed_Number &t_lhs) { - return oper(Operators::unary_minus, t_lhs.bv); + return Boxed_Number(oper(Operators::Opers::unary_minus, t_lhs.bv)); } static const Boxed_Number difference(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::difference, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::difference, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign_bitwise_and(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign_bitwise_and, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign_bitwise_and, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign_bitwise_or(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign_bitwise_or, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign_bitwise_or, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign_bitwise_xor(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign_bitwise_xor, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign_bitwise_xor, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign_remainder(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign_remainder, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign_remainder, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign_shift_left(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign_shift_left, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign_shift_left, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign_shift_right(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign_shift_right, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign_shift_right, t_lhs.bv, t_rhs.bv)); } static const Boxed_Number bitwise_and(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::bitwise_and, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::bitwise_and, t_lhs.bv, t_rhs.bv)); } static const Boxed_Number bitwise_complement(const Boxed_Number &t_lhs) { - return oper(Operators::bitwise_complement, t_lhs.bv, Boxed_Value(0)); + return Boxed_Number(oper(Operators::Opers::bitwise_complement, t_lhs.bv, Boxed_Value(0))); } static const Boxed_Number bitwise_xor(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::bitwise_xor, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::bitwise_xor, t_lhs.bv, t_rhs.bv)); } static const Boxed_Number bitwise_or(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::bitwise_or, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::bitwise_or, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign_product(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign_product, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign_product, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign_quotient(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign_quotient, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign_quotient, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign_sum(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign_sum, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign_sum, t_lhs.bv, t_rhs.bv)); } static Boxed_Number assign_difference(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::assign_difference, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::assign_difference, t_lhs.bv, t_rhs.bv)); } static const Boxed_Number quotient(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::quotient, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::quotient, t_lhs.bv, t_rhs.bv)); } static const Boxed_Number shift_left(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::shift_left, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::shift_left, t_lhs.bv, t_rhs.bv)); } static const Boxed_Number product(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::product, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::product, t_lhs.bv, t_rhs.bv)); } static const Boxed_Number remainder(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::remainder, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::remainder, t_lhs.bv, t_rhs.bv)); } static const Boxed_Number shift_right(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { - return oper(Operators::shift_right, t_lhs.bv, t_rhs.bv); + return Boxed_Number(oper(Operators::Opers::shift_right, t_lhs.bv, t_rhs.bv)); } @@ -1009,9 +843,7 @@ namespace chaiscript template<> struct Cast_Helper { - typedef Boxed_Number Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + static Boxed_Number cast(const Boxed_Value &ob, const Type_Conversions_State *) { return Boxed_Number(ob); } diff --git a/include/chaiscript/dispatchkit/boxed_value.hpp b/include/chaiscript/dispatchkit/boxed_value.hpp index 538391f6..70a1b967 100644 --- a/include/chaiscript/dispatchkit/boxed_value.hpp +++ b/include/chaiscript/dispatchkit/boxed_value.hpp @@ -77,9 +77,9 @@ namespace chaiscript struct Object_Data { - static std::unique_ptr get(Boxed_Value::Void_Type, bool t_return_value) + static auto get(Boxed_Value::Void_Type, bool t_return_value) { - return std::make_unique( + return std::make_shared( detail::Get_Type_Info::get(), chaiscript::detail::Any(), false, @@ -89,15 +89,15 @@ namespace chaiscript } template - static std::unique_ptr get(const std::shared_ptr *obj, bool t_return_value) + static auto get(const std::shared_ptr *obj, bool t_return_value) { return get(*obj, t_return_value); } template - static std::unique_ptr get(const std::shared_ptr &obj, bool t_return_value) + static auto get(const std::shared_ptr &obj, bool t_return_value) { - return std::make_unique( + return std::make_shared( detail::Get_Type_Info::get(), chaiscript::detail::Any(obj), false, @@ -107,10 +107,10 @@ namespace chaiscript } template - static std::unique_ptr get(std::shared_ptr &&obj, bool t_return_value) + static auto get(std::shared_ptr &&obj, bool t_return_value) { auto ptr = obj.get(); - return std::make_unique( + return std::make_shared( detail::Get_Type_Info::get(), chaiscript::detail::Any(std::move(obj)), false, @@ -120,23 +120,23 @@ namespace chaiscript } template - static std::unique_ptr get(T *t, bool t_return_value) + static auto get(T *t, bool t_return_value) { return get(std::ref(*t), t_return_value); } template - static std::unique_ptr get(const T *t, bool t_return_value) + static auto get(const T *t, bool t_return_value) { return get(std::cref(*t), t_return_value); } template - static std::unique_ptr get(std::reference_wrapper obj, bool t_return_value) + static auto get(std::reference_wrapper obj, bool t_return_value) { auto p = &obj.get(); - return std::make_unique( + return std::make_shared( detail::Get_Type_Info::get(), chaiscript::detail::Any(std::move(obj)), true, @@ -146,11 +146,11 @@ namespace chaiscript } template - static std::unique_ptr get(T t, bool t_return_value) + static auto get(T t, bool t_return_value) { auto p = std::make_shared(std::move(t)); auto ptr = p.get(); - return std::make_unique( + return std::make_shared( detail::Get_Type_Info::get(), chaiscript::detail::Any(std::move(p)), false, @@ -159,9 +159,9 @@ namespace chaiscript ); } - static std::unique_ptr get() + static std::shared_ptr get() { - return std::make_unique( + return std::make_shared( Type_Info(), chaiscript::detail::Any(), false, @@ -182,10 +182,7 @@ namespace chaiscript } /// Unknown-type constructor - Boxed_Value() - : m_data(Object_Data::get()) - { - } + Boxed_Value() = default; Boxed_Value(Boxed_Value&&) = default; Boxed_Value& operator=(Boxed_Value&&) = default; @@ -226,48 +223,39 @@ namespace chaiscript return m_data->m_type_info.bare_equal(ti); } - template - struct Sentinel { - Sentinel(std::shared_ptr &ptr, Data &data) - : m_ptr(ptr), m_data(data) - { - } - - ~Sentinel() - { - // save new pointer data - m_data.get().m_data_ptr = m_ptr.get().get(); - m_data.get().m_const_data_ptr = m_ptr.get().get(); - } - - Sentinel& operator=(Sentinel&&s) { - m_ptr = std::move(s.m_ptr); - m_data = std::move(s.m_data); - } - - Sentinel(Sentinel &&s) - : m_ptr(std::move(s.m_ptr)), - m_data(std::move(s.m_data)) - { - } - - operator std::shared_ptr&() const - { - return m_ptr.get(); - } - - Sentinel &operator=(const Sentinel &) = delete; - Sentinel(Sentinel&) = delete; - - std::reference_wrapper> m_ptr; - std::reference_wrapper m_data; - }; - template - Sentinel pointer_sentinel(std::shared_ptr &ptr) const + auto pointer_sentinel(std::shared_ptr &ptr) const { - return Sentinel(ptr, *(m_data.get())); + struct Sentinel { + Sentinel(std::shared_ptr &t_ptr, Data &data) + : m_ptr(t_ptr), m_data(data) + { + } + + ~Sentinel() + { + // save new pointer data + m_data.get().m_data_ptr = m_ptr.get().get(); + m_data.get().m_const_data_ptr = m_ptr.get().get(); + } + + Sentinel& operator=(Sentinel&&s) = default; + Sentinel(Sentinel &&s) = default; + + operator std::shared_ptr&() const + { + return m_ptr.get(); + } + + Sentinel &operator=(const Sentinel &) = delete; + Sentinel(Sentinel&) = delete; + + std::reference_wrapper> m_ptr; + std::reference_wrapper m_data; + }; + + return Sentinel(ptr, *(m_data.get())); } bool is_null() const noexcept @@ -358,7 +346,7 @@ namespace chaiscript : m_data(t_data) { } - std::shared_ptr m_data; + std::shared_ptr m_data = Object_Data::get(); }; /// @brief Creates a Boxed_Value. If the object passed in is a value type, it is copied. If it is a pointer, std::shared_ptr, or std::reference_type diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 0528cb99..41bf964a 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -8,7 +8,6 @@ #define CHAISCRIPT_DISPATCHKIT_HPP_ #include -#include #include #include #include @@ -31,12 +30,16 @@ #include "proxy_constructors.hpp" #include "proxy_functions.hpp" #include "type_info.hpp" +#include "short_alloc.hpp" namespace chaiscript { class Boxed_Number; } // namespace chaiscript namespace chaiscript { + namespace parser { + class ChaiScript_Parser_Base; + } namespace dispatch { class Dynamic_Proxy_Function; class Proxy_Function_Base; @@ -45,6 +48,7 @@ struct Placeholder_Object; } // namespace chaiscript + /// \namespace chaiscript::dispatch /// \brief Classes and functions specific to the runtime dispatch side of ChaiScript. Some items may be of use to the end user. @@ -310,11 +314,6 @@ namespace chaiscript [&vals, &t_conversions](const Proxy_Function &f){ return f->call_match(vals, t_conversions); }); } - std::string annotation() const override - { - return "Multiple method dispatch function wrapper."; - } - protected: Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override { @@ -377,22 +376,56 @@ namespace chaiscript { struct Stack_Holder { - typedef std::vector> Scope; - typedef std::vector StackData; + //template + // using SmallVector = std::vector>; + + template + using SmallVector = std::vector; + + + typedef SmallVector> Scope; + typedef SmallVector StackData; + typedef SmallVector Stacks; + typedef SmallVector Call_Param_List; + typedef SmallVector Call_Params; Stack_Holder() - : call_depth(0) { - stacks.reserve(2); - stacks.emplace_back(1); - call_params.emplace_back(); - call_params.back().reserve(2); + push_stack(); + push_call_params(); } - std::vector stacks; + void push_stack_data() + { + stacks.back().emplace_back(); +// stacks.back().emplace_back(Scope(scope_allocator)); + } - std::vector> call_params; - int call_depth; + 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; + + int call_depth = 0; }; /// Main class for the dispatchkit. Handles management @@ -403,7 +436,7 @@ namespace chaiscript public: typedef std::map Type_Name_Map; typedef std::vector> Scope; - typedef std::vector StackData; + typedef Stack_Holder::StackData StackData; struct State { @@ -412,28 +445,20 @@ namespace chaiscript std::vector> m_boxed_functions; std::map m_global_objects; Type_Name_Map m_types; - std::set m_reserved_words; - - State &operator=(const State &) = default; - State() = default; - State(const State &) = default; }; - Dispatch_Engine() - : m_stack_holder(this) - { - } - - ~Dispatch_Engine() + Dispatch_Engine(chaiscript::parser::ChaiScript_Parser_Base &parser) + : m_stack_holder(this), + m_parser(parser) { } /// \brief casts an object while applying any Dynamic_Conversion available template - typename detail::Cast_Helper::Result_Type boxed_cast(const Boxed_Value &bv) const + decltype(auto) boxed_cast(const Boxed_Value &bv) const { Type_Conversions_State state(m_conversions, m_conversions.conversion_saves()); - return chaiscript::boxed_cast(bv, &state); + return(chaiscript::boxed_cast(bv, &state)); } /// Add a new conversion for upcasting to a base class @@ -445,7 +470,6 @@ namespace chaiscript /// Add a new named Proxy_Function to the system void add(const Proxy_Function &f, const std::string &name) { - validate_object_name(name); add_function(f, name); } @@ -453,7 +477,6 @@ namespace chaiscript /// is not available in the current scope it is created void add(Boxed_Value obj, const std::string &name) { - validate_object_name(name); auto &stack = get_stack_data(); for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem) @@ -473,6 +496,26 @@ namespace chaiscript add_object(name, std::move(obj)); } + /// 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) + { + 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; + })) + { + throw chaiscript::exception::name_conflict_error(t_name); + } + + stack_elem.emplace_back(t_name, std::move(obj)); + return stack_elem.back().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 @@ -488,7 +531,7 @@ namespace chaiscript throw chaiscript::exception::name_conflict_error(t_name); } - get_stack_data(t_holder).back().emplace_back(t_name, std::move(obj)); + stack_elem.emplace_back(t_name, std::move(obj)); } @@ -503,7 +546,6 @@ namespace chaiscript /// Adds a new global shared object, between all the threads void add_global_const(const Boxed_Value &obj, const std::string &name) { - validate_object_name(name); if (!obj.is_const()) { throw chaiscript::exception::global_non_const(); @@ -522,8 +564,6 @@ 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) { - validate_object_name(name); - chaiscript::detail::threading::unique_lock l(m_mutex); const auto itr = m_state.m_global_objects.find(name); @@ -540,8 +580,6 @@ namespace chaiscript /// Adds a new global (non-const) shared object, between all the threads void add_global(const Boxed_Value &obj, const std::string &name) { - validate_object_name(name); - chaiscript::detail::threading::unique_lock l(m_mutex); if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) @@ -555,8 +593,6 @@ namespace chaiscript /// 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) { - validate_object_name(name); - chaiscript::detail::threading::unique_lock l(m_mutex); const auto itr = m_state.m_global_objects.find(name); @@ -583,8 +619,8 @@ namespace chaiscript /// Adds a new scope to the stack static void new_scope(Stack_Holder &t_holder) { - get_stack_data(t_holder).emplace_back(); - t_holder.call_params.emplace_back(); + t_holder.push_stack_data(); + t_holder.push_call_params(); } /// Pops the current scope from the stack @@ -603,7 +639,7 @@ namespace chaiscript static void new_stack(Stack_Holder &t_holder) { // add a new Stack with 1 element - t_holder.stacks.emplace_back(1); + t_holder.push_stack(); } static void pop_stack(Stack_Holder &t_holder) @@ -815,7 +851,7 @@ namespace chaiscript { auto &stack = get_stack_data(); auto &scope = stack.front(); - scope = std::vector>(t_locals.begin(), t_locals.end()); + scope.assign(t_locals.begin(), t_locals.end()); } @@ -886,12 +922,6 @@ namespace chaiscript return rets; } - void add_reserved_word(const std::string &name) - { - chaiscript::detail::threading::unique_lock l(m_mutex); - - m_state.m_reserved_words.insert(name); - } const Type_Conversions &conversions() const { @@ -947,14 +977,21 @@ namespace chaiscript This_Foist fi(*this, l_params.front()); - auto func = boxed_cast>(bv); try { - return (*func)({l_params.begin() + l_num_params, l_params.end()}, l_conversions); + auto func = boxed_cast(bv); + try { + return (*func)({l_params.begin() + l_num_params, l_params.end()}, l_conversions); + } catch (const chaiscript::exception::bad_boxed_cast &) { + } catch (const chaiscript::exception::arity_error &) { + } catch (const chaiscript::exception::guard_error &) { + } + throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()}, + std::vector{boxed_cast(bv)}); } catch (const chaiscript::exception::bad_boxed_cast &) { - } catch (const chaiscript::exception::arity_error &) { - } catch (const chaiscript::exception::guard_error &) { + // unable to convert bv into a Proxy_Function_Base + throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()}, + std::vector(l_funs.begin(), l_funs.end())); } - throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()}, std::vector{func}); } else { return bv; } @@ -1058,6 +1095,7 @@ namespace chaiscript void dump_function(const std::pair &f) const { std::vector params = f.second->get_param_types(); + std::vector> typed_params; auto func(std::dynamic_pointer_cast(f.second)); @@ -1065,11 +1103,6 @@ namespace chaiscript typed_params = func->get_dynamic_param_types().types(); } - std::string annotation = f.second->annotation(); - - if (annotation.size() > 0) { - std::cout << annotation; - } dump_type(params.front()); std::cout << " " << f.first << "("; @@ -1261,6 +1294,11 @@ namespace chaiscript return m_stack_holder->stacks.back(); } + parser::ChaiScript_Parser_Base &get_parser() + { + return m_parser.get(); + } + private: const std::vector> &get_boxed_functions_int() const @@ -1395,10 +1433,6 @@ namespace chaiscript if (rt.bare_equal(boxed_type)) { - if (lt.bare_equal(boxed_pod_type)) - { - return true; - } return true; } @@ -1450,20 +1484,6 @@ namespace chaiscript } - /// Throw a reserved_word exception if the name is not allowed - void validate_object_name(const std::string &name) const - { - if (name.find("::") != std::string::npos) { - throw chaiscript::exception::illegal_name_error(name); - } - - chaiscript::detail::threading::shared_lock l(m_mutex); - - if (m_state.m_reserved_words.find(name) != m_state.m_reserved_words.end()) - { - throw chaiscript::exception::reserved_word_error(name); - } - } template static void add_keyed_value(Container &t_c, const Key &t_key, Value &&t_value) @@ -1501,7 +1521,7 @@ namespace chaiscript static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key, const size_t t_hint) { if (t_c.size() > t_hint && t_c[t_hint].first == t_key) { - return advance_copy(t_c.begin(), t_hint); + return std::next(t_c.begin(), static_cast::difference_type>(t_hint)); } else { return find_keyed_value(t_c, t_key); } @@ -1558,8 +1578,9 @@ namespace chaiscript Type_Conversions m_conversions; chaiscript::detail::threading::Thread_Storage m_stack_holder; + std::reference_wrapper m_parser; - mutable std::atomic_uint_fast32_t m_method_missing_loc; + mutable std::atomic_uint_fast32_t m_method_missing_loc = {0}; State m_state; }; @@ -1594,8 +1615,12 @@ namespace chaiscript return m_conversions.saves(); } + Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj) const { + return m_engine.get().add_get_object(t_name, std::move(obj), m_stack_holder.get()); + } + void add_object(const std::string &t_name, Boxed_Value obj) const { - m_engine.get().add_object(t_name, std::move(obj), m_stack_holder.get()); + return m_engine.get().add_object(t_name, std::move(obj), m_stack_holder.get()); } Boxed_Value get_object(const std::string &t_name, std::atomic_uint_fast32_t &t_loc) const { @@ -1612,3 +1637,4 @@ namespace chaiscript #endif + diff --git a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp index 23b20dad..41b22f2c 100644 --- a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp @@ -109,12 +109,6 @@ namespace chaiscript return {m_func}; } - std::string annotation() const override - { - return m_func->annotation(); - } - - protected: virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override { @@ -218,7 +212,7 @@ namespace chaiscript } - virtual Param_Types get_dynamic_param_types() const { + Param_Types get_dynamic_param_types() const override { auto dynamic(std::dynamic_pointer_cast(m_func)); if (dynamic) { @@ -243,11 +237,6 @@ namespace chaiscript return m_func->call_match(new_vals, t_conversions); } - std::string annotation() const override - { - return m_func->annotation(); - } - protected: Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override { diff --git a/include/chaiscript/dispatchkit/function_call.hpp b/include/chaiscript/dispatchkit/function_call.hpp index 990be95a..3efb7031 100644 --- a/include/chaiscript/dispatchkit/function_call.hpp +++ b/include/chaiscript/dispatchkit/function_call.hpp @@ -81,9 +81,7 @@ namespace chaiscript template struct Cast_Helper &> { - typedef std::function Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) + static std::function cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { if (ob.get_type_info().bare_equal(user_type())) { @@ -98,9 +96,7 @@ namespace chaiscript template struct Cast_Helper > { - typedef std::function Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) + static std::function cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { if (ob.get_type_info().bare_equal(user_type())) { @@ -115,9 +111,7 @@ namespace chaiscript template struct Cast_Helper > { - typedef std::function Result_Type; - - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) + static std::function cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { if (ob.get_type_info().bare_equal(user_type())) { diff --git a/include/chaiscript/dispatchkit/handle_return.hpp b/include/chaiscript/dispatchkit/handle_return.hpp index ca353ecf..2b770ac5 100644 --- a/include/chaiscript/dispatchkit/handle_return.hpp +++ b/include/chaiscript/dispatchkit/handle_return.hpp @@ -100,6 +100,24 @@ namespace chaiscript } }; + template + struct Handle_Return + { + static Boxed_Value handle(Ret *p) + { + return Boxed_Value(p, true); + } + }; + + template + struct Handle_Return + { + static Boxed_Value handle(const Ret *p) + { + return Boxed_Value(p, true); + } + }; + template struct Handle_Return { @@ -146,6 +164,14 @@ namespace chaiscript } }; + template + struct Handle_Return + { + static Boxed_Value handle(const Ret &r) + { + return Boxed_Value(std::cref(r)); + } + }; template struct Handle_Return @@ -154,11 +180,6 @@ namespace chaiscript { return Boxed_Value(std::ref(r)); } - - static Boxed_Value handle(const Ret &r) - { - return Boxed_Value(std::cref(r)); - } }; template<> diff --git a/include/chaiscript/dispatchkit/operators.hpp b/include/chaiscript/dispatchkit/operators.hpp index 8dc54ba0..4ebaec55 100644 --- a/include/chaiscript/dispatchkit/operators.hpp +++ b/include/chaiscript/dispatchkit/operators.hpp @@ -16,414 +16,202 @@ namespace chaiscript { namespace operators { - namespace detail - { - /// \todo make this return a decltype once we drop gcc 4.6 - template - auto assign(L l, R r) -> L& - { - return (l = r); - } - - template - auto assign_bitwise_and(L l, R r) -> decltype((l &= r)) - { - return (l &= r); - } - - template - auto assign_xor(L l, R r) -> decltype((l^=r)) - { - return (l ^= r); - } - - template - auto assign_bitwise_or(L l, R r) -> decltype((l |= r)) - { - return (l |= r); - } - - template - auto assign_difference(L l, R r) -> decltype(( l -= r)) - { - return (l -= r); - } - - template - auto assign_left_shift(L l, R r) -> decltype(( l <<= r)) - { - return (l <<= r); - } - - template - auto assign_product(L l, R r) -> decltype(( l *= r )) - { - return (l *= r); - } - - template - auto assign_quotient(L l, R r) -> decltype(( l /= r )) - { - return (l /= r); - } - - template - auto assign_remainder(L l, R r) -> decltype(( l %= r )) - { - return (l %= r); - } - - template - auto assign_right_shift(L l, R r) -> decltype(( l >>= r)) - { - return (l >>= r); - } - - /// \todo make this return a decltype once we drop gcc 4.6 - template - auto assign_sum(L l, R r) -> L& - { - return (l += r); - } - - template - auto prefix_decrement(L l) -> decltype(( --l )) - { - return (--l); - } - - template - auto prefix_increment(L l) -> decltype(( ++l )) - { - return (++l); - } - - template - auto equal(L l, R r) -> decltype(( l == r )) - { - return (l == r); - } - - template - auto greater_than(L l, R r) -> decltype(( l > r )) - { - return (l > r); - } - - template - auto greater_than_equal(L l, R r) -> decltype(( l >= r )) - { - return (l >= r); - } - - template - auto less_than(L l, R r) -> decltype(( l < r )) - { - return (l < r); - } - - template - auto less_than_equal(L l, R r) -> decltype(( l <= r )) - { - return (l <= r); - } - - template - auto logical_compliment(L l) -> decltype(( !l )) - { - return (!l); - } - - template - auto not_equal(L l, R r) -> decltype(( l != r )) - { - return (l != r); - } - - template - auto addition(L l, R r) -> decltype(( l + r )) - { - return (l + r); - } - - template - auto unary_plus(L l) -> decltype(( +l )) - { - return (+l); - } - - template - auto subtraction(L l, R r) -> decltype(( l - r )) - { - return (l - r); - } - - template - auto unary_minus(L l) -> decltype(( -l )) - { -#ifdef CHAISCRIPT_MSVC -#pragma warning(push) -#pragma warning(disable : 4146) - return (-l); -#pragma warning(pop) -#else - return (-l); -#endif - } - - template - auto bitwise_and(L l, R r) -> decltype(( l & r )) - { - return (l & r); - } - - template - auto bitwise_compliment(L l) -> decltype(( ~l )) - { - return (~l); - } - - template - auto bitwise_xor(L l, R r) -> decltype(( l ^ r )) - { - return (l ^ r); - } - - template - auto bitwise_or(L l, R r) -> decltype(( l | r )) - { - return (l | r); - } - - template - auto division(L l, R r) -> decltype(( l / r )) - { - return (l / r); - } - - template - auto left_shift(L l, R r) -> decltype(( l << r )) - { - return l << r; - } - - template - auto multiplication(L l, R r) -> decltype(( l * r )) - { - return l * r; - } - - template - auto remainder(L l, R r) -> decltype(( l % r )) - { - return (l % r); - } - - template - auto right_shift(L l, R r) -> decltype(( l >> r )) - { - return (l >> r); - } - } - - - template void assign(Module& m) { - m.add(chaiscript::fun(&detail::assign), "="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs = rhs;}), "="); } template void assign_bitwise_and(Module& m) { - m.add(chaiscript::fun(&detail::assign_bitwise_and), "&="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs &= rhs;}), "&="); } template void assign_xor(Module& m) { - m.add(chaiscript::fun(&detail::assign_xor), "^="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs ^= rhs;}), "^="); } template void assign_bitwise_or(Module& m) { - m.add(chaiscript::fun(&detail::assign_bitwise_or), "|="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs |= rhs;}), "|="); } template void assign_difference(Module& m) { - m.add(chaiscript::fun(&detail::assign_difference), "-="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs -= rhs;}), "-="); } template void assign_left_shift(Module& m) { - m.add(chaiscript::fun(&detail::assign_left_shift), "<<="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs <<= rhs;}), "<<="); } template void assign_product(Module& m) { - m.add(chaiscript::fun(&detail::assign_product), "*="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs <<= rhs;}), "*="); } template void assign_quotient(Module& m) { - m.add(chaiscript::fun(&detail::assign_quotient), "/="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs /= rhs;}), "/="); } template void assign_remainder(Module& m) { - m.add(chaiscript::fun(&detail::assign_remainder), "%="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs %= rhs;}), "%="); } template void assign_right_shift(Module& m) { - m.add(chaiscript::fun(&detail::assign_right_shift), ">>="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs >>= rhs;}), ">>="); } template void assign_sum(Module& m) { - m.add(chaiscript::fun(&detail::assign_sum), "+="); + m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs += rhs;}), "+="); } template void prefix_decrement(Module& m) { - m.add(chaiscript::fun(&detail::prefix_decrement), "--"); + m.add(chaiscript::fun([](T &lhs)->T&{return --lhs;}), "--"); } template void prefix_increment(Module& m) { - m.add(chaiscript::fun(&detail::prefix_increment), "++"); + m.add(chaiscript::fun([](T &lhs)->T&{return ++lhs;}), "++"); } template void equal(Module& m) { - m.add(chaiscript::fun(&detail::equal), "=="); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs==rhs;}), "=="); } template void greater_than(Module& m) { - m.add(chaiscript::fun(&detail::greater_than), ">"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs>rhs;}), ">"); } template void greater_than_equal(Module& m) { - m.add(chaiscript::fun(&detail::greater_than_equal), ">="); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs>=rhs;}), ">="); } template void less_than(Module& m) { - m.add(chaiscript::fun(&detail::less_than), "<"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs void less_than_equal(Module& m) { - m.add(chaiscript::fun(&detail::less_than_equal), "<="); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs<=rhs;}), "<="); } template void logical_compliment(Module& m) { - m.add(chaiscript::fun(&detail::logical_compliment), "!"); + m.add(chaiscript::fun([](const T &lhs){return !lhs;}), "!"); } template void not_equal(Module& m) { - m.add(chaiscript::fun(&detail::not_equal), "!="); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs!=rhs;}), "!="); } template void addition(Module& m) { - m.add(chaiscript::fun(&detail::addition), "+"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs+rhs;}), "+"); } template void unary_plus(Module& m) { - m.add(chaiscript::fun(&detail::unary_plus), "+"); + m.add(chaiscript::fun([](const T &lhs){return +lhs;}), "+"); } template void subtraction(Module& m) { - m.add(chaiscript::fun(&detail::subtraction), "-"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs-rhs;}), "-"); } template void unary_minus(Module& m) { - m.add(chaiscript::fun(&detail::unary_minus), "-"); + m.add(chaiscript::fun([](const T &lhs){return -lhs;}), "-"); } template void bitwise_and(Module& m) { - m.add(chaiscript::fun(&detail::bitwise_and), "&"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs&rhs;}), "&"); } template void bitwise_compliment(Module& m) { - m.add(chaiscript::fun(&detail::bitwise_compliment), "~"); + m.add(chaiscript::fun([](const T &lhs){return ~lhs;}), "~"); } template void bitwise_xor(Module& m) { - m.add(chaiscript::fun(&detail::bitwise_xor), "^"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs^rhs;}), "^"); } template void bitwise_or(Module& m) { - m.add(chaiscript::fun(&detail::bitwise_or), "|"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs|rhs;}), "|"); } template void division(Module& m) { - m.add(chaiscript::fun(&detail::division), "/"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs/rhs;}), "/"); } template void left_shift(Module& m) { - m.add(chaiscript::fun(&detail::left_shift), "<<"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs< void multiplication(Module& m) { - m.add(chaiscript::fun(&detail::multiplication), "*"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs*rhs;}), "*"); } template void remainder(Module& m) { - m.add(chaiscript::fun(&detail::remainder), "%"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs%rhs;}), "%"); } template void right_shift(Module& m) { - m.add(chaiscript::fun(&detail::right_shift), ">>"); + m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs>>rhs;}), ">>"); } } } diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index 1e27db34..65267c86 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -200,20 +200,15 @@ namespace chaiscript //! to the passed in values bool filter(const std::vector &vals, const Type_Conversions_State &t_conversions) const { + assert(m_arity == -1 || (m_arity > 0 && static_cast(vals.size()) == m_arity)); + if (m_arity < 0) { return true; - } else if (static_cast(m_arity) == vals.size()) { - if (m_arity == 0) - { - return true; - } else if (m_arity > 1) { - return compare_type_to_param(m_types[1], vals[0], t_conversions) && compare_type_to_param(m_types[2], vals[1], t_conversions); - } else { - return compare_type_to_param(m_types[1], vals[0], t_conversions); - } + } else if (m_arity > 1) { + return compare_type_to_param(m_types[1], vals[0], t_conversions) && compare_type_to_param(m_types[2], vals[1], t_conversions); } else { - return false; + return compare_type_to_param(m_types[1], vals[0], t_conversions); } } @@ -223,8 +218,6 @@ namespace chaiscript return m_arity; } - virtual std::string annotation() const = 0; - static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv, const Type_Conversions_State &t_conversions) { if (ti.is_undef() @@ -332,11 +325,10 @@ namespace chaiscript int t_arity=-1, AST_NodePtr t_parsenode = AST_NodePtr(), Param_Types t_param_types = Param_Types(), - std::string t_description = "", Proxy_Function t_guard = Proxy_Function()) : Proxy_Function_Base(build_param_type_list(t_param_types), t_arity), m_param_types(std::move(t_param_types)), - m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode)), m_description(std::move(t_description)) + m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode)) { } @@ -369,12 +361,7 @@ namespace chaiscript return m_parsenode; } - virtual std::string annotation() const override - { - return m_description; - } - - virtual Param_Types get_dynamic_param_types() const { + Param_Types get_dynamic_param_types() const override { return m_param_types; } @@ -418,7 +405,6 @@ namespace chaiscript Param_Types m_param_types; Proxy_Function m_guard; AST_NodePtr m_parsenode; - std::string m_description; }; @@ -432,13 +418,11 @@ namespace chaiscript int t_arity=-1, AST_NodePtr t_parsenode = AST_NodePtr(), Param_Types t_param_types = Param_Types(), - std::string t_description = "", Proxy_Function t_guard = Proxy_Function()) : Dynamic_Proxy_Function( t_arity, std::move(t_parsenode), std::move(t_param_types), - std::move(t_description), std::move(t_guard) ), m_f(std::move(t_f)) @@ -537,10 +521,6 @@ namespace chaiscript return args; } - virtual std::string annotation() const override - { - return "Bound: " + m_f->annotation(); - } protected: static std::vector build_param_type_info(const Const_Proxy_Function &t_f, @@ -585,11 +565,6 @@ namespace chaiscript { } - std::string annotation() const override - { - return ""; - } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override { return static_cast(vals.size()) == get_arity() @@ -626,8 +601,7 @@ namespace chaiscript protected: Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override { - typedef typename detail::Function_Signature::Return_Type Return_Type; - return detail::Do_Call::template go(m_f, params, t_conversions); + return detail::call_func(detail::Function_Signature(), m_f, params, t_conversions); } private: @@ -679,7 +653,7 @@ namespace chaiscript protected: Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override { - return detail::Do_Call::result_type>::template go(m_f.get(), params, t_conversions); + return detail::call_func(detail::Function_Signature(), m_f.get(), params, t_conversions); } @@ -724,11 +698,6 @@ namespace chaiscript return vals[0].get_type_info().bare_equal(user_type()); } - std::string annotation() const override - { - return ""; - } - protected: Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override { @@ -759,13 +728,13 @@ namespace chaiscript template auto do_call_impl(Class *o) const -> std::enable_if_t::value, Boxed_Value> { - return detail::Handle_Return::type>::handle(o->*m_attr); + return detail::Handle_Return::type>::handle(o->*m_attr); } template auto do_call_impl(const Class *o) const -> std::enable_if_t::value, Boxed_Value> { - return detail::Handle_Return::type>::handle(o->*m_attr); + return detail::Handle_Return::type>::type>::handle(o->*m_attr); } @@ -881,7 +850,8 @@ namespace chaiscript plist.begin(), std::back_inserter(newplist), [](const Type_Info &ti, const Boxed_Value ¶m) -> Boxed_Value { - if (ti.is_arithmetic() && param.get_type_info().is_arithmetic()) { + if (ti.is_arithmetic() && param.get_type_info().is_arithmetic() + && param.get_type_info() != ti) { return Boxed_Number(param).get_as(ti).bv; } else { return param; @@ -889,8 +859,6 @@ namespace chaiscript } ); - - try { return (*(matching_func->second))(newplist, t_conversions); } catch (const exception::bad_boxed_cast &) { @@ -918,14 +886,8 @@ namespace chaiscript std::vector> ordered_funcs; ordered_funcs.reserve(funcs.size()); -#ifdef CHAISCRIPT_HAS_MAGIC_STATICS - static auto boxed_type = user_type(); - static auto dynamic_type = user_type(); -#else - auto boxed_type = user_type(); - auto dynamic_type = user_type(); -#endif - + const constexpr auto boxed_type = user_type(); + const constexpr auto dynamic_type = user_type(); for (const auto &func : funcs) { @@ -956,7 +918,7 @@ namespace chaiscript for (const auto &func : ordered_funcs ) { try { - if (func.first == i && func.second->filter(plist, t_conversions)) + if (func.first == i && (i == 0 || func.second->filter(plist, t_conversions))) { return (*(func.second))(plist, t_conversions); } diff --git a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp index 69b88a82..8258521d 100644 --- a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp @@ -99,17 +99,31 @@ namespace chaiscript } - /** - * Used by Proxy_Function_Impl to perform typesafe execution of a function. - * The function attempts to unbox each parameter to the expected type. - * if any unboxing fails the execution of the function fails and - * the bad_boxed_cast is passed up to the caller. - */ + /// Used by Proxy_Function_Impl to perform typesafe execution of a function. + /// The function attempts to unbox each parameter to the expected type. + /// if any unboxing fails the execution of the function fails and + /// the bad_boxed_cast is passed up to the caller. template - Ret call_func(const chaiscript::dispatch::detail::Function_Signature &sig, const Callable &f, + Boxed_Value call_func(const chaiscript::dispatch::detail::Function_Signature &sig, const Callable &f, const std::vector ¶ms, const Type_Conversions_State &t_conversions) { - return call_func(sig, std::index_sequence_for{}, f, params, t_conversions); + return Handle_Return::handle(call_func(sig, std::index_sequence_for{}, f, params, t_conversions)); + } + + template + Boxed_Value call_func(const chaiscript::dispatch::detail::Function_Signature &sig, const Callable &f, + const std::vector ¶ms, const Type_Conversions_State &t_conversions) + { + call_func(sig, std::index_sequence_for{}, f, params, t_conversions); +#ifdef CHAISCRIPT_MSVC +#pragma warning(push) +#pragma warning(disable : 4702) +#endif + // MSVC is reporting that this is unreachable code - and it's wrong. + return Handle_Return::handle(); +#ifdef CHAISCRIPT_MSVC +#pragma warning(pop) +#endif } } @@ -118,34 +132,4 @@ namespace chaiscript } -namespace chaiscript -{ - namespace dispatch - { - namespace detail - { - template - struct Do_Call - { - template - static Boxed_Value go(const Callable &fun, const std::vector ¶ms, const Type_Conversions_State &t_conversions) - { - return Handle_Return::handle(call_func(Function_Signature(), fun, params, t_conversions)); - } - }; - - template<> - struct Do_Call - { - template - static Boxed_Value go(const Callable &fun, const std::vector ¶ms, const Type_Conversions_State &t_conversions) - { - call_func(Function_Signature(), fun, params, t_conversions); - return Handle_Return::handle(); - } - }; - } - } -} - #endif diff --git a/include/chaiscript/dispatchkit/short_alloc.hpp b/include/chaiscript/dispatchkit/short_alloc.hpp new file mode 100644 index 00000000..3cb04223 --- /dev/null +++ b/include/chaiscript/dispatchkit/short_alloc.hpp @@ -0,0 +1,157 @@ +#ifndef SHORT_ALLOC_H +#define SHORT_ALLOC_H + +// The MIT License (MIT) +// +// Copyright (c) 2015 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +template +class arena +{ + alignas(alignment) char buf_[N]; + char* ptr_; + +public: + ~arena() {ptr_ = nullptr;} + arena() noexcept : ptr_(buf_) {} + arena(const arena&) = delete; + arena& operator=(const arena&) = delete; + + template char* allocate(std::size_t n); + void deallocate(char* p, std::size_t n) noexcept; + + static constexpr std::size_t size() noexcept {return N;} + std::size_t used() const noexcept {return static_cast(ptr_ - buf_);} + void reset() noexcept {ptr_ = buf_;} + +private: + static + std::size_t + align_up(std::size_t n) noexcept + {return (n + (alignment-1)) & ~(alignment-1);} + + bool + pointer_in_buffer(char* p) noexcept + {return buf_ <= p && p <= buf_ + N;} +}; + +template +template +char* +arena::allocate(std::size_t n) +{ + static_assert(ReqAlign <= alignment, "alignment is too small for this arena"); + assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); + auto const aligned_n = align_up(n); + if (static_cast(buf_ + N - ptr_) >= aligned_n) + { + char* r = ptr_; + ptr_ += aligned_n; + return r; + } + + static_assert(alignment <= alignof(std::max_align_t), "you've chosen an " + "alignment that is larger than alignof(std::max_align_t), and " + "cannot be guaranteed by normal operator new"); + return static_cast(::operator new(n)); +} + +template +void +arena::deallocate(char* p, std::size_t n) noexcept +{ + assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); + if (pointer_in_buffer(p)) + { + n = align_up(n); + if (p + n == ptr_) + ptr_ = p; + } + else + ::operator delete(p); +} + +template +class short_alloc +{ +public: + using value_type = T; + static auto constexpr alignment = Align; + static auto constexpr size = N; + using arena_type = arena; + +private: + arena_type& a_; + +public: + short_alloc(const short_alloc&) = default; + short_alloc& operator=(const short_alloc&) = delete; + + short_alloc(arena_type& a) noexcept : a_(a) + { + static_assert(size % alignment == 0, + "size N needs to be a multiple of alignment Align"); + } + template + short_alloc(const short_alloc& a) noexcept + : a_(a.a_) {} + + template struct rebind {using other = short_alloc<_Up, N, alignment>;}; + + T* allocate(std::size_t n) + { + return reinterpret_cast(a_.template allocate(n*sizeof(T))); + } + void deallocate(T* p, std::size_t n) noexcept + { + a_.deallocate(reinterpret_cast(p), n*sizeof(T)); + } + + template + friend + bool + operator==(const short_alloc& x, const short_alloc& y) noexcept; + + template friend class short_alloc; +}; + +template +inline +bool +operator==(const short_alloc& x, const short_alloc& y) noexcept +{ + return N == M && A1 == A2 && &x.a_ == &y.a_; +} + +template +inline +bool +operator!=(const short_alloc& x, const short_alloc& y) noexcept +{ + return !(x == y); +} + +#endif // SHORT_ALLOC_HPP + diff --git a/include/chaiscript/dispatchkit/type_info.hpp b/include/chaiscript/dispatchkit/type_info.hpp index 59235f5e..592271a8 100644 --- a/include/chaiscript/dispatchkit/type_info.hpp +++ b/include/chaiscript/dispatchkit/type_info.hpp @@ -29,8 +29,8 @@ namespace chaiscript class Type_Info { public: - constexpr Type_Info(bool t_is_const, bool t_is_reference, bool t_is_pointer, bool t_is_void, - bool t_is_arithmetic, const std::type_info *t_ti, const std::type_info *t_bare_ti) + constexpr Type_Info(const bool t_is_const, const bool t_is_reference, const bool t_is_pointer, const bool t_is_void, + const bool t_is_arithmetic, const std::type_info *t_ti, const std::type_info *t_bare_ti) : m_type_info(t_ti), m_bare_type_info(t_bare_ti), m_flags((static_cast(t_is_const) << is_const_flag) + (static_cast(t_is_reference) << is_reference_flag) @@ -47,6 +47,16 @@ namespace chaiscript return m_type_info < ti.m_type_info; } + constexpr bool operator!=(const Type_Info &ti) const noexcept + { + return !(operator==(ti)); + } + + constexpr bool operator!=(const std::type_info &ti) const noexcept + { + return !(operator==(ti)); + } + constexpr bool operator==(const Type_Info &ti) const noexcept { return ti.m_type_info == m_type_info @@ -121,9 +131,7 @@ namespace chaiscript template struct Get_Type_Info { - typedef T type; - - constexpr static Type_Info get() + static constexpr Type_Info get() { return Type_Info(std::is_const::type>::type>::value, std::is_reference::value, std::is_pointer::value, @@ -138,9 +146,9 @@ namespace chaiscript template struct Get_Type_Info > { - typedef T type; +// typedef T type; - constexpr static Type_Info get() + static constexpr Type_Info get() { return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, std::is_void::value, @@ -158,9 +166,7 @@ namespace chaiscript template struct Get_Type_Info &> { - typedef T type; - - constexpr static Type_Info get() + static constexpr Type_Info get() { return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, std::is_void::value, @@ -173,9 +179,7 @@ namespace chaiscript template struct Get_Type_Info > { - typedef T type; - - constexpr static Type_Info get() + static constexpr Type_Info get() { return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, std::is_void::value, @@ -188,9 +192,7 @@ namespace chaiscript template struct Get_Type_Info &> { - typedef T type; - - constexpr static Type_Info get() + static constexpr Type_Info get() { return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, std::is_void::value, @@ -212,7 +214,7 @@ namespace chaiscript /// chaiscript::Type_Info ti = chaiscript::user_type(i); /// \endcode template - constexpr Type_Info user_type(const T &/*t*/) + constexpr Type_Info user_type(const T &/*t*/) { return detail::Get_Type_Info::get(); } @@ -227,7 +229,7 @@ namespace chaiscript /// chaiscript::Type_Info ti = chaiscript::user_type(); /// \endcode template - constexpr Type_Info user_type() + constexpr Type_Info user_type() { return detail::Get_Type_Info::get(); } diff --git a/include/chaiscript/language/chaiscript_algebraic.hpp b/include/chaiscript/language/chaiscript_algebraic.hpp index 52441e8f..0338a029 100644 --- a/include/chaiscript/language/chaiscript_algebraic.hpp +++ b/include/chaiscript/language/chaiscript_algebraic.hpp @@ -13,7 +13,7 @@ namespace chaiscript { struct Operators { - enum Opers + enum class Opers { boolean_flag, equals, less_than, greater_than, less_than_equal, greater_than_equal, not_equal, @@ -31,7 +31,7 @@ namespace chaiscript }; static const char *to_string(Opers t_oper) { - const char *opers[] = { + static const char *opers[] = { "", "==", "<", ">", "<=", ">=", "!=", "", @@ -46,80 +46,80 @@ namespace chaiscript "+", "/", "*", "-", "+", "-", "" }; - return opers[t_oper]; + return opers[static_cast(t_oper)]; } static Opers to_operator(const std::string &t_str, bool t_is_unary = false) { if (t_str == "==") { - return equals; + return Opers::equals; } else if (t_str == "<") { - return less_than; + return Opers::less_than; } else if (t_str == ">") { - return greater_than; + return Opers::greater_than; } else if (t_str == "<=") { - return less_than_equal; + return Opers::less_than_equal; } else if (t_str == ">=") { - return greater_than_equal; + return Opers::greater_than_equal; } else if (t_str == "!=") { - return not_equal; + return Opers::not_equal; } else if (t_str == "=") { - return assign; + return Opers::assign; } else if (t_str == "++") { - return pre_increment; + return Opers::pre_increment; } else if (t_str == "--") { - return pre_decrement; + return Opers::pre_decrement; } else if (t_str == "*=") { - return assign_product; + return Opers::assign_product; } else if (t_str == "+=") { - return assign_sum; + return Opers::assign_sum; } else if (t_str == "-=") { - return assign_difference; + return Opers::assign_difference; } else if (t_str == "&=") { - return assign_bitwise_and; + return Opers::assign_bitwise_and; } else if (t_str == "|=") { - return assign_bitwise_or; + return Opers::assign_bitwise_or; } else if (t_str == "<<=") { - return assign_shift_left; + return Opers::assign_shift_left; } else if (t_str == ">>=") { - return assign_shift_right; + return Opers::assign_shift_right; } else if (t_str == "%=") { - return assign_remainder; + return Opers::assign_remainder; } else if (t_str == "^=") { - return assign_bitwise_xor; + return Opers::assign_bitwise_xor; } else if (t_str == "<<") { - return shift_left; + return Opers::shift_left; } else if (t_str == ">>") { - return shift_right; + return Opers::shift_right; } else if (t_str == "%") { - return remainder; + return Opers::remainder; } else if (t_str == "&") { - return bitwise_and; + return Opers::bitwise_and; } else if (t_str == "|") { - return bitwise_or; + return Opers::bitwise_or; } else if (t_str == "^") { - return bitwise_xor; + return Opers::bitwise_xor; } else if (t_str == "~") { - return bitwise_complement; + return Opers::bitwise_complement; } else if (t_str == "+") { if (t_is_unary) { - return unary_plus; + return Opers::unary_plus; } else { - return sum; + return Opers::sum; } } else if (t_str == "-") { if (t_is_unary) { - return unary_minus; + return Opers::unary_minus; } else { - return difference; + return Opers::difference; } } else if (t_str == "/") { - return quotient; + return Opers::quotient; } else if (t_str == "*") { - return product; + return Opers::product; } else { - return invalid; + return Opers::invalid; } } diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 27ac85f3..7a70892f 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -26,34 +26,60 @@ struct AST_Node; namespace chaiscript { + struct Name_Validator { + static bool is_reserved_word(const std::string &name) + { + static const std::set m_reserved_words + = {"def", "fun", "while", "for", "if", "else", "&&", "||", ",", "auto", + "return", "break", "true", "false", "class", "attr", "var", "global", "GLOBAL", "_", + "__LINE__", "__FILE__", "__FUNC__", "__CLASS__"}; + return m_reserved_words.count(name) > 0; + } + + static bool valid_object_name(const std::string &name) + { + return name.find("::") == std::string::npos && !is_reserved_word(name); + } + + static void validate_object_name(const std::string &name) + { + if (is_reserved_word(name)) { + throw exception::reserved_word_error(name); + } + + if (name.find("::") != std::string::npos) { + throw exception::illegal_name_error(name); + } + } + }; /// Signature of module entry point that all binary loadable modules must implement. typedef ModulePtr (*Create_Module_Func)(); /// Types of AST nodes available to the parser and eval - class AST_Node_Type { - public: - enum Type { Error, Int, Float, Id, Char, Str, Eol, Fun_Call, Arg_List, Variable, Equation, Var_Decl, - Comparison, Addition, Subtraction, Multiplication, Division, Modulus, Array_Call, Dot_Access, Quoted_String, Single_Quoted_String, - Lambda, Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range, - Inline_Range, Annotation, Try, Catch, Finally, Method, Attr_Decl, Shift, Equality, Bitwise_And, Bitwise_Xor, Bitwise_Or, - Logical_And, Logical_Or, Reference, Switch, Case, Default, Ternary_Cond, Noop, Class, Binary, Arg, Global_Decl - }; + enum class AST_Node_Type { Id, Fun_Call, Unused_Return_Fun_Call, Arg_List, Equation, Var_Decl, + Array_Call, Dot_Access, + Lambda, Block, Scopeless_Block, Def, While, If, For, Ranged_For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range, + Inline_Range, Try, Catch, Finally, Method, Attr_Decl, + Logical_And, Logical_Or, Reference, Switch, Case, Default, Ternary_Cond, Noop, Class, Binary, Arg, Global_Decl, Constant, Compiled }; + enum class Operator_Precidence { Ternary_Cond, Logical_Or, + Logical_And, Bitwise_Or, Bitwise_Xor, Bitwise_And, + Equality, Comparison, Shift, Addition, Multiplication, Prefix }; + namespace { - /// Helper lookup to get the name of each node type - const char *ast_node_type_to_string(int ast_node_type) { - const char *ast_node_types[] = { "Internal Parser Error", "Int", "Float", "Id", "Char", "Str", "Eol", "Fun_Call", "Arg_List", "Variable", "Equation", "Var_Decl", - "Comparison", "Addition", "Subtraction", "Multiplication", "Division", "Modulus", "Array_Call", "Dot_Access", "Quoted_String", "Single_Quoted_String", - "Lambda", "Block", "Def", "While", "If", "For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", - "Inline_Range", "Annotation", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Shift", "Equality", "Bitwise_And", "Bitwise_Xor", "Bitwise_Or", - "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Ternary Condition", "Noop", "Class", "Binary", "Arg"}; + const char *ast_node_type_to_string(AST_Node_Type ast_node_type) { + static const char * const ast_node_types[] = { "Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", + "Array_Call", "Dot_Access", + "Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", + "Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl", + "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Ternary Condition", "Noop", "Class", "Binary", "Arg", "Global_Decl", "Constant", "Compiled"}; - return ast_node_types[ast_node_type]; + return ast_node_types[static_cast(ast_node_type)]; } } @@ -101,6 +127,36 @@ namespace chaiscript /// \brief Classes which may be thrown during error cases when ChaiScript is executing. namespace exception { + /// \brief Thrown if an error occurs while attempting to load a binary module + struct load_module_error : std::runtime_error + { + load_module_error(const std::string &t_reason) noexcept + : std::runtime_error(t_reason) + { + } + + load_module_error(const std::string &t_name, const std::vector &t_errors) + : std::runtime_error(format_error(t_name, t_errors)) + { + } + + load_module_error(const load_module_error &) = default; + virtual ~load_module_error() noexcept = default; + + static std::string format_error(const std::string &t_name, const std::vector &t_errors) + { + std::stringstream ss; + ss << "Error loading module '" << t_name << "'\n" + << " The following locations were searched:\n"; + + for (const auto &err : t_errors) { + ss << " " << err.what() << "\n"; + } + + return ss.str(); + } + }; + /// Errors generated during parsing or evaluation struct eval_error : std::runtime_error { @@ -161,12 +217,12 @@ namespace chaiscript return ss.str(); } - virtual ~eval_error() noexcept {} + virtual ~eval_error() noexcept = default; private: template - static int id(const T& t) + static AST_Node_Type id(const T& t) { return t->identifier; } @@ -434,11 +490,9 @@ namespace chaiscript /// \brief Struct that doubles as both a parser ast_node and an AST node. struct AST_Node : std::enable_shared_from_this { public: - const int identifier; //< \todo shouldn't this be a strongly typed enum value? + const AST_Node_Type identifier; const std::string text; Parse_Location location; - std::vector children; - AST_NodePtr annotation; const std::string &filename() const { return *location.filename; @@ -452,19 +506,22 @@ namespace chaiscript return location.end; } - virtual std::string pretty_print() const + std::string pretty_print() const { std::ostringstream oss; oss << text; - for (auto & elem : this->children) { - oss << elem->pretty_print(); + for (auto & elem : this->get_children()) { + oss << elem->pretty_print() << ' '; } return oss.str(); } + virtual std::vector get_children() const = 0; + virtual Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const = 0; + /// Prints the contents of an AST node, including its children, recursively std::string to_string(const std::string &t_prepend = "") const { @@ -473,25 +530,16 @@ namespace chaiscript oss << t_prepend << "(" << ast_node_type_to_string(this->identifier) << ") " << this->text << " : " << this->location.start.line << ", " << this->location.start.column << '\n'; - for (auto & elem : this->children) { + for (auto & elem : this->get_children()) { oss << elem->to_string(t_prepend + " "); } return oss.str(); } - Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const - { - try { - return eval_internal(t_e); - } catch (exception::eval_error &ee) { - ee.call_stack.push_back(shared_from_this()); - throw; - } - } - static bool get_bool_condition(const Boxed_Value &t_bv) { + static bool get_bool_condition(const Boxed_Value &t_bv, const chaiscript::detail::Dispatch_State &t_ss) { try { - return boxed_cast(t_bv); + return t_ss->boxed_cast(t_bv); } catch (const exception::bad_boxed_cast &) { throw exception::eval_error("Condition not boolean"); @@ -499,33 +547,47 @@ namespace chaiscript } - void replace_child(const AST_NodePtr &t_child, const AST_NodePtr &t_new_child) - { - std::replace(children.begin(), children.end(), t_child, t_new_child); - } - - virtual ~AST_Node() {} - - protected: - AST_Node(std::string t_ast_node_text, int t_id, Parse_Location t_loc, - std::vector t_children = std::vector()) : - identifier(t_id), text(std::move(t_ast_node_text)), - location(std::move(t_loc)), - children(std::move(t_children)) - { - } - - virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const - { - throw std::runtime_error("Undispatched ast_node (internal error)"); - } - - private: - // Copy and assignment explicitly unimplemented + virtual ~AST_Node() = default; + AST_Node(AST_Node &&) = default; + AST_Node &operator=(AST_Node &&) = default; AST_Node(const AST_Node &) = delete; AST_Node& operator=(const AST_Node &) = delete; + + + protected: + AST_Node(std::string t_ast_node_text, AST_Node_Type t_id, Parse_Location t_loc) + : identifier(t_id), text(std::move(t_ast_node_text)), + location(std::move(t_loc)) + { + } + + }; + namespace parser { + class ChaiScript_Parser_Base + { + public: + virtual AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) = 0; + virtual void debug_print(AST_NodePtr t, std::string prepend = "") const = 0; + virtual void *get_tracer_ptr() = 0; + virtual ~ChaiScript_Parser_Base() = default; + ChaiScript_Parser_Base() = default; + ChaiScript_Parser_Base(ChaiScript_Parser_Base &&) = default; + ChaiScript_Parser_Base &operator=(ChaiScript_Parser_Base &&) = delete; + ChaiScript_Parser_Base &operator=(const ChaiScript_Parser_Base &&) = delete; + + template + T &get_tracer() + { + // to do type check this somehow? + return *static_cast(get_tracer_ptr()); + } + + protected: + ChaiScript_Parser_Base(const ChaiScript_Parser_Base &) = default; + }; + } namespace eval { @@ -554,77 +616,83 @@ namespace chaiscript /// Creates a new scope then pops it on destruction struct Scope_Push_Pop { + Scope_Push_Pop(Scope_Push_Pop &&) = default; + Scope_Push_Pop& operator=(Scope_Push_Pop &&) = default; Scope_Push_Pop(const Scope_Push_Pop &) = delete; Scope_Push_Pop& operator=(const Scope_Push_Pop &) = delete; Scope_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) : m_ds(t_ds) { - m_ds.get()->new_scope(m_ds.get().stack_holder()); + m_ds->new_scope(m_ds.stack_holder()); } ~Scope_Push_Pop() { - m_ds.get()->pop_scope(m_ds.get().stack_holder()); + m_ds->pop_scope(m_ds.stack_holder()); } private: - std::reference_wrapper m_ds; + const chaiscript::detail::Dispatch_State &m_ds; }; /// Creates a new function call and pops it on destruction struct Function_Push_Pop { + Function_Push_Pop(Function_Push_Pop &&) = default; + Function_Push_Pop& operator=(Function_Push_Pop &&) = default; Function_Push_Pop(const Function_Push_Pop &) = delete; Function_Push_Pop& operator=(const Function_Push_Pop &) = delete; Function_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) : m_ds(t_ds) { - m_ds.get()->new_function_call(m_ds.get().stack_holder(), m_ds.get().conversion_saves()); + m_ds->new_function_call(m_ds.stack_holder(), m_ds.conversion_saves()); } ~Function_Push_Pop() { - m_ds.get()->pop_function_call(m_ds.get().stack_holder(), m_ds.get().conversion_saves()); + m_ds->pop_function_call(m_ds.stack_holder(), m_ds.conversion_saves()); } void save_params(const std::vector &t_params) { - m_ds.get()->save_function_params(t_params); + m_ds->save_function_params(t_params); } void save_params(std::initializer_list t_params) { - m_ds.get()->save_function_params(std::move(t_params)); + m_ds->save_function_params(std::move(t_params)); } private: - std::reference_wrapper m_ds; + const chaiscript::detail::Dispatch_State &m_ds; }; /// Creates a new scope then pops it on destruction struct Stack_Push_Pop { + Stack_Push_Pop(Stack_Push_Pop &&) = default; + Stack_Push_Pop& operator=(Stack_Push_Pop &&) = default; Stack_Push_Pop(const Stack_Push_Pop &) = delete; Stack_Push_Pop& operator=(const Stack_Push_Pop &) = delete; Stack_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) : m_ds(t_ds) { - m_ds.get()->new_stack(m_ds.get().stack_holder()); + m_ds->new_stack(m_ds.stack_holder()); } ~Stack_Push_Pop() { - m_ds.get()->pop_stack(m_ds.get().stack_holder()); + m_ds->pop_stack(m_ds.stack_holder()); } private: - std::reference_wrapper m_ds; + const chaiscript::detail::Dispatch_State &m_ds; }; } } diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 78d86ee7..659014d0 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "../chaiscript_defines.hpp" #include "../chaiscript_threading.hpp" @@ -33,227 +34,31 @@ #if defined(_POSIX_VERSION) && !defined(__CYGWIN__) #include -#else +#endif + + #ifdef CHAISCRIPT_WINDOWS -#define VC_EXTRA_LEAN -#if !defined(WIN32_LEAN_AND_MEAN) -#define WIN32_LEAN_AND_MEAN -#endif -#include -#endif +#include "chaiscript_windows.hpp" +#elif _POSIX_VERSION +#include "chaiscript_posix.hpp" +#else +#include "chaiscript_unknown.hpp" #endif #include "../dispatchkit/exception_specification.hpp" -#include "chaiscript_parser.hpp" namespace chaiscript { - namespace exception - { - /// \brief Thrown if an error occurs while attempting to load a binary module - struct load_module_error : std::runtime_error - { - load_module_error(const std::string &t_reason) noexcept - : std::runtime_error(t_reason) - { - } - - load_module_error(const load_module_error &) = default; - virtual ~load_module_error() noexcept {} - }; - } namespace detail { -#if defined(_POSIX_VERSION) && !defined(__CYGWIN__) - struct Loadable_Module - { - struct DLModule - { - DLModule(const std::string &t_filename) - : m_data(dlopen(t_filename.c_str(), RTLD_NOW)) - { - if (!m_data) - { - throw chaiscript::exception::load_module_error(dlerror()); - } - } - - DLModule(const DLModule &); // Explicitly unimplemented copy constructor - DLModule &operator=(const DLModule &); // Explicitly unimplemented assignment operator - - ~DLModule() - { - dlclose(m_data); - } - - void *m_data; - }; - - template - struct DLSym - { - DLSym(DLModule &t_mod, const std::string &t_symbol) - : m_symbol(cast_symbol(dlsym(t_mod.m_data, t_symbol.c_str()))) - { - if (!m_symbol) - { - throw chaiscript::exception::load_module_error(dlerror()); - } - } - - static T cast_symbol(void *p) - { - union cast_union - { - T func_ptr; - void *in_ptr; - }; - - cast_union c; - c.in_ptr = p; - return c.func_ptr; - } - - T m_symbol; - }; - - Loadable_Module(const std::string &t_module_name, const std::string &t_filename) - : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), - m_moduleptr(m_func.m_symbol()) - { - } - - DLModule m_dlmodule; - DLSym m_func; - ModulePtr m_moduleptr; - }; -#else - -#ifdef WIN32 - - - struct Loadable_Module - { - template - static std::wstring to_wstring(const T &t_str) - { - return std::wstring(t_str.begin(), t_str.end()); - } - - template - static std::string to_string(const T &t_str) - { - return std::string(t_str.begin(), t_str.end()); - } - -#if defined(_UNICODE) || defined(UNICODE) - template - static std::wstring to_proper_string(const T &t_str) - { - return to_wstring(t_str); - } -#else - template - static std::string to_proper_string(const T &t_str) - { - return to_string(t_str); - } -#endif - - static std::string get_error_message(DWORD t_err) - { - typedef LPTSTR StringType; - -#if defined(_UNICODE) || defined(UNICODE) - std::wstring retval = L"Unknown Error"; -#else - std::string retval = "Unknown Error"; -#endif - StringType lpMsgBuf = nullptr; - - if (FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, - t_err, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&lpMsgBuf), - 0, nullptr ) != 0 && lpMsgBuf) - { - retval = lpMsgBuf; - LocalFree(lpMsgBuf); - } - - return to_string(retval); - } - - struct DLModule - { - DLModule(const std::string &t_filename) - : m_data(LoadLibrary(to_proper_string(t_filename).c_str())) - { - if (!m_data) - { - throw chaiscript::exception::load_module_error(get_error_message(GetLastError())); - } - } - - ~DLModule() - { - FreeLibrary(m_data); - } - - HMODULE m_data; - }; - - template - struct DLSym - { - DLSym(DLModule &t_mod, const std::string &t_symbol) - : m_symbol(reinterpret_cast(GetProcAddress(t_mod.m_data, t_symbol.c_str()))) - { - if (!m_symbol) - { - throw chaiscript::exception::load_module_error(get_error_message(GetLastError())); - } - } - - T m_symbol; - }; - - Loadable_Module(const std::string &t_module_name, const std::string &t_filename) - : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), - m_moduleptr(m_func.m_symbol()) - { - } - - DLModule m_dlmodule; - DLSym m_func; - ModulePtr m_moduleptr; - }; - -#else - struct Loadable_Module - { - Loadable_Module(const std::string &, const std::string &) - { - throw chaiscript::exception::load_module_error("Loadable module support not available for your platform"); - } - - ModulePtr m_moduleptr; - }; -#endif -#endif - typedef std::shared_ptr Loadable_Module_Ptr; } /// \brief The main object that the ChaiScript user will use. - class ChaiScript { + class ChaiScript_Basic { mutable chaiscript::detail::threading::shared_mutex m_mutex; mutable chaiscript::detail::threading::recursive_mutex m_use_mutex; @@ -265,19 +70,16 @@ namespace chaiscript std::vector m_module_paths; std::vector m_use_paths; + std::unique_ptr m_parser; + chaiscript::detail::Dispatch_Engine m_engine; /// Evaluates the given string in by parsing it and running the results through the evaluator Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false) { try { - parser::ChaiScript_Parser parser; - if (parser.parse(t_input, t_filename)) { - //parser.show_match_stack(); - return parser.optimized_ast()->eval(m_engine); - } else { - return Boxed_Value(); - } + const auto p = m_parser->parse(t_input, t_filename); + return p->eval(m_engine); } catch (chaiscript::eval::detail::Return_Value &rv) { return rv.retval; @@ -286,8 +88,6 @@ namespace chaiscript - - /// Evaluates the given file and looks in the 'use' paths const Boxed_Value internal_eval_file(const std::string &t_filename) { for (const auto &path : m_use_paths) @@ -324,28 +124,7 @@ namespace chaiscript } /// Builds all the requirements for ChaiScript, including its evaluator and a run of its prelude. - void build_eval_system(const ModulePtr &t_lib) { - m_engine.add_reserved_word("def"); - m_engine.add_reserved_word("fun"); - m_engine.add_reserved_word("while"); - m_engine.add_reserved_word("for"); - m_engine.add_reserved_word("if"); - m_engine.add_reserved_word("else"); - m_engine.add_reserved_word("&&"); - m_engine.add_reserved_word("||"); - m_engine.add_reserved_word(","); - m_engine.add_reserved_word("auto"); - m_engine.add_reserved_word("return"); - m_engine.add_reserved_word("break"); - m_engine.add_reserved_word("true"); - m_engine.add_reserved_word("false"); - m_engine.add_reserved_word("class"); - m_engine.add_reserved_word("attr"); - m_engine.add_reserved_word("var"); - m_engine.add_reserved_word("global"); - m_engine.add_reserved_word("GLOBAL"); - m_engine.add_reserved_word("_"); - + void build_eval_system(const ModulePtr &t_lib, const std::vector &t_opts) { if (t_lib) { add(t_lib); @@ -390,23 +169,26 @@ namespace chaiscript - m_engine.add(fun([this](const std::string &t_module, const std::string &t_file){ return load_module(t_module, t_file); }), "load_module"); - m_engine.add(fun([this](const std::string &t_module){ return load_module(t_module); }), "load_module"); + if (std::find(t_opts.begin(), t_opts.end(), Options::No_Load_Modules) == t_opts.end() + && std::find(t_opts.begin(), t_opts.end(), Options::Load_Modules) != t_opts.end()) + { + m_engine.add(fun([this](const std::string &t_module, const std::string &t_file){ return load_module(t_module, t_file); }), "load_module"); + m_engine.add(fun([this](const std::string &t_module){ return load_module(t_module); }), "load_module"); + } + + if (std::find(t_opts.begin(), t_opts.end(), Options::No_External_Scripts) == t_opts.end() + && std::find(t_opts.begin(), t_opts.end(), Options::External_Scripts) != t_opts.end()) + { + m_engine.add(fun([this](const std::string &t_file){ return use(t_file); }), "use"); + m_engine.add(fun([this](const std::string &t_file){ return internal_eval_file(t_file); }), "eval_file"); + } - m_engine.add(fun([this](const std::string &t_file){ return use(t_file); }), "use"); - m_engine.add(fun([this](const std::string &t_file){ return internal_eval_file(t_file); }), "eval_file"); m_engine.add(fun([this](const std::string &t_str){ return internal_eval(t_str); }), "eval"); m_engine.add(fun([this](const AST_NodePtr &t_ast){ return eval(t_ast); }), "eval"); - m_engine.add(fun(&parse), "parse"); - m_engine.add(fun(&ChaiScript::version_major), "version_major"); - m_engine.add(fun(&ChaiScript::version_minor), "version_minor"); - m_engine.add(fun(&ChaiScript::version_patch), "version_patch"); - m_engine.add(fun(&ChaiScript::version), "version"); - m_engine.add(fun(&ChaiScript::compiler_version), "compiler_version"); - m_engine.add(fun(&ChaiScript::compiler_name), "compiler_name"); - m_engine.add(fun(&ChaiScript::compiler_id), "compiler_id"); - m_engine.add(fun(&ChaiScript::debug_build), "debug_build"); + m_engine.add(fun([this](const std::string &t_str, const bool t_dump){ return parse(t_str, t_dump); }), "parse"); + m_engine.add(fun([this](const std::string &t_str){ return parse(t_str); }), "parse"); + m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global_const(t_bv, t_name); }), "add_global_const"); m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global"); @@ -437,64 +219,42 @@ namespace chaiscript } } + std::vector ensure_minimum_path_vec(std::vector paths) + { + if (paths.empty()) { return {""}; } + else { return paths; } + } + public: + /// \brief Constructor for ChaiScript /// \param[in] t_lib Standard library to apply to this ChaiScript instance /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file - ChaiScript(const ModulePtr &t_lib, - std::vector t_modulepaths = std::vector(), - std::vector t_usepaths = std::vector()) - : m_module_paths(std::move(t_modulepaths)), m_use_paths(std::move(t_usepaths)) + ChaiScript_Basic(const ModulePtr &t_lib, + std::unique_ptr &&parser, + std::vector t_module_paths = {}, + std::vector t_use_paths = {}, + const std::vector &t_opts = chaiscript::default_options()) + : m_module_paths(ensure_minimum_path_vec(std::move(t_module_paths))), + m_use_paths(ensure_minimum_path_vec(std::move(t_use_paths))), + m_parser(std::move(parser)), + m_engine(*m_parser) { - if (m_module_paths.empty()) - { - m_module_paths.push_back(""); - } - - if (m_use_paths.empty()) - { - m_use_paths.push_back(""); - } - - build_eval_system(t_lib); - } - - /// \brief Constructor for ChaiScript. - /// - /// This version of the ChaiScript constructor attempts to find the stdlib module to load - /// at runtime generates an error if it cannot be found. - /// - /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module - /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file - ChaiScript( std::vector t_modulepaths = std::vector(), - std::vector t_usepaths = std::vector()) - : m_module_paths(std::move(t_modulepaths)), m_use_paths(std::move(t_usepaths)) - { - if (m_module_paths.empty()) - { - m_module_paths.push_back(""); - } - - if (m_use_paths.empty()) - { - m_use_paths.push_back(""); - } - #if defined(_POSIX_VERSION) && !defined(__CYGWIN__) // If on Unix, add the path of the current executable to the module search path // as windows would do union cast_union { - Boxed_Value (ChaiScript::*in_ptr)(const std::string&); + Boxed_Value (ChaiScript_Basic::*in_ptr)(const std::string&); void *out_ptr; }; Dl_info rInfo; memset( &rInfo, 0, sizeof(rInfo) ); cast_union u; - u.in_ptr = &ChaiScript::use; + u.in_ptr = &ChaiScript_Basic::use; if ( dladdr(static_cast(u.out_ptr), &rInfo) && rInfo.dli_fname ) { std::string dllpath(rInfo.dli_fname); const size_t lastslash = dllpath.rfind('/'); @@ -514,12 +274,37 @@ namespace chaiscript m_module_paths.insert(m_module_paths.begin(), dllpath+"/"); } #endif + build_eval_system(t_lib, t_opts); + } - - // attempt to load the stdlib - load_module("chaiscript_stdlib-" + version()); - - build_eval_system(ModulePtr()); + /// \brief Constructor for ChaiScript. + /// + /// This version of the ChaiScript constructor attempts to find the stdlib module to load + /// at runtime generates an error if it cannot be found. + /// + /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module + /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file + ChaiScript_Basic(std::unique_ptr &&parser, + std::vector t_module_paths = {}, + std::vector t_use_paths = {}, + const std::vector &t_opts = chaiscript::default_options()) + : ChaiScript_Basic({}, std::move(parser), t_module_paths, t_use_paths, t_opts) + { + try { + // attempt to load the stdlib + load_module("chaiscript_stdlib-" + Build_Info::version()); + } catch (const exception::load_module_error &t_err) { + std::cout << "An error occured while trying to load the chaiscript standard library.\n" + << "\n" + << "You must either provide a standard library, or compile it in.\n" + << "For an example of compiling the standard library in,\n" + << "see: https://gist.github.com/lefticus/9456197\n" + << "Compiling the stdlib in is the recommended and MOST SUPPORTED method.\n" + << "\n" + << "\n" + << t_err.what(); + throw; + } } @@ -532,65 +317,16 @@ namespace chaiscript } } - static AST_NodePtr parse(const std::string &t_input) + AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false) { - parser::ChaiScript_Parser parser; - if (parser.parse(t_input, "PARSE")) { - //parser.show_match_stack(); - return parser.optimized_ast(); - } else { - throw chaiscript::exception::eval_error("Unknown error while parsing"); + const auto ast = m_parser->parse(t_input, "PARSE"); + if (t_debug_print) { + m_parser->debug_print(ast); } + return ast; } - static int version_major() - { - return chaiscript::version_major; - } - - static int version_minor() - { - return chaiscript::version_minor; - } - - static int version_patch() - { - return chaiscript::version_patch; - } - - 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() - { - return compiler_name() + '-' + compiler_version(); - } - - static std::string build_id() - { - return compiler_id() + (debug_build()?"-Debug":"-Release"); - } - - static std::string compiler_version() - { - return chaiscript::compiler_version; - } - - static std::string compiler_name() - { - return chaiscript::compiler_name; - } - - static bool debug_build() - { - return chaiscript::debug_build; - } - - - std::string get_type_name(const Type_Info &ti) const { return m_engine.get_type_name(ti); @@ -643,8 +379,9 @@ namespace chaiscript /// \param[in] t_name Name of the value to add /// \throw chaiscript::exception::global_non_const If t_bv is not a constant object /// \sa Boxed_Value::is_const - ChaiScript &add_global_const(const Boxed_Value &t_bv, const std::string &t_name) + ChaiScript_Basic &add_global_const(const Boxed_Value &t_bv, const std::string &t_name) { + Name_Validator::validate_object_name(t_name); m_engine.add_global_const(t_bv, t_name); return *this; } @@ -654,14 +391,16 @@ namespace chaiscript /// \param[in] t_name Name of the value to add /// \warning The user is responsible for making sure the object is thread-safe if necessary /// ChaiScript is thread-safe but provides no threading locking mechanism to the script - ChaiScript &add_global(const Boxed_Value &t_bv, const std::string &t_name) + ChaiScript_Basic &add_global(const Boxed_Value &t_bv, const std::string &t_name) { + Name_Validator::validate_object_name(t_name); m_engine.add_global(t_bv, t_name); return *this; } - ChaiScript &set_global(const Boxed_Value &t_bv, const std::string &t_name) + ChaiScript_Basic &set_global(const Boxed_Value &t_bv, const std::string &t_name) { + Name_Validator::validate_object_name(t_name); m_engine.set_global(t_bv, t_name); return *this; } @@ -759,8 +498,9 @@ namespace chaiscript /// /// \sa \ref adding_items template - ChaiScript &add(const T &t_t, const std::string &t_name) + ChaiScript_Basic &add(const T &t_t, const std::string &t_name) { + Name_Validator::validate_object_name(t_name); m_engine.add(t_t, t_name); return *this; } @@ -774,7 +514,7 @@ namespace chaiscript /// chaiscript::ChaiScript chai; /// chai.add(chaiscript::base_class()); /// \endcode - ChaiScript &add(const Type_Conversion &d) + ChaiScript_Basic &add(const Type_Conversion &d) { m_engine.add(d); return *this; @@ -783,7 +523,7 @@ namespace chaiscript /// \brief Adds all elements of a module to ChaiScript runtime /// \param[in] t_p The module to add. /// \sa chaiscript::Module - ChaiScript &add(const ModulePtr &t_p) + ChaiScript_Basic &add(const ModulePtr &t_p) { t_p->apply(*this, this->get_eval_engine()); return *this; @@ -805,7 +545,7 @@ namespace chaiscript { std::vector errors; std::string version_stripped_name = t_module_name; - size_t version_pos = version_stripped_name.find("-"+version()); + size_t version_pos = version_stripped_name.find("-" + Build_Info::version()); if (version_pos != std::string::npos) { version_stripped_name.erase(version_pos); @@ -835,21 +575,7 @@ namespace chaiscript } } - std::string errstring; - - for (std::vector::const_iterator itr = errors.begin(); - itr != errors.end(); - ++itr) - { - if (!errstring.empty()) - { - errstring += "; "; - } - - errstring += itr->what(); - } - - throw chaiscript::exception::load_module_error("Unable to find module: " + t_module_name + " Errors: " + errstring); + throw chaiscript::exception::load_module_error(t_module_name, errors); } /// \brief Load a binary module from a dynamic library. Works on platforms that support @@ -886,14 +612,7 @@ namespace chaiscript /// \throw chaiscript::exception::eval_error In the case that evaluation fails. Boxed_Value operator()(const std::string &t_script, const Exception_Handler &t_handler = Exception_Handler()) { - try { - return do_eval(t_script); - } catch (Boxed_Value &bv) { - if (t_handler) { - t_handler->handle(bv, m_engine); - } - throw; - } + return eval(t_script, t_handler); } /// \brief Evaluates a string and returns a typesafe result. @@ -912,21 +631,14 @@ namespace chaiscript template T eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__") { - try { - return m_engine.boxed_cast(do_eval(t_input, t_filename)); - } catch (Boxed_Value &bv) { - if (t_handler) { - t_handler->handle(bv, m_engine); - } - throw; - } + return m_engine.boxed_cast(eval(t_input, t_handler, t_filename)); } /// \brief casts an object while applying any Dynamic_Conversion available template - typename detail::Cast_Helper::Result_Type boxed_cast(const Boxed_Value &bv) const + decltype(auto) boxed_cast(const Boxed_Value &bv) const { - return m_engine.boxed_cast(bv); + return(m_engine.boxed_cast(bv)); } @@ -958,14 +670,7 @@ namespace chaiscript /// \return result of the script execution /// \throw chaiscript::exception::eval_error In the case that evaluation fails. Boxed_Value eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) { - try { - return do_eval(load_file(t_filename), t_filename); - } catch (Boxed_Value &bv) { - if (t_handler) { - t_handler->handle(bv, m_engine); - } - throw; - } + return eval(load_file(t_filename), t_handler, t_filename); } /// \brief Loads the file specified by filename, evaluates it, and returns the type safe result. @@ -978,14 +683,7 @@ namespace chaiscript /// to the requested type. template T eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) { - try { - return m_engine.boxed_cast(do_eval(load_file(t_filename), t_filename)); - } catch (Boxed_Value &bv) { - if (t_handler) { - t_handler->handle(bv, m_engine); - } - throw; - } + return m_engine.boxed_cast(eval_file(t_filename, t_handler)); } }; diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index b692538c..76706ff4 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -41,10 +41,15 @@ namespace chaiscript /// \brief Classes and functions that are part of the runtime eval system namespace eval { + template struct AST_Node_Impl; + + template using AST_Node_Impl_Ptr = typename std::shared_ptr>; + namespace detail { /// Helper function that will set up the scope around a function call, including handling the named function parameters - static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_NodePtr &t_node, const std::vector &t_param_names, const std::vector &t_vals, const std::map *t_locals=nullptr) { + template + static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl_Ptr &t_node, const std::vector &t_param_names, const std::vector &t_vals, const std::map *t_locals=nullptr) { chaiscript::detail::Dispatch_State state(t_ss); const Boxed_Value *thisobj = [&]() -> const Boxed_Value *{ @@ -81,21 +86,116 @@ namespace chaiscript } } - struct Binary_Operator_AST_Node : AST_Node { - Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector t_children) : - AST_Node(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children)), + template + struct AST_Node_Impl : AST_Node + { + AST_Node_Impl(std::string t_ast_node_text, AST_Node_Type t_id, Parse_Location t_loc, + std::vector> t_children = std::vector>()) + : AST_Node(std::move(t_ast_node_text), std::move(t_id), std::move(t_loc)), + children(std::move(t_children)) + { + } + + + std::vector get_children() const final { + return {children.begin(), children.end()}; + } + + Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const final + { + try { + T::trace(t_e, this); + return eval_internal(t_e); + } catch (exception::eval_error &ee) { + ee.call_stack.push_back(shared_from_this()); + throw; + } + } + + std::vector> children; + + protected: + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const + { + throw std::runtime_error("Undispatched ast_node (internal error)"); + } + }; + + + template + struct Compiled_AST_Node : AST_Node_Impl { + Compiled_AST_Node(AST_Node_Impl_Ptr t_original_node, std::vector> t_children, + std::function> &, const chaiscript::detail::Dispatch_State &t_ss)> t_func) : + AST_Node_Impl(t_original_node->text, AST_Node_Type::Compiled, t_original_node->location, std::move(t_children)), + m_func(std::move(t_func)), + m_original_node(std::move(t_original_node)) + { } + + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + return m_func(this->children, t_ss); + } + + std::function> &, const chaiscript::detail::Dispatch_State &t_ss)> m_func; + AST_Node_Impl_Ptr m_original_node; + }; + + + template + struct Fold_Right_Binary_Operator_AST_Node : AST_Node_Impl { + Fold_Right_Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector> t_children, Boxed_Value t_rhs) : + AST_Node_Impl(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children)), + m_oper(Operators::to_operator(t_oper)), + m_rhs(std::move(t_rhs)) + { } + + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + return do_oper(t_ss, this->text, this->children[0]->eval(t_ss)); + } + + protected: + Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss, + const std::string &t_oper_string, const Boxed_Value &t_lhs) const + { + try { + if (t_lhs.get_type_info().is_arithmetic()) + { + // If it's an arithmetic operation we want to short circuit dispatch + try{ + return Boxed_Number::do_oper(m_oper, t_lhs, m_rhs); + } catch (const chaiscript::exception::arithmetic_error &) { + throw; + } catch (...) { + throw exception::eval_error("Error with numeric operator calling: " + t_oper_string); + } + } 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()); + } + } + catch(const exception::dispatch_error &e){ + throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss); + } + } + + private: + Operators::Opers m_oper; + Boxed_Value m_rhs; + mutable std::atomic_uint_fast32_t m_loc = {0}; + }; + + + template + struct Binary_Operator_AST_Node : AST_Node_Impl { + Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children)), m_oper(Operators::to_operator(t_oper)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { auto lhs = this->children[0]->eval(t_ss); auto rhs = this->children[1]->eval(t_ss); - return do_oper(t_ss, m_oper, text, lhs, rhs); - } - - std::string pretty_print() const override - { - return "(" + this->children[0]->pretty_print() + " " + text + " " + this->children[1]->pretty_print() + ")"; + return do_oper(t_ss, m_oper, this->text, lhs, rhs); } protected: @@ -103,7 +203,7 @@ namespace chaiscript Operators::Opers t_oper, const std::string &t_oper_string, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) const { try { - if (t_oper != Operators::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic()) + if (t_oper != Operators::Opers::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic()) { // If it's an arithmetic operation we want to short circuit dispatch try{ @@ -126,103 +226,102 @@ namespace chaiscript private: Operators::Opers m_oper; - mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; - struct Int_AST_Node final : AST_Node { - Int_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, Boxed_Value t_bv) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Int, std::move(t_loc)), - m_value(std::move(t_bv)) { assert(text != ""); } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { - return m_value; - } + template + struct Constant_AST_Node final : AST_Node_Impl { + Constant_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, Boxed_Value t_value) + : AST_Node_Impl(t_ast_node_text, AST_Node_Type::Constant, std::move(t_loc)), + m_value(std::move(t_value)) + { + } - private: - Boxed_Value m_value; + Constant_AST_Node(Boxed_Value t_value) + : AST_Node_Impl("", AST_Node_Type::Constant, Parse_Location()), + m_value(std::move(t_value)) + { + } + + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { + return m_value; + } + + Boxed_Value m_value; }; - struct Float_AST_Node final : AST_Node { - Float_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, Boxed_Value t_bv) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Float, std::move(t_loc)), - m_value(std::move(t_bv)) { } - - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { - return m_value; - } - - private: - Boxed_Value m_value; - - }; - - struct Id_AST_Node final : AST_Node { + template + struct Id_AST_Node final : AST_Node_Impl { Id_AST_Node(const std::string &t_ast_node_text, Parse_Location t_loc) : - AST_Node(t_ast_node_text, AST_Node_Type::Id, std::move(t_loc)), - m_value(get_value(t_ast_node_text)), m_loc(0) + AST_Node_Impl(t_ast_node_text, AST_Node_Type::Id, std::move(t_loc)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - if (!m_value.is_undef()) - { - return m_value; - } else { - try { - return t_ss.get_object(this->text, m_loc); - } - catch (std::exception &) { - throw exception::eval_error("Can not find object: " + this->text); - } + try { + return t_ss.get_object(this->text, m_loc); + } + catch (std::exception &) { + throw exception::eval_error("Can not find object: " + this->text); } } private: - static Boxed_Value get_value(const std::string &t_text) + mutable std::atomic_uint_fast32_t m_loc = {0}; + }; + + + template + struct Unused_Return_Fun_Call_AST_Node final : AST_Node_Impl { + Unused_Return_Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Unused_Return_Fun_Call, std::move(t_loc), std::move(t_children)) { } + + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - if (t_text == "true") { - return const_var(true); - } else if (t_text == "false") { - return const_var(false); - } else if (t_text == "Infinity") { - return const_var(std::numeric_limits::infinity()); - } else if (t_text == "NaN") { - return const_var(std::numeric_limits::quiet_NaN()); - } else if (t_text == "_") { - return Boxed_Value(std::make_shared()); - } else { - return Boxed_Value(); + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + + std::vector params; + + params.reserve(this->children[1]->children.size()); + for (const auto &child : this->children[1]->children) { + params.push_back(child->eval(t_ss)); + } + + Boxed_Value fn(this->children[0]->eval(t_ss)); + + try { + return (*t_ss->boxed_cast(fn))(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); + } + catch(const exception::bad_boxed_cast &){ + try { + 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); + } catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function."); + } + } + catch(const exception::arity_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } + catch(const exception::guard_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } + catch(detail::Return_Value &rv) { + return rv.retval; } } - - Boxed_Value m_value; - - mutable std::atomic_uint_fast32_t m_loc; - }; - - struct Char_AST_Node final : AST_Node { - Char_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Char, std::move(t_loc)) { } - }; - - struct Str_AST_Node final : AST_Node { - Str_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Str, std::move(t_loc)) { } - }; - - struct Eol_AST_Node final : AST_Node { - Eol_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Eol, std::move(t_loc)) { } - - std::string pretty_print() const override - { - return "\n"; - } }; - struct Fun_Call_AST_Node final : AST_Node { - Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) { } + + template + struct Fun_Call_AST_Node final : AST_Node_Impl { + Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { @@ -240,7 +339,7 @@ namespace chaiscript Boxed_Value fn(this->children[0]->eval(t_ss)); try { - return (*t_ss->boxed_cast(fn))(params, t_ss.conversions()); + return (*t_ss->boxed_cast(fn))(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); @@ -265,70 +364,25 @@ namespace chaiscript } } - std::string pretty_print() const override - { - std::ostringstream oss; - - int count = 0; - for (const auto &child : this->children) { - oss << child->pretty_print(); - - if (count == 0) - { - oss << "("; - } - ++count; - } - - oss << ")"; - - return oss.str(); - } }; - struct Arg_AST_Node final : AST_Node { - Arg_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { } + template + struct Arg_AST_Node final : AST_Node_Impl { + Arg_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { } - std::string pretty_print() const override - { - std::ostringstream oss; - for (size_t j = 0; j < this->children.size(); ++j) { - if (j != 0) - { - oss << " "; - } - - oss << this->children[j]->pretty_print(); - } - - return oss.str(); - } }; - struct Arg_List_AST_Node final : AST_Node { - Arg_List_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { } + template + struct Arg_List_AST_Node final : AST_Node_Impl { + Arg_List_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { } - std::string pretty_print() const override - { - std::ostringstream oss; - for (size_t j = 0; j < this->children.size(); ++j) { - if (j != 0) - { - oss << ", "; - } - oss << this->children[j]->pretty_print(); - } - - return oss.str(); - } - - static std::string get_arg_name(const AST_NodePtr &t_node) { + static std::string get_arg_name(const AST_Node_Impl_Ptr &t_node) { if (t_node->children.empty()) { return t_node->text; @@ -339,7 +393,7 @@ namespace chaiscript } } - static std::vector get_arg_names(const AST_NodePtr &t_node) { + static std::vector get_arg_names(const AST_Node_Impl_Ptr &t_node) { std::vector retval; for (const auto &node : t_node->children) @@ -350,7 +404,7 @@ namespace chaiscript return retval; } - static std::pair get_arg_type(const AST_NodePtr &t_node, const chaiscript::detail::Dispatch_State &t_ss) + static std::pair get_arg_type(const AST_Node_Impl_Ptr &t_node, const chaiscript::detail::Dispatch_State &t_ss) { if (t_node->children.size() < 2) { @@ -360,7 +414,7 @@ namespace chaiscript } } - static dispatch::Param_Types get_arg_types(const AST_NodePtr &t_node, const chaiscript::detail::Dispatch_State &t_ss) { + static dispatch::Param_Types get_arg_types(const AST_Node_Impl_Ptr &t_node, const chaiscript::detail::Dispatch_State &t_ss) { std::vector> retval; for (const auto &child : t_node->children) @@ -372,19 +426,20 @@ namespace chaiscript } }; - struct Equation_AST_Node final : AST_Node { - Equation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Equation, std::move(t_loc), std::move(t_children)), - m_oper(Operators::to_operator(children[1]->text)) - { assert(children.size() == 3); } + template + struct Equation_AST_Node final : AST_Node_Impl { + Equation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Equation, std::move(t_loc), std::move(t_children)), + m_oper(Operators::to_operator(this->text)) + { assert(this->children.size() == 2); } 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[2]->eval(t_ss); + Boxed_Value rhs = this->children[1]->eval(t_ss); Boxed_Value lhs = this->children[0]->eval(t_ss); - if (m_oper != Operators::invalid && lhs.get_type_info().is_arithmetic() && + if (m_oper != Operators::Opers::invalid && lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) { try { @@ -392,7 +447,7 @@ namespace chaiscript } catch (const std::exception &) { throw exception::eval_error("Error with unsupported arithmetic assignment operation"); } - } else if (m_oper == Operators::assign) { + } else if (m_oper == Operators::Opers::assign) { if (lhs.is_return_value()) { throw exception::eval_error("Error, cannot assign to temporary value."); } @@ -400,9 +455,13 @@ namespace chaiscript try { if (lhs.is_undef()) { - if (!this->children.empty() && - !this->children[0]->children.empty() - && this->children[0]->children[0]->identifier == AST_Node_Type::Reference) + if ((!this->children.empty() + && ((this->children[0]->identifier == AST_Node_Type::Reference) + || (!this->children[0]->children.empty() + && this->children[0]->children[0]->identifier == AST_Node_Type::Reference) + ) + ) + ) { /// \todo This does not handle the case of an unassigned reference variable /// being assigned outside of its declaration @@ -419,17 +478,17 @@ namespace chaiscript } try { - return t_ss->call_function(this->children[1]->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions()); + return t_ss->call_function(this->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions()); } catch(const exception::dispatch_error &e){ - throw exception::eval_error("Unable to find appropriate'" + this->children[1]->text + "' operator.", e.parameters, e.functions, false, *t_ss); + throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss); } } catch(const exception::dispatch_error &e){ throw exception::eval_error("Missing clone or copy constructor for right hand side of equation", e.parameters, e.functions, false, *t_ss); } } - else if (this->children[1]->text == ":=") { + else if (this->text == ":=") { if (lhs.is_undef() || Boxed_Value::type_match(lhs, rhs)) { lhs.assign(rhs); lhs.reset_return_value(); @@ -439,9 +498,9 @@ namespace chaiscript } else { try { - return t_ss->call_function(this->children[1]->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions()); + return t_ss->call_function(this->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions()); } catch(const exception::dispatch_error &e){ - throw exception::eval_error("Unable to find appropriate'" + this->children[1]->text + "' operator.", e.parameters, e.functions, false, *t_ss); + throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss); } } @@ -450,76 +509,59 @@ namespace chaiscript private: Operators::Opers m_oper; - mutable std::atomic_uint_fast32_t m_loc; - mutable std::atomic_uint_fast32_t m_clone_loc; + mutable std::atomic_uint_fast32_t m_loc = {0}; + mutable std::atomic_uint_fast32_t m_clone_loc = {0}; }; - struct Global_Decl_AST_Node final : AST_Node { - Global_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Global_Decl, std::move(t_loc), std::move(t_children)) { } + template + struct Global_Decl_AST_Node final : AST_Node_Impl { + Global_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Global_Decl, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { const std::string &idname = [&]()->const std::string & { - if (children[0]->identifier == AST_Node_Type::Reference) { - return children[0]->children[0]->text; + if (this->children[0]->identifier == AST_Node_Type::Reference) { + return this->children[0]->children[0]->text; } else { - return children[0]->text; + return this->children[0]->text; } }(); - try { - return t_ss->add_global_no_throw(Boxed_Value(), idname); - } - catch (const exception::reserved_word_error &) { - throw exception::eval_error("Reserved word used as global '" + idname + "'"); - } + return t_ss->add_global_no_throw(Boxed_Value(), idname); } }; - struct Var_Decl_AST_Node final : AST_Node { - Var_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Var_Decl, std::move(t_loc), std::move(t_children)) { } + template + struct Var_Decl_AST_Node final : AST_Node_Impl { + Var_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Var_Decl, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - if (this->children[0]->identifier == AST_Node_Type::Reference) - { - return this->children[0]->eval(t_ss); - } else { - const std::string &idname = this->children[0]->text; + const std::string &idname = this->children[0]->text; - try { - Boxed_Value bv; - t_ss.add_object(idname, bv); - return bv; - } - catch (const exception::reserved_word_error &) { - throw exception::eval_error("Reserved word used as variable '" + idname + "'"); - } catch (const exception::name_conflict_error &e) { - throw exception::eval_error("Variable redefined '" + e.name() + "'"); - } + try { + Boxed_Value bv; + t_ss.add_object(idname, bv); + return bv; + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Variable redefined '" + e.name() + "'"); } - } - - std::string pretty_print() const override - { - return "var " + this->children[0]->text; - } - }; - struct Array_Call_AST_Node final : AST_Node { - Array_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Array_Call, std::move(t_loc), std::move(t_children)) { } + template + struct Array_Call_AST_Node final : AST_Node_Impl { + Array_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Array_Call, std::move(t_loc), std::move(t_children)) { } 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{children[0]->eval(t_ss), children[1]->eval(t_ss)}; + const std::vector params{this->children[0]->eval(t_ss), this->children[1]->eval(t_ss)}; try { fpp.save_params(params); @@ -530,43 +572,30 @@ namespace chaiscript } } - std::string pretty_print() const override - { - std::ostringstream oss; - oss << this->children[0]->pretty_print(); - - for (size_t i = 1; i < this->children.size(); ++i) - { - oss << "["; - oss << this->children[i]->pretty_print(); - oss << "]"; - } - - return oss.str(); - } private: - mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; - struct Dot_Access_AST_Node final : AST_Node { - Dot_Access_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Dot_Access, std::move(t_loc), std::move(t_children)), + template + struct Dot_Access_AST_Node final : AST_Node_Impl { + Dot_Access_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Dot_Access, std::move(t_loc), std::move(t_children)), m_fun_name( - ((children[2]->identifier == AST_Node_Type::Fun_Call) || (children[2]->identifier == AST_Node_Type::Array_Call))? - children[2]->children[0]->text:children[2]->text) { } + ((this->children[1]->identifier == AST_Node_Type::Fun_Call) || (this->children[1]->identifier == AST_Node_Type::Array_Call))? + this->children[1]->children[0]->text:this->children[1]->text) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - Boxed_Value retval = children[0]->eval(t_ss); + Boxed_Value retval = this->children[0]->eval(t_ss); std::vector params{retval}; bool has_function_params = false; - if (children[2]->children.size() > 1) { + if (this->children[1]->children.size() > 1) { has_function_params = true; - for (const auto &child : children[2]->children[1]->children) { + for (const auto &child : this->children[1]->children[1]->children) { params.push_back(child->eval(t_ss)); } } @@ -588,9 +617,9 @@ namespace chaiscript retval = std::move(rv.retval); } - if (this->children[2]->identifier == AST_Node_Type::Array_Call) { + if (this->children[1]->identifier == AST_Node_Type::Array_Call) { try { - retval = t_ss->call_function("[]", m_array_loc, {retval, this->children[2]->children[1]->eval(t_ss)}, t_ss.conversions()); + retval = t_ss->call_function("[]", m_array_loc, {retval, this->children[1]->children[1]->eval(t_ss)}, 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); @@ -601,64 +630,30 @@ namespace chaiscript } private: - mutable std::atomic_uint_fast32_t m_loc; - mutable std::atomic_uint_fast32_t m_array_loc; + mutable std::atomic_uint_fast32_t m_loc = {0}; + mutable std::atomic_uint_fast32_t m_array_loc = {0}; const std::string m_fun_name; }; - struct Quoted_String_AST_Node final : AST_Node { - Quoted_String_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Quoted_String, std::move(t_loc)), - m_value(const_var(text)) { } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { - return m_value; - } - - std::string pretty_print() const override - { - return "\"" + text + "\""; - } - - private: - Boxed_Value m_value; - }; - - struct Single_Quoted_String_AST_Node final : AST_Node { - Single_Quoted_String_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Single_Quoted_String, std::move(t_loc)), - m_value(const_var(char(text.at(0)))) { } - - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override{ - return m_value; - } - - std::string pretty_print() const override - { - return "'" + text + "'"; - } - - private: - Boxed_Value m_value; - }; - - struct Lambda_AST_Node final : AST_Node { - Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(t_ast_node_text, AST_Node_Type::Lambda, std::move(t_loc), std::move(t_children)), - m_param_names(Arg_List_AST_Node::get_arg_names(children[1])) { } + template + struct Lambda_AST_Node final : AST_Node_Impl { + Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(t_ast_node_text, AST_Node_Type::Lambda, std::move(t_loc), std::move(t_children)), + m_param_names(Arg_List_AST_Node::get_arg_names(this->children[1])) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { const auto captures = [&]()->std::map{ std::map named_captures; - for (const auto &capture : children[0]->children) { + for (const auto &capture : this->children[0]->children) { named_captures.insert(std::make_pair(capture->children[0]->text, capture->children[0]->eval(t_ss))); } return named_captures; }(); const auto numparams = this->children[1]->children.size(); - const auto param_types = Arg_List_AST_Node::get_arg_types(this->children[1], t_ss); + const auto param_types = Arg_List_AST_Node::get_arg_types(this->children[1], t_ss); const auto &lambda_node = this->children.back(); std::reference_wrapper engine(*t_ss); @@ -679,36 +674,52 @@ namespace chaiscript }; - struct Block_AST_Node final : AST_Node { - Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Block, std::move(t_loc), std::move(t_children)) { } + template + struct Scopeless_Block_AST_Node final : AST_Node_Impl { + Scopeless_Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Scopeless_Block, std::move(t_loc), std::move(t_children)) { } + + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + const auto num_children = this->children.size(); + for (size_t i = 0; i < num_children-1; ++i) { + this->children[i]->eval(t_ss); + } + return this->children.back()->eval(t_ss); + } + }; + + template + struct Block_AST_Node final : AST_Node_Impl { + Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Block, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - const auto num_children = children.size(); + const auto num_children = this->children.size(); for (size_t i = 0; i < num_children-1; ++i) { - children[i]->eval(t_ss); + this->children[i]->eval(t_ss); } - return children.back()->eval(t_ss); + return this->children.back()->eval(t_ss); } }; - struct Def_AST_Node final : AST_Node { - Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), std::move(t_children)) { } + template + struct Def_AST_Node final : AST_Node_Impl { + Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ std::vector t_param_names; size_t numparams = 0; - AST_NodePtr guardnode; + AST_Node_Impl_Ptr guardnode; dispatch::Param_Types param_types; if ((this->children.size() > 2) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) { numparams = this->children[1]->children.size(); - t_param_names = Arg_List_AST_Node::get_arg_names(this->children[1]); - param_types = Arg_List_AST_Node::get_arg_types(this->children[1], t_ss); + t_param_names = Arg_List_AST_Node::get_arg_names(this->children[1]); + param_types = Arg_List_AST_Node::get_arg_types(this->children[1], t_ss); if (this->children.size() > 3) { guardnode = this->children[2]; @@ -736,7 +747,6 @@ namespace chaiscript try { const std::string & l_function_name = this->children[0]->text; - const std::string & l_annotation = this->annotation?this->annotation->text:""; const auto & func_node = this->children.back(); t_ss->add( dispatch::make_dynamic_proxy_function( @@ -745,10 +755,7 @@ namespace chaiscript return detail::eval_function(engine, func_node, t_param_names, t_params); }, static_cast(numparams), this->children.back(), - param_types, l_annotation, guard), l_function_name); - } - catch (const exception::reserved_word_error &e) { - throw exception::eval_error("Reserved word used as function name '" + e.word() + "'"); + param_types, guard), l_function_name); } catch (const exception::name_conflict_error &e) { throw exception::eval_error("Function redefined '" + e.name() + "'"); } @@ -757,15 +764,16 @@ namespace chaiscript }; - struct While_AST_Node final : AST_Node { - While_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::While, std::move(t_loc), std::move(t_children)) { } + template + struct While_AST_Node final : AST_Node_Impl { + While_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::While, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); try { - while (get_bool_condition(this->children[0]->eval(t_ss))) { + while (this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)) { try { this->children[1]->eval(t_ss); } catch (detail::Continue_Loop &) { @@ -782,85 +790,153 @@ namespace chaiscript } }; - struct Class_AST_Node final : AST_Node { - Class_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Class, std::move(t_loc), std::move(t_children)) { } + template + struct Class_AST_Node final : AST_Node_Impl { + Class_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Class, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); /// \todo do this better // put class name in current scope so it can be looked up by the attrs and methods - t_ss.add_object("_current_class_name", const_var(children[0]->text)); + t_ss.add_object("_current_class_name", const_var(this->children[0]->text)); - children[1]->eval(t_ss); + this->children[1]->eval(t_ss); return void_var(); } }; - struct Ternary_Cond_AST_Node final : AST_Node { - Ternary_Cond_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::If, std::move(t_loc), std::move(t_children)) - { assert(children.size() == 3); } + template + struct Ternary_Cond_AST_Node final : AST_Node_Impl { + Ternary_Cond_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Ternary_Cond, std::move(t_loc), std::move(t_children)) + { assert(this->children.size() == 3); } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - if (get_bool_condition(children[0]->eval(t_ss))) { - return children[1]->eval(t_ss); + if (this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)) { + return this->children[1]->eval(t_ss); } else { - return children[2]->eval(t_ss); + return this->children[2]->eval(t_ss); } } - }; - struct If_AST_Node final : AST_Node { - If_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::If, std::move(t_loc), std::move(t_children)) { } + + template + struct If_AST_Node final : AST_Node_Impl { + If_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::If, std::move(t_loc), std::move(t_children)) + { + assert(this->children.size() == 3); + } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - - if (get_bool_condition(children[0]->eval(t_ss))) { - return children[1]->eval(t_ss); + if (this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)) { + return this->children[1]->eval(t_ss); } else { - if (children.size() > 2) { - size_t i = 2; - while (i < children.size()) { - if (children[i]->text == "else") { - return children[i+1]->eval(t_ss); + return this->children[2]->eval(t_ss); + } + } + }; + + template + struct Ranged_For_AST_Node final : AST_Node_Impl { + Ranged_For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Ranged_For, std::move(t_loc), std::move(t_children)) + { assert(this->children.size() == 3); } + + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ + const auto get_function = [&t_ss](const std::string &t_name, auto &t_hint){ + uint_fast32_t hint = t_hint; + auto funs = t_ss->get_function(t_name, hint); + if (funs.first != hint) t_hint = uint_fast32_t(funs.first); + return std::move(funs.second); + }; + + 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()); + }; + + + const std::string &loop_var_name = this->children[0]->text; + Boxed_Value range_expression_result = this->children[1]->eval(t_ss); + + + const auto do_loop = [&loop_var_name, &t_ss, this](const auto &ranged_thing){ + try { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + Boxed_Value &obj = t_ss.add_get_object(loop_var_name, void_var()); + for (auto loop_var : ranged_thing) { + obj = Boxed_Value(std::move(loop_var)); + try { + this->children[2]->eval(t_ss); + } catch (detail::Continue_Loop &) { } - else if (children[i]->text == "else if") { - if (get_bool_condition(children[i+1]->eval(t_ss))) { - return children[i+2]->eval(t_ss); - } - } - i += 3; } + } catch (detail::Break_Loop &) { + // loop broken } + return void_var(); + }; + + if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::vector))) { + return do_loop(boxed_cast &>(range_expression_result)); + } else if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::map))) { + return do_loop(boxed_cast &>(range_expression_result)); + } else { + const auto range_funcs = get_function("range", m_range_loc); + const auto empty_funcs = get_function("empty", m_empty_loc); + const auto front_funcs = get_function("front", m_front_loc); + const auto pop_front_funcs = get_function("pop_front", m_pop_front_loc); + + try { + const auto range_obj = call_function(range_funcs, range_expression_result); + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + Boxed_Value &obj = t_ss.add_get_object(loop_var_name, void_var()); + while (!boxed_cast(call_function(empty_funcs, range_obj))) { + obj = call_function(front_funcs, range_obj); + try { + this->children[2]->eval(t_ss); + } catch (detail::Continue_Loop &) { + } + call_function(pop_front_funcs, range_obj); + } + } catch (detail::Break_Loop &) { + // loop broken + } + return void_var(); } - return void_var(); } + private: + mutable std::atomic_uint_fast32_t m_range_loc = {0}; + mutable std::atomic_uint_fast32_t m_empty_loc = {0}; + mutable std::atomic_uint_fast32_t m_front_loc = {0}; + mutable std::atomic_uint_fast32_t m_pop_front_loc = {0}; }; - struct For_AST_Node final : AST_Node { - For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::For, std::move(t_loc), std::move(t_children)) - { assert(children.size() == 4); } + + template + struct For_AST_Node final : AST_Node_Impl { + For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::For, std::move(t_loc), std::move(t_children)) + { assert(this->children.size() == 4); } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); try { for ( - children[0]->eval(t_ss); - get_bool_condition(children[1]->eval(t_ss)); - children[2]->eval(t_ss) + this->children[0]->eval(t_ss); + this->get_bool_condition(this->children[1]->eval(t_ss), t_ss); + this->children[2]->eval(t_ss) ) { try { // Body of Loop - children[3]->eval(t_ss); + this->children[3]->eval(t_ss); } catch (detail::Continue_Loop &) { // we got a continue exception, which means all of the remaining // loop implementation is skipped and we just need to continue to @@ -876,9 +952,10 @@ namespace chaiscript }; - struct Switch_AST_Node final : AST_Node { - Switch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Switch, std::move(t_loc), std::move(t_children)) { } + template + struct Switch_AST_Node final : AST_Node_Impl { + Switch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Switch, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { bool breaking = false; @@ -916,48 +993,51 @@ namespace chaiscript return void_var(); } - mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; - struct Case_AST_Node final : AST_Node { - Case_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Case, std::move(t_loc), std::move(t_children)) - { assert(children.size() == 2); /* how many children does it have? */ } + template + struct Case_AST_Node final : AST_Node_Impl { + Case_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Case, std::move(t_loc), std::move(t_children)) + { assert(this->children.size() == 2); /* how many children does it have? */ } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - children[1]->eval(t_ss); + this->children[1]->eval(t_ss); return void_var(); } }; - struct Default_AST_Node final : AST_Node { - Default_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Default, std::move(t_loc), std::move(t_children)) - { assert(children.size() == 1); } + template + struct Default_AST_Node final : AST_Node_Impl { + Default_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Default, std::move(t_loc), std::move(t_children)) + { assert(this->children.size() == 1); } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - children[0]->eval(t_ss); + this->children[0]->eval(t_ss); return void_var(); } }; - struct Inline_Array_AST_Node final : AST_Node { - Inline_Array_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Inline_Array, std::move(t_loc), std::move(t_children)) { } + template + struct Inline_Array_AST_Node final : AST_Node_Impl { + Inline_Array_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Inline_Array, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { try { std::vector vec; - if (!children.empty()) { - vec.reserve(children[0]->children.size()); - for (const auto &child : children[0]->children) { + if (!this->children.empty()) { + vec.reserve(this->children[0]->children.size()); + for (const auto &child : this->children[0]->children) { auto obj = child->eval(t_ss); if (!obj.is_return_value()) { vec.push_back(t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions())); @@ -973,24 +1053,21 @@ namespace chaiscript } } - std::string pretty_print() const override - { - return "[" + AST_Node::pretty_print() + "]"; - } - private: - mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; - struct Inline_Map_AST_Node final : AST_Node { - Inline_Map_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Inline_Map, std::move(t_loc), std::move(t_children)) { } + template + struct Inline_Map_AST_Node final : AST_Node_Impl { + Inline_Map_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Inline_Map, std::move(t_loc), std::move(t_children)) { } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override + { try { std::map retval; - for (const auto &child : children[0]->children) { + for (const auto &child : this->children[0]->children) { auto obj = child->children[1]->eval(t_ss); if (!obj.is_return_value()) { obj = t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions()); @@ -1007,16 +1084,17 @@ namespace chaiscript } private: - mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; - struct Return_AST_Node final : AST_Node { - Return_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Return, std::move(t_loc), std::move(t_children)) { } + template + struct Return_AST_Node final : AST_Node_Impl { + Return_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Return, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ if (!this->children.empty()) { - throw detail::Return_Value(children[0]->eval(t_ss)); + throw detail::Return_Value(this->children[0]->eval(t_ss)); } else { throw detail::Return_Value(void_var()); @@ -1024,19 +1102,20 @@ namespace chaiscript } }; - struct File_AST_Node final : AST_Node { - File_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::File, std::move(t_loc), std::move(t_children)) { } + template + struct File_AST_Node final : AST_Node_Impl { + File_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::File, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { try { - const auto num_children = children.size(); + const auto num_children = this->children.size(); if (num_children > 0) { for (size_t i = 0; i < num_children-1; ++i) { - children[i]->eval(t_ss); + this->children[i]->eval(t_ss); } - return children.back()->eval(t_ss); + return this->children.back()->eval(t_ss); } else { return void_var(); } @@ -1048,100 +1127,104 @@ namespace chaiscript } }; - struct Reference_AST_Node final : AST_Node { - Reference_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Reference, std::move(t_loc), std::move(t_children)) - { assert(children.size() == 1); } + template + struct Reference_AST_Node final : AST_Node_Impl { + Reference_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Reference, std::move(t_loc), std::move(t_children)) + { assert(this->children.size() == 1); } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ - try { - Boxed_Value bv; - t_ss.add_object(this->children[0]->text, bv); - return bv; - } - catch (const exception::reserved_word_error &) { - throw exception::eval_error("Reserved word used as variable '" + this->children[0]->text + "'"); - } + Boxed_Value bv; + t_ss.add_object(this->children[0]->text, bv); + return bv; } }; - struct Prefix_AST_Node final : AST_Node { - Prefix_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Prefix, std::move(t_loc), std::move(t_children)), - m_oper(Operators::to_operator(children[0]->text, true)) + template + struct Prefix_AST_Node final : AST_Node_Impl { + Prefix_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Prefix, std::move(t_loc), std::move(t_children)), + m_oper(Operators::to_operator(this->text, true)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ - Boxed_Value bv(children[1]->eval(t_ss)); + Boxed_Value bv(this->children[0]->eval(t_ss)); try { // short circuit arithmetic operations - if (m_oper != Operators::invalid && m_oper != Operators::bitwise_and && bv.get_type_info().is_arithmetic()) + if (m_oper != Operators::Opers::invalid && m_oper != Operators::Opers::bitwise_and && bv.get_type_info().is_arithmetic()) { 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(children[0]->text, m_loc, {std::move(bv)}, t_ss.conversions()); + return t_ss->call_function(this->text, m_loc, {std::move(bv)}, t_ss.conversions()); } } catch (const exception::dispatch_error &e) { - throw exception::eval_error("Error with prefix operator evaluation: '" + children[0]->text + "'", e.parameters, e.functions, false, *t_ss); + throw exception::eval_error("Error with prefix operator evaluation: '" + this->text + "'", e.parameters, e.functions, false, *t_ss); } } private: - Operators::Opers m_oper; - mutable std::atomic_uint_fast32_t m_loc; + Operators::Opers m_oper = Operators::Opers::invalid; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; - struct Break_AST_Node final : AST_Node { - Break_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Break, std::move(t_loc), std::move(t_children)) { } - + template + struct Break_AST_Node final : AST_Node_Impl { + Break_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Break, std::move(t_loc), std::move(t_children)) { } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override{ throw detail::Break_Loop(); } }; - struct Continue_AST_Node final : AST_Node { - Continue_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Continue, std::move(t_loc), std::move(t_children)) { } + template + struct Continue_AST_Node final : AST_Node_Impl { + Continue_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Continue, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override{ throw detail::Continue_Loop(); } }; - struct Noop_AST_Node final : public AST_Node { + template + struct Noop_AST_Node final : AST_Node_Impl { Noop_AST_Node() : - AST_Node("", AST_Node_Type::Noop, Parse_Location()) + AST_Node_Impl("", AST_Node_Type::Noop, Parse_Location()) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override{ - // It's a no-op, that evaluates to "true" - // the magic-static version of const_var(true) helps us here - return const_var(true); + // It's a no-op, that evaluates to "void" + return val; } + + Boxed_Value val = void_var(); }; - struct Map_Pair_AST_Node final : AST_Node { - Map_Pair_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Map_Pair, std::move(t_loc), std::move(t_children)) { } + template + struct Map_Pair_AST_Node final : AST_Node_Impl { + Map_Pair_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Map_Pair, std::move(t_loc), std::move(t_children)) { } }; - struct Value_Range_AST_Node final : AST_Node { - Value_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Value_Range, std::move(t_loc), std::move(t_children)) { } + template + struct Value_Range_AST_Node final : AST_Node_Impl { + Value_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Value_Range, std::move(t_loc), std::move(t_children)) { } }; - struct Inline_Range_AST_Node final : AST_Node { - Inline_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Inline_Range, std::move(t_loc), std::move(t_children)) { } + template + struct Inline_Range_AST_Node final : AST_Node_Impl { + Inline_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Inline_Range, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ try { - auto oper1 = children[0]->children[0]->children[0]->eval(t_ss); - auto oper2 = children[0]->children[0]->children[1]->eval(t_ss); + 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()); } catch (const exception::dispatch_error &e) { @@ -1150,17 +1233,13 @@ namespace chaiscript } private: - mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; - struct Annotation_AST_Node final : AST_Node { - Annotation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Annotation, std::move(t_loc)) { } - }; - - struct Try_AST_Node final : AST_Node { - Try_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Try, std::move(t_loc), std::move(t_children)) { } + template + struct Try_AST_Node final : AST_Node_Impl { + Try_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Try, std::move(t_loc), std::move(t_children)) { } Boxed_Value handle_exception(const chaiscript::detail::Dispatch_State &t_ss, const Boxed_Value &t_except) const { @@ -1173,17 +1252,17 @@ namespace chaiscript } for (size_t i = 1; i < end_point; ++i) { chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss); - AST_NodePtr catch_block = this->children[i]; + AST_Node_Impl_Ptr catch_block = this->children[i]; if (catch_block->children.size() == 1) { //No variable capture, no guards retval = catch_block->children[0]->eval(t_ss); break; } else if (catch_block->children.size() == 2 || catch_block->children.size() == 3) { - const auto name = Arg_List_AST_Node::get_arg_name(catch_block->children[0]); + const auto name = Arg_List_AST_Node::get_arg_name(catch_block->children[0]); if (dispatch::Param_Types( - std::vector>{Arg_List_AST_Node::get_arg_type(catch_block->children[0], t_ss)} + std::vector>{Arg_List_AST_Node::get_arg_type(catch_block->children[0], t_ss)} ).match(std::vector{t_except}, t_ss.conversions())) { t_ss.add_object(name, t_except); @@ -1267,47 +1346,48 @@ namespace chaiscript }; - struct Catch_AST_Node final : AST_Node { - Catch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Catch, std::move(t_loc), std::move(t_children)) { } + template + struct Catch_AST_Node final : AST_Node_Impl { + Catch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Catch, std::move(t_loc), std::move(t_children)) { } }; - struct Finally_AST_Node final : AST_Node { - Finally_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Finally, std::move(t_loc), std::move(t_children)) { } + template + struct Finally_AST_Node final : AST_Node_Impl { + Finally_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Finally, std::move(t_loc), std::move(t_children)) { } }; - struct Method_AST_Node final : AST_Node { - Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc), std::move(t_children)) { } + template + struct Method_AST_Node final : AST_Node_Impl { + Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ - AST_NodePtr guardnode; + AST_Node_Impl_Ptr guardnode; - const auto d = t_ss->get_parent_locals(); - const auto itr = d.find("_current_class_name"); - const auto class_offset = (itr != d.end())?-1:0; - const std::string & class_name = (itr != d.end())?std::string(boxed_cast(itr->second)):this->children[0]->text; + const std::string & class_name = this->children[0]->text; //The first param of a method is always the implied this ptr. std::vector t_param_names{"this"}; dispatch::Param_Types param_types; - if ((children.size() > static_cast(3 + class_offset)) && (children[static_cast(2 + class_offset)]->identifier == AST_Node_Type::Arg_List)) { - auto args = Arg_List_AST_Node::get_arg_names(children[static_cast(2 + class_offset)]); + if ((this->children.size() > 3) + && (this->children[2]->identifier == AST_Node_Type::Arg_List)) { + auto args = Arg_List_AST_Node::get_arg_names(this->children[2]); t_param_names.insert(t_param_names.end(), args.begin(), args.end()); - param_types = Arg_List_AST_Node::get_arg_types(children[static_cast(2 + class_offset)], t_ss); + param_types = Arg_List_AST_Node::get_arg_types(this->children[2], t_ss); - if (children.size() > static_cast(4 + class_offset)) { - guardnode = children[static_cast(3 + class_offset)]; + if (this->children.size() > 4) { + guardnode = this->children[3]; } } else { //no parameters - if (children.size() > static_cast(3 + class_offset)) { - guardnode = children[static_cast(2 + class_offset)]; + if (this->children.size() > 3) { + guardnode = this->children[2]; } } @@ -1324,9 +1404,8 @@ namespace chaiscript } try { - const std::string & l_annotation = annotation?annotation->text:""; - const std::string & function_name = children[static_cast(1 + class_offset)]->text; - auto node = children.back(); + const std::string & function_name = this->children[1]->text; + auto node = this->children.back(); if (function_name == class_name) { param_types.push_front(class_name, Type_Info()); @@ -1337,7 +1416,7 @@ namespace chaiscript [engine, t_param_names, node](const std::vector &t_params) { return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params); }, - static_cast(numparams), node, param_types, l_annotation, guard + static_cast(numparams), node, param_types, guard ) ), function_name); @@ -1353,12 +1432,9 @@ namespace chaiscript [engine, t_param_names, node](const std::vector &t_params) { return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params); }, - static_cast(numparams), node, param_types, l_annotation, guard), type), + static_cast(numparams), node, param_types, guard), type), function_name); } - } - catch (const exception::reserved_word_error &e) { - throw exception::eval_error("Reserved word used as method name '" + e.word() + "'"); } catch (const exception::name_conflict_error &e) { throw exception::eval_error("Method redefined '" + e.name() + "'"); } @@ -1367,19 +1443,17 @@ namespace chaiscript }; - struct Attr_Decl_AST_Node final : AST_Node { - Attr_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Attr_Decl, std::move(t_loc), std::move(t_children)) { } + template + struct Attr_Decl_AST_Node final : AST_Node_Impl { + Attr_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Attr_Decl, std::move(t_loc), std::move(t_children)) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - const auto &d = t_ss->get_parent_locals(); - const auto itr = d.find("_current_class_name"); - const auto class_offset = (itr != d.end())?-1:0; - std::string class_name = (itr != d.end())?std::string(boxed_cast(itr->second)):this->children[0]->text; + std::string class_name = this->children[0]->text; try { - std::string attr_name = this->children[static_cast(1 + class_offset)]->text; + std::string attr_name = this->children[1]->text; t_ss->add( std::make_shared( @@ -1389,11 +1463,7 @@ namespace chaiscript }), true - ), this->children[static_cast(1 + class_offset)]->text); - - } - catch (const exception::reserved_word_error &) { - throw exception::eval_error("Reserved word used as attribute '" + this->children[static_cast(1 + class_offset)]->text + "'"); + ), this->children[1]->text); } catch (const exception::name_conflict_error &e) { throw exception::eval_error("Attribute redefined '" + e.name() + "'"); } @@ -1403,39 +1473,31 @@ namespace chaiscript }; - struct Logical_And_AST_Node final : AST_Node { - Logical_And_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Logical_And, std::move(t_loc), std::move(t_children)) - { assert(children.size() == 3); } + template + struct Logical_And_AST_Node final : AST_Node_Impl { + Logical_And_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Logical_And, std::move(t_loc), std::move(t_children)) + { assert(this->children.size() == 2); } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - return const_var(get_bool_condition(children[0]->eval(t_ss)) - && get_bool_condition(children[2]->eval(t_ss))); + return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss) + && this->get_bool_condition(this->children[1]->eval(t_ss), t_ss)); } - std::string pretty_print() const override - { - return "(" + AST_Node::pretty_print() + ")"; - } }; - struct Logical_Or_AST_Node final : AST_Node { - Logical_Or_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : - AST_Node(std::move(t_ast_node_text), AST_Node_Type::Logical_Or, std::move(t_loc), std::move(t_children)) - { assert(children.size() == 3); } + template + struct Logical_Or_AST_Node final : AST_Node_Impl { + Logical_Or_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Logical_Or, std::move(t_loc), std::move(t_children)) + { assert(this->children.size() == 2); } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - return const_var(get_bool_condition(children[0]->eval(t_ss)) - || get_bool_condition(children[2]->eval(t_ss))); + return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss) + || this->get_bool_condition(this->children[1]->eval(t_ss), t_ss)); } - - std::string pretty_print() const override - { - return "(" + AST_Node::pretty_print() + ")"; - } - }; } diff --git a/include/chaiscript/language/chaiscript_optimizer.hpp b/include/chaiscript/language/chaiscript_optimizer.hpp new file mode 100644 index 00000000..125782b7 --- /dev/null +++ b/include/chaiscript/language/chaiscript_optimizer.hpp @@ -0,0 +1,427 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_OPTIMIZER_HPP_ +#define CHAISCRIPT_OPTIMIZER_HPP_ + +#include "chaiscript_eval.hpp" + + +namespace chaiscript { + namespace optimizer { + + template + struct Optimizer : T... + { + Optimizer() = default; + Optimizer(T ... t) + : T(std::move(t))... + { + } + + template + auto optimize(eval::AST_Node_Impl_Ptr p) { + (void)std::initializer_list{ (p = static_cast(*this).optimize(p), 0)... }; + return p; + } + }; + + template + auto child_at(const eval::AST_Node_Impl_Ptr &node, const size_t offset) { + if (node->children[offset]->identifier == AST_Node_Type::Compiled) { + return dynamic_cast&>(*node->children[offset]).m_original_node; + } else { + return node->children[offset]; + } + + + /* + if (node->identifier == AST_Node_Type::Compiled) { + return dynamic_cast&>(*node).m_original_node->children[offset]; + } else { + return node->children[offset]; + } + */ + } + + template + auto child_count(const eval::AST_Node_Impl_Ptr &node) { + if (node->identifier == AST_Node_Type::Compiled) { + return dynamic_cast&>(*node).m_original_node->children.size(); + } else { + return node->children.size(); + } + } + + template + auto make_compiled_node(const eval::AST_Node_Impl_Ptr &original_node, std::vector> children, Callable callable) + { + return chaiscript::make_shared, eval::Compiled_AST_Node>(original_node, std::move(children), std::move(callable)); + } + + + struct Return { + template + auto optimize(const eval::AST_Node_Impl_Ptr &p) + { + if ( (p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda) + && !p->children.empty()) + { + auto &last_child = p->children.back(); + if (last_child->identifier == AST_Node_Type::Block) { + auto &block_last_child = last_child->children.back(); + if (block_last_child->identifier == AST_Node_Type::Return) { + if (block_last_child->children.size() == 1) { + last_child->children.back() = block_last_child->children[0]; + } + } + } + } + + return p; + } + }; + + template + bool contains_var_decl_in_scope(const T &node) + { + if (node->identifier == AST_Node_Type::Var_Decl) { + return true; + } + + const auto num = child_count(node); + + for (size_t i = 0; i < num; ++i) { + const auto &child = child_at(node, i); + if (child->identifier != AST_Node_Type::Block + && child->identifier != AST_Node_Type::For + && contains_var_decl_in_scope(child)) { + return true; + } + } + + return false; + } + + struct Block { + template + auto optimize(const eval::AST_Node_Impl_Ptr &node) { + if (node->identifier == AST_Node_Type::Block) + { + if (!contains_var_decl_in_scope(node)) + { + if (node->children.size() == 1) { + return node->children[0]; + } else { + return chaiscript::make_shared, eval::Scopeless_Block_AST_Node>(node->text, node->location, node->children); + } + } + } + + return node; + } + }; + + struct Dead_Code { + template + auto optimize(const eval::AST_Node_Impl_Ptr &node) { + if (node->identifier == AST_Node_Type::Block) + { + std::vector keepers; + const auto num_children = node->children.size(); + keepers.reserve(num_children); + + for (size_t i = 0; i < num_children; ++i) { + auto child = node->children[i]; + if ( (child->identifier != AST_Node_Type::Id + && child->identifier != AST_Node_Type::Constant + && child->identifier != AST_Node_Type::Noop) + || i == num_children - 1) { + keepers.push_back(i); + } + } + + if (keepers.size() == num_children) { + return node; + } else { + std::vector> new_children; + for (const auto x : keepers) + { + new_children.push_back(node->children[x]); + } + return chaiscript::make_shared, eval::Block_AST_Node>(node->text, node->location, new_children); + } + } else { + return node; + } + } + }; + + struct Unused_Return { + template + auto optimize(const eval::AST_Node_Impl_Ptr &node) { + if ((node->identifier == AST_Node_Type::Block + || node->identifier == AST_Node_Type::Scopeless_Block) + && node->children.size() > 0) + { + for (size_t i = 0; i < node->children.size()-1; ++i) { + auto child = node->children[i]; + if (child->identifier == AST_Node_Type::Fun_Call) { + node->children[i] = chaiscript::make_shared, eval::Unused_Return_Fun_Call_AST_Node>(child->text, child->location, std::move(child->children)); + } + } + } else if ((node->identifier == AST_Node_Type::For + || node->identifier == AST_Node_Type::While) + && child_count(node) > 0) { + auto child = child_at(node, child_count(node) - 1); + if (child->identifier == AST_Node_Type::Block + || child->identifier == AST_Node_Type::Scopeless_Block) + { + auto num_sub_children = child_count(child); + for (size_t i = 0; i < num_sub_children; ++i) { + auto sub_child = child_at(child, i); + if (sub_child->identifier == AST_Node_Type::Fun_Call) { + child->children[i] = chaiscript::make_shared, eval::Unused_Return_Fun_Call_AST_Node>(sub_child->text, sub_child->location, std::move(sub_child->children)); + } + } + } + } + return node; + } + }; + + struct If { + template + auto optimize(const eval::AST_Node_Impl_Ptr &node) { + if ((node->identifier == AST_Node_Type::If || node->identifier == AST_Node_Type::Ternary_Cond) + && node->children.size() >= 2 + && node->children[0]->identifier == AST_Node_Type::Constant) + { + const auto condition = std::dynamic_pointer_cast>(node->children[0])->m_value; + if (condition.get_type_info().bare_equal_type_info(typeid(bool))) { + if (boxed_cast(condition)) { + return node->children[1]; + } else if (node->children.size() == 3) { + return node->children[2]; + } + } + } + + return node; + } + }; + + struct Partial_Fold { + template + auto optimize(const eval::AST_Node_Impl_Ptr &node) { + + // Fold right side + if (node->identifier == AST_Node_Type::Binary + && node->children.size() == 2 + && node->children[0]->identifier != AST_Node_Type::Constant + && node->children[1]->identifier == AST_Node_Type::Constant) + { + try { + const auto &oper = node->text; + const auto parsed = Operators::to_operator(oper); + if (parsed != Operators::Opers::invalid) { + const auto rhs = std::dynamic_pointer_cast>(node->children[1])->m_value; + if (rhs.get_type_info().is_arithmetic()) { + return chaiscript::make_shared, eval::Fold_Right_Binary_Operator_AST_Node>(node->text, node->location, node->children, rhs); + } + } + } catch (const std::exception &) { + //failure to fold, that's OK + } + } + + return node; + } + }; + + struct Constant_Fold { + template + auto optimize(const eval::AST_Node_Impl_Ptr &node) { + + if (node->identifier == AST_Node_Type::Prefix + && node->children.size() == 1 + && node->children[0]->identifier == AST_Node_Type::Constant) + { + try { + const auto &oper = node->text; + const auto parsed = Operators::to_operator(oper, true); + const auto lhs = std::dynamic_pointer_cast>(node->children[0])->m_value; + const auto match = oper + node->children[0]->text; + + if (parsed != Operators::Opers::invalid && parsed != Operators::Opers::bitwise_and && lhs.get_type_info().is_arithmetic()) { + const auto val = Boxed_Number::do_oper(parsed, lhs); + return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); + } else if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && oper == "!") { + return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, Boxed_Value(!boxed_cast(lhs))); + } + } catch (const std::exception &) { + //failure to fold, that's OK + } + } else if ((node->identifier == AST_Node_Type::Logical_And || node->identifier == AST_Node_Type::Logical_Or) + && node->children.size() == 2 + && node->children[0]->identifier == AST_Node_Type::Constant + && node->children[1]->identifier == AST_Node_Type::Constant) + { + try { + const auto lhs = std::dynamic_pointer_cast>(node->children[0])->m_value; + const auto rhs = std::dynamic_pointer_cast>(node->children[1])->m_value; + if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && rhs.get_type_info().bare_equal_type_info(typeid(bool))) { + const auto match = node->children[0]->text + " " + node->text + " " + node->children[1]->text; + const auto val = [lhs_val = boxed_cast(lhs), rhs_val = boxed_cast(rhs), id = node->identifier] { + if (id == AST_Node_Type::Logical_And) { return Boxed_Value(lhs_val && rhs_val); } + else { return Boxed_Value(lhs_val || rhs_val); } + }(); + + return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); + } + } catch (const std::exception &) { + //failure to fold, that's OK + } + } else if (node->identifier == AST_Node_Type::Binary + && node->children.size() == 2 + && node->children[0]->identifier == AST_Node_Type::Constant + && node->children[1]->identifier == AST_Node_Type::Constant) + { + try { + const auto &oper = node->text; + const auto parsed = Operators::to_operator(oper); + if (parsed != Operators::Opers::invalid) { + const auto lhs = std::dynamic_pointer_cast>(node->children[0])->m_value; + const auto rhs = std::dynamic_pointer_cast>(node->children[1])->m_value; + if (lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) { + const auto val = Boxed_Number::do_oper(parsed, lhs, rhs); + const auto match = node->children[0]->text + " " + oper + " " + node->children[1]->text; + return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); + } + } + } catch (const std::exception &) { + //failure to fold, that's OK + } + } else if (node->identifier == AST_Node_Type::Fun_Call + && node->children.size() == 2 + && node->children[0]->identifier == AST_Node_Type::Id + && node->children[1]->identifier == AST_Node_Type::Arg_List + && node->children[1]->children.size() == 1 + && node->children[1]->children[0]->identifier == AST_Node_Type::Constant) { + + const auto arg = std::dynamic_pointer_cast>(node->children[1]->children[0])->m_value; + if (arg.get_type_info().is_arithmetic()) { + const auto &fun_name = node->children[0]->text; + + const auto make_constant = [&node, &fun_name](auto val){ + const auto match = fun_name + "(" + node->children[1]->children[0]->text + ")"; + return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, Boxed_Value(val)); + }; + + if (fun_name == "double") { + return make_constant(Boxed_Number(arg).get_as()); + } else if (fun_name == "int") { + return make_constant(Boxed_Number(arg).get_as()); + } else if (fun_name == "float") { + return make_constant(Boxed_Number(arg).get_as()); + } else if (fun_name == "long") { + return make_constant(Boxed_Number(arg).get_as()); + } else if (fun_name == "size_t") { + return make_constant(Boxed_Number(arg).get_as()); + } + + + } + + } + + return node; + } + }; + + struct For_Loop { + template + auto optimize(const eval::AST_Node_Impl_Ptr &for_node) { + + if (for_node->identifier != AST_Node_Type::For) { + return for_node; + } + + const auto eq_node = child_at(for_node, 0); + const auto binary_node = child_at(for_node, 1); + const auto prefix_node = child_at(for_node, 2); + + if (eq_node->identifier == AST_Node_Type::Equation + && child_count(eq_node) == 2 + && child_at(eq_node, 0)->identifier == AST_Node_Type::Var_Decl + && child_at(eq_node, 1)->identifier == AST_Node_Type::Constant + && binary_node->identifier == AST_Node_Type::Binary + && binary_node->text == "<" + && child_count(binary_node) == 2 + && child_at(binary_node, 0)->identifier == AST_Node_Type::Id + && child_at(binary_node, 0)->text == child_at(child_at(eq_node,0), 0)->text + && child_at(binary_node, 1)->identifier == AST_Node_Type::Constant + && prefix_node->identifier == AST_Node_Type::Prefix + && prefix_node->text == "++" + && child_count(prefix_node) == 1 + && child_at(prefix_node, 0)->identifier == AST_Node_Type::Id + && child_at(prefix_node, 0)->text == child_at(child_at(eq_node,0), 0)->text) + { + const Boxed_Value &begin = std::dynamic_pointer_cast>(child_at(eq_node, 1))->m_value; + const Boxed_Value &end = std::dynamic_pointer_cast>(child_at(binary_node, 1))->m_value; + const std::string &id = child_at(prefix_node, 0)->text; + + if (begin.get_type_info().bare_equal(user_type()) + && end.get_type_info().bare_equal(user_type())) { + + const auto start_int = boxed_cast(begin); + const auto end_int = boxed_cast(end); + + const auto body = child_at(for_node, 3); + + return make_compiled_node(for_node, {body}, + [id, start_int, end_int](const std::vector> &children, const chaiscript::detail::Dispatch_State &t_ss) { + assert(children.size() == 1); + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + int i = start_int; + t_ss.add_object(id, var(&i)); + + try { + for (; i < end_int; ++i) { + try { + // Body of Loop + children[0]->eval(t_ss); + } catch (eval::detail::Continue_Loop &) { + // we got a continue exception, which means all of the remaining + // loop implementation is skipped and we just need to continue to + // the next iteration step + } + } + } catch (eval::detail::Break_Loop &) { + // loop broken + } + + return void_var(); + } + ); + } else { + return for_node; + } + } else { + return for_node; + } + } + }; + + typedef Optimizer Optimizer_Default; + + } +} + + +#endif diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 1ea73716..7c694efb 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -18,9 +18,16 @@ + #include "../dispatchkit/boxed_value.hpp" #include "chaiscript_common.hpp" +#include "chaiscript_optimizer.hpp" +#include "chaiscript_tracer.hpp" +#if defined(CHAISCRIPT_UTF16_UTF32) +#include +#include +#endif #if defined(CHAISCRIPT_MSVC) && defined(max) && defined(min) #define CHAISCRIPT_PUSHED_MIN_MAX @@ -55,9 +62,61 @@ namespace chaiscript , max_alphabet , lengthof_alphabet = 256 }; + + // Generic for u16, u32 and wchar + template + struct Char_Parser_Helper + { + // common for all implementations + static std::string u8str_from_ll(long long val) + { + typedef std::string::value_type char_type; + + char_type c[2]; + c[1] = char_type(val); + c[0] = char_type(val >> 8); + + if (c[0] == 0) + { + return std::string(1, c[1]); // size, character + } + + return std::string(c, 2); // char buffer, size + } + + static string_type str_from_ll(long long val) + { + typedef typename string_type::value_type target_char_type; +#if defined (CHAISCRIPT_UTF16_UTF32) + // prepare converter + std::wstring_convert, target_char_type> converter; + // convert + return converter.from_bytes(u8str_from_ll(val)); +#else + // no conversion available, just put value as character + return string_type(1, target_char_type(val)); // size, character +#endif + } + }; + + // Specialization for char AKA UTF-8 + template<> + struct Char_Parser_Helper + { + static std::string str_from_ll(long long val) + { + // little SFINAE trick to avoid base class + return Char_Parser_Helper::u8str_from_ll(val); + } + }; } - class ChaiScript_Parser { + + template + class ChaiScript_Parser final : public ChaiScript_Parser_Base { + void *get_tracer_ptr() override { + return &m_tracer; + } static std::array, detail::max_alphabet> build_alphabet() { @@ -140,26 +199,28 @@ namespace chaiscript {"<<", ">>"}, //We share precedence here but then separate them later {"+", "-"}, - {"*", "/", "%"} + {"*", "/", "%"}, + {"++", "--", "-", "+", "!", "~"} }; return operator_matches; } - static const std::array &create_operators() { - static const std::array operators = { { - AST_Node_Type::Ternary_Cond, - AST_Node_Type::Logical_Or, - AST_Node_Type::Logical_And, - AST_Node_Type::Bitwise_Or, - AST_Node_Type::Bitwise_Xor, - AST_Node_Type::Bitwise_And, - AST_Node_Type::Equality, - AST_Node_Type::Comparison, - AST_Node_Type::Shift, - AST_Node_Type::Addition, - AST_Node_Type::Multiplication + static const std::array &create_operators() { + static const 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 } }; return operators; } @@ -167,19 +228,19 @@ namespace chaiscript static constexpr const char * const m_multiline_comment_begin = "/*"; static constexpr const char * const m_multiline_comment_end = "*/"; static constexpr const char * const m_singleline_comment = "//"; + static constexpr const char * const m_annotation = "#"; const std::array, detail::max_alphabet> &m_alphabet = create_alphabet(); const std::vector> &m_operator_matches = create_operator_matches(); - const std::array &m_operators = create_operators(); + const std::array &m_operators = create_operators(); std::shared_ptr m_filename; - std::vector m_match_stack; + std::vector> m_match_stack; struct Position { - Position() - : line(-1), col(-1), m_last_col(-1) {} + Position() = default; Position(std::string::const_iterator t_pos, std::string::const_iterator t_end) : line(1), col(1), m_pos(std::move(t_pos)), m_end(std::move(t_end)), m_last_col(1) @@ -194,8 +255,7 @@ namespace chaiscript if (m_pos != m_end) { if (*m_pos == '\n') { ++line; - m_last_col = col; - col = 1; + m_last_col = std::exchange(col, 1); } else { ++col; } @@ -266,130 +326,52 @@ namespace chaiscript } } - int line; - int col; + int line = -1; + int col = -1; private: std::string::const_iterator m_pos; std::string::const_iterator m_end; - int m_last_col; + int m_last_col = -1; }; Position m_position; + Tracer m_tracer; + Optimizer m_optimizer; + + void validate_object_name(const std::string &name) const + { + if (!Name_Validator::valid_object_name(name)) { + throw exception::eval_error("Invalid Object Name: " + name, File_Position(m_position.line, m_position.col), *m_filename); + } + } + public: - ChaiScript_Parser() + explicit ChaiScript_Parser(Tracer tracer = Tracer(), Optimizer optimizer=Optimizer()) + : m_tracer(std::move(tracer)), + m_optimizer(std::move(optimizer)) { m_match_stack.reserve(2); } - ChaiScript_Parser(const ChaiScript_Parser &) = delete; + ChaiScript_Parser(const ChaiScript_Parser &) = default; ChaiScript_Parser &operator=(const ChaiScript_Parser &) = delete; - + ChaiScript_Parser(ChaiScript_Parser &&) = default; + ChaiScript_Parser &operator=(ChaiScript_Parser &&) = delete; /// test a char in an m_alphabet bool char_in_alphabet(char c, detail::Alphabet a) const { return m_alphabet[a][static_cast(c)]; } /// Prints the parsed ast_nodes as a tree - void debug_print(AST_NodePtr t, std::string prepend = "") const { + void debug_print(AST_NodePtr t, std::string prepend = "") const override { std::cout << prepend << "(" << ast_node_type_to_string(t->identifier) << ") " << t->text << " : " << t->start().line << ", " << t->start().column << '\n'; - for (unsigned int j = 0; j < t->children.size(); ++j) { - debug_print(t->children[j], prepend + " "); + for (const auto &node : t->get_children()) { + debug_print(node, prepend + " "); } } - /// Shows the current stack of matched ast_nodes - void show_match_stack() const { - for (auto & elem : m_match_stack) { - //debug_print(match_stack[i]); - std::cout << elem->to_string(); - } - } - - /// Clears the stack of matched ast_nodes - void clear_match_stack() { - m_match_stack.clear(); - } - - /// Returns the front-most AST node - AST_NodePtr ast() const { - if (m_match_stack.empty()) throw exception::eval_error("Attempted to access AST of failed parse."); - return m_match_stack.front(); - } - - static std::map count_fun_calls(const AST_NodePtr &p, bool in_loop) { - if (p->identifier == AST_Node_Type::Fun_Call) { - if (p->children[0]->identifier == AST_Node_Type::Id) { - return std::map{ {p->children[0]->text, in_loop?99:1} }; - } - return std::map(); - } else { - std::map counts; - for (const auto &child : p->children) { - auto childcounts = count_fun_calls(child, in_loop || p->identifier == AST_Node_Type::For || p->identifier == AST_Node_Type::While); - for (const auto &count : childcounts) { - counts[count.first] += count.second; - } - } - return counts; - } - - } - - - static void optimize_blocks(AST_NodePtr &p) - { - for (auto &c : p->children) - { - if (c->identifier == AST_Node_Type::Block) { - if (c->children.size() == 1) { - // std::cout << "swapping out block child for block\n"; - c = c->children[0]; - } - } - optimize_blocks(c); - } - } - - static void optimize_returns(AST_NodePtr &p) - { - for (auto &c : p->children) - { - if (c->identifier == AST_Node_Type::Def && c->children.size() > 0) { - auto &last_child = c->children.back(); - if (last_child->identifier == AST_Node_Type::Block) { - auto &block_last_child = last_child->children.back(); - if (block_last_child->identifier == AST_Node_Type::Return) { - if (block_last_child->children.size() == 1) { - block_last_child = block_last_child->children[0]; - } - } - } - } - optimize_returns(c); - } - } - - - static int count_nodes(const AST_NodePtr &p) - { - int count = 1; - for (auto &c : p->children) { - count += count_nodes(c); - } - return count; - } - - AST_NodePtr optimized_ast(bool t_optimize_blocks = false, bool t_optimize_returns = true) { - AST_NodePtr p = ast(); - //Note, optimize_blocks is currently broken; it breaks stack management - if (t_optimize_blocks) { optimize_blocks(p); } - if (t_optimize_returns) { optimize_returns(p); } - return p; - } - - /// Helper function that collects ast_nodes from a starting position to the top of the stack into a new AST node template void build_match(size_t t_match_start, std::string t_text = "") { @@ -417,7 +399,7 @@ namespace chaiscript } }(); - std::vector new_children; + std::vector> new_children; if (is_deep) { new_children.assign(std::make_move_iterator(m_match_stack.begin() + static_cast(t_match_start)), @@ -427,10 +409,12 @@ namespace chaiscript /// \todo fix the fact that a successful match that captured no ast_nodes doesn't have any real start position m_match_stack.push_back( - chaiscript::make_shared( - std::move(t_text), - std::move(filepos), - std::move(new_children))); + m_optimizer.optimize( + chaiscript::make_shared, NodeType>( + std::move(t_text), + std::move(filepos), + std::move(new_children))) + ); } @@ -458,6 +442,19 @@ namespace chaiscript } } return true; + } else if (Symbol_(m_annotation)) { + while (m_position.has_more()) { + if (Symbol_("\r\n")) { + m_position -= 2; + break; + } else if (Char_('\n')) { + --m_position; + break; + } else { + ++m_position; + } + } + return true; } return false; } @@ -724,9 +721,9 @@ namespace chaiscript } template - std::shared_ptr make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param) + std::shared_ptr> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param) { - return chaiscript::make_shared(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward(param)...); + return chaiscript::make_shared, T>(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward(param)...); } /// Reads a number from the input, detecting if it's an integer or floating point @@ -742,20 +739,20 @@ namespace chaiscript if (Hex_()) { auto match = Position::str(start, m_position); auto bv = buildInt(16, match, true); - m_match_stack.emplace_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + m_match_stack.emplace_back(make_node>(std::move(match), start.line, start.col, std::move(bv))); return true; } if (Binary_()) { auto match = Position::str(start, m_position); auto bv = buildInt(2, match, true); - m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + m_match_stack.push_back(make_node>(std::move(match), start.line, start.col, std::move(bv))); return true; } if (Float_()) { auto match = Position::str(start, m_position); auto bv = buildFloat(match); - m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + m_match_stack.push_back(make_node>(std::move(match), start.line, start.col, std::move(bv))); return true; } else { @@ -763,11 +760,11 @@ namespace chaiscript auto match = Position::str(start, m_position); if (!match.empty() && (match[0] == '0')) { auto bv = buildInt(8, match, false); - m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + m_match_stack.push_back(make_node>(std::move(match), start.line, start.col, std::move(bv))); } else if (!match.empty()) { auto bv = buildInt(10, match, false); - m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + m_match_stack.push_back(make_node>(std::move(match), start.line, start.col, std::move(bv))); } else { return false; } @@ -820,21 +817,80 @@ namespace chaiscript } /// Reads (and potentially captures) an identifier from input - bool Id() { + bool Id(const bool validate) { SkipWS(); const auto start = m_position; if (Id_()) { - m_match_stack.push_back(make_node( - [&]()->std::string{ - if (*start == '`') { - //Id Literal - return Position::str(start+1, m_position-1); - } else { - return Position::str(start, m_position); - } - }(), - start.line, start.col)); + + const auto text = Position::str(start, m_position); + + if (validate) { + validate_object_name(text); + } + + if (text == "true") { + m_match_stack.push_back(make_node>(text, start.line, start.col, const_var(true))); + } else if (text == "false") { + m_match_stack.push_back(make_node>(text, start.line, start.col, const_var(false))); + } else if (text == "Infinity") { + m_match_stack.push_back(make_node>(text, start.line, start.col, + const_var(std::numeric_limits::infinity()))); + } else if (text == "NaN") { + m_match_stack.push_back(make_node>(text, start.line, start.col, + const_var(std::numeric_limits::quiet_NaN()))); + } else if (text == "__LINE__") { + m_match_stack.push_back(make_node>(text, start.line, start.col, + const_var(start.line))); + } else if (text == "__FILE__") { + m_match_stack.push_back(make_node>(text, start.line, start.col, + const_var(m_filename))); + } else if (text == "__FUNC__") { + const std::string fun_name = [&]()->std::string{ + for (size_t idx = m_match_stack.size() - 1; idx > 0; --idx) + { + if (m_match_stack[idx-1]->identifier == AST_Node_Type::Id + && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) { + return m_match_stack[idx-1]->text; + } + } + return "NOT_IN_FUNCTION"; + }(); + + m_match_stack.push_back(make_node>(text, start.line, start.col, + const_var(std::move(fun_name)))); + } else if (text == "__CLASS__") { + const std::string fun_name = [&]()->std::string{ + for (size_t idx = m_match_stack.size() - 1; idx > 1; --idx) + { + if (m_match_stack[idx-2]->identifier == AST_Node_Type::Id + && m_match_stack[idx-1]->identifier == AST_Node_Type::Id + && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) { + return m_match_stack[idx-2]->text; + } + } + return "NOT_IN_CLASS"; + }(); + + m_match_stack.push_back(make_node>(text, start.line, start.col, + const_var(std::move(fun_name)))); + } else if (text == "_") { + m_match_stack.push_back(make_node>(text, start.line, start.col, + Boxed_Value(std::make_shared()))); + } else { + m_match_stack.push_back(make_node>( + [&]()->std::string{ + if (*start == '`') { + // 'escaped' literal, like an operator name + return Position::str(start+1, m_position-1); + } else { + return text; + } + }(), + start.line, start.col)); + } + + return true; } else { return false; @@ -846,48 +902,23 @@ namespace chaiscript const auto prev_stack_top = m_match_stack.size(); SkipWS(); - if (!Id()) { + if (!Id(true)) { return false; } SkipWS(); if (t_type_allowed) { - Id(); + Id(true); } - build_match(prev_stack_top); + build_match>(prev_stack_top); return true; } - /// Checks for a node annotation of the form "#" - bool Annotation() { - SkipWS(); - const auto start = m_position; - if (Symbol_("#")) { - do { - while (m_position.has_more()) { - if (Eol_()) { - break; - } - else { - ++m_position; - } - } - } while (Symbol("#")); - - auto match = Position::str(start, m_position); - m_match_stack.push_back(make_node(std::move(match), start.line, start.col)); - return true; - } - else { - return false; - } - } - /// Reads a quoted string from input, without skipping initial whitespace bool Quoted_String_() { if (m_position.has_more() && (*m_position == '\"')) { @@ -933,11 +964,12 @@ namespace chaiscript { string_type &match; typedef typename string_type::value_type char_type; - bool is_escaped; - bool is_interpolated; - bool saw_interpolation_marker; - bool is_octal; - bool is_hex; + bool is_escaped = false; + bool is_interpolated = false; + bool saw_interpolation_marker = false; + bool is_octal = false; + bool is_hex = false; + bool is_unicode = false; const bool interpolation_allowed; string_type octal_matches; @@ -945,11 +977,6 @@ namespace chaiscript Char_Parser(string_type &t_match, const bool t_interpolation_allowed) : match(t_match), - is_escaped(false), - is_interpolated(false), - saw_interpolation_marker(false), - is_octal(false), - is_hex(false), interpolation_allowed(t_interpolation_allowed) { } @@ -964,6 +991,10 @@ namespace chaiscript if (is_hex) { process_hex(); } + + if (is_unicode) { + process_unicode(); + } } void process_hex() @@ -985,9 +1016,23 @@ namespace chaiscript is_octal = false; } + + void process_unicode() + { + auto val = stoll(hex_matches, 0, 16); + hex_matches.clear(); + match += detail::Char_Parser_Helper::str_from_ll(val); + is_escaped = false; + is_unicode = false; + } + void parse(const char_type t_char, const int line, const int col, const std::string &filename) { const bool is_octal_char = t_char >= '0' && t_char <= '7'; + const bool is_hex_char = (t_char >= '0' && t_char <= '9') + || (t_char >= 'a' && t_char <= 'f') + || (t_char >= 'A' && t_char <= 'F'); + if (is_octal) { if (is_octal_char) { octal_matches.push_back(t_char); @@ -1000,10 +1045,6 @@ namespace chaiscript process_octal(); } } else if (is_hex) { - const bool is_hex_char = (t_char >= '0' && t_char <= '9') - || (t_char >= 'a' && t_char <= 'f') - || (t_char >= 'A' && t_char <= 'F'); - if (is_hex_char) { hex_matches.push_back(t_char); @@ -1018,6 +1059,21 @@ namespace chaiscript } else { process_hex(); } + } else if (is_unicode) { + if (is_hex_char) { + hex_matches.push_back(t_char); + + if(hex_matches.size() == 4) { + // Format is specified to be 'slash'uABCD + // on collecting from A to D do parsing + process_unicode(); + } + return; + } else { + // Not a unicode anymore, try parsing any way + // May be someone used 'slash'uAA only + process_unicode(); + } } if (t_char == '\\') { @@ -1034,6 +1090,8 @@ namespace chaiscript octal_matches.push_back(t_char); } else if (t_char == 'x') { is_hex = true; + } else if (t_char == 'u') { + is_unicode = true; } else { switch (t_char) { case ('\'') : match.push_back('\''); break; @@ -1086,13 +1144,11 @@ namespace chaiscript if (*s == '{') { //We've found an interpolation point + m_match_stack.push_back(make_node>(match, start.line, start.col, const_var(match))); + if (cparser.is_interpolated) { //If we've seen previous interpolation, add on instead of making a new one - m_match_stack.push_back(make_node(match, start.line, start.col)); - - build_match(prev_stack_top, "+"); - } else { - m_match_stack.push_back(make_node(match, start.line, start.col)); + build_match>(prev_stack_top, "+"); } //We've finished with the part of the string up to this point, so clear it @@ -1112,21 +1168,19 @@ namespace chaiscript const auto tostr_stack_top = m_match_stack.size(); - m_match_stack.push_back(make_node("to_string", start.line, start.col)); + m_match_stack.push_back(make_node>("to_string", start.line, start.col)); const auto ev_stack_top = m_match_stack.size(); try { - ChaiScript_Parser parser; - parser.parse(eval_match, "instr eval"); - m_match_stack.push_back(parser.ast()); + m_match_stack.push_back(parse_instr_eval(eval_match)); } catch (const exception::eval_error &e) { throw exception::eval_error(e.what(), File_Position(start.line, start.col), *m_filename); } - build_match(ev_stack_top); - build_match(tostr_stack_top); - build_match(prev_stack_top, "+"); + build_match>(ev_stack_top); + build_match>(tostr_stack_top); + build_match>(prev_stack_top, "+"); } else { throw exception::eval_error("Unclosed in-string eval", File_Position(start.line, start.col), *m_filename); } @@ -1143,13 +1197,12 @@ namespace chaiscript return cparser.is_interpolated; }(); - if (is_interpolated) { - m_match_stack.push_back(make_node(match, start.line, start.col)); + m_match_stack.push_back(make_node>(match, start.line, start.col, const_var(match))); - build_match(prev_stack_top, "+"); - } else { - m_match_stack.push_back(make_node(match, start.line, start.col)); + if (is_interpolated) { + build_match>(prev_stack_top, "+"); } + return true; } else { return false; @@ -1209,7 +1262,7 @@ namespace chaiscript throw exception::eval_error("Single-quoted strings must be 1 character long", File_Position(m_position.line, m_position.col), *m_filename); } - m_match_stack.push_back(make_node(match, start.line, start.col)); + m_match_stack.push_back(make_node>(match, start.line, start.col, const_var(char(match.at(0))))); return true; } else { @@ -1229,20 +1282,9 @@ namespace chaiscript } /// Reads (and potentially captures) a char from input if it matches the parameter - bool Char(const char t_c, bool t_capture = false) { + bool Char(const char t_c) { SkipWS(); - - if (!t_capture) { - return Char_(t_c); - } else { - const auto start = m_position; - if (Char_(t_c)) { - m_match_stack.push_back(make_node(Position::str(start, m_position), start.line, start.col)); - return true; - } else { - return false; - } - } + return Char_(t_c); } /// Reads a string from input if it matches the parameter, without skipping initial whitespace @@ -1265,7 +1307,7 @@ namespace chaiscript } /// Reads (and potentially captures) a string from input if it matches the parameter - bool Keyword(const char *t_s, bool t_capture = false) { + bool Keyword(const char *t_s) { SkipWS(); const auto start = m_position; bool retval = Keyword_(t_s); @@ -1275,9 +1317,6 @@ namespace chaiscript retval = false; } - if ( t_capture && retval ) { - m_match_stack.push_back(make_node(Position::str(start, m_position), start.line, start.col)); - } return retval; } @@ -1311,7 +1350,7 @@ namespace chaiscript } /// Reads (and potentially captures) a symbol group from input if it matches the parameter - bool Symbol(const char *t_s, const bool t_capture = false, const bool t_disallow_prevention=false) { + bool Symbol(const char *t_s, const bool t_disallow_prevention=false) { SkipWS(); const auto start = m_position; bool retval = Symbol_(t_s); @@ -1326,10 +1365,6 @@ namespace chaiscript } } - if ( t_capture && retval ) { - m_match_stack.push_back(make_node(Position::str(start, m_position), start.line, start.col)); - } - return retval; } @@ -1372,16 +1407,15 @@ namespace chaiscript if (Arg(false)) { retval = true; while (Eol()) {} - if (Char(',')) { - do { - while (Eol()) {} - if (!Arg(false)) { - throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); - } - } while (Char(',')); - } + + while (Char(',')) { + while (Eol()) {} + if (!Arg(false)) { + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); + } + } } - build_match(prev_stack_top); + build_match>(prev_stack_top); SkipWS(true); @@ -1398,16 +1432,15 @@ namespace chaiscript if (Arg()) { retval = true; while (Eol()) {} - if (Char(',')) { - do { - while (Eol()) {} - if (!Arg()) { - throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); - } - } while (Char(',')); + + while (Char(',')) { + while (Eol()) {} + if (!Arg()) { + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); + } } } - build_match(prev_stack_top); + build_match>(prev_stack_top); SkipWS(true); @@ -1425,17 +1458,15 @@ namespace chaiscript if (Equation()) { retval = true; while (Eol()) {} - if (Char(',')) { - do { - while (Eol()) {} - if (!Equation()) { - throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); - } - } while (Char(',')); + while (Char(',')) { + while (Eol()) {} + if (!Equation()) { + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); + } } } - build_match(prev_stack_top); + build_match>(prev_stack_top); SkipWS(true); @@ -1451,31 +1482,27 @@ namespace chaiscript if (Value_Range()) { retval = true; - build_match(prev_stack_top); + build_match>(prev_stack_top); } else if (Map_Pair()) { retval = true; while (Eol()) {} - if (Char(',')) { - do { - while (Eol()) {} - if (!Map_Pair()) { - throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename); - } - } while (Char(',')); + while (Char(',')) { + while (Eol()) {} + if (!Map_Pair()) { + throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename); + } } - build_match(prev_stack_top); + build_match>(prev_stack_top); } else if (Operator()) { retval = true; while (Eol()) {} - if (Char(',')) { - do { - while (Eol()) {} - if (!Operator()) { - throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename); - } - } while (Char(',')); + while (Char(',')) { + while (Eol()) {} + if (!Operator()) { + throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename); + } } - build_match(prev_stack_top); + build_match>(prev_stack_top); } SkipWS(true); @@ -1499,7 +1526,7 @@ namespace chaiscript } } else { // make sure we always have the same number of nodes - build_match(prev_stack_top); + build_match>(prev_stack_top); } if (Char('(')) { @@ -1518,39 +1545,36 @@ namespace chaiscript throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } return retval; } /// Reads a function definition from input - bool Def(const bool t_class_context = false) { + bool Def(const bool t_class_context = false, const std::string &t_class_name = "") { bool retval = false; - AST_NodePtr annotation; - - if (Annotation()) { - while (Eol_()) {} - annotation = m_match_stack.back(); - m_match_stack.pop_back(); - } const auto prev_stack_top = m_match_stack.size(); if (Keyword("def")) { retval = true; - if (!Id()) { + if (t_class_context) { + m_match_stack.push_back(make_node>(t_class_name, m_position.line, m_position.col)); + } + + if (!Id(true)) { throw exception::eval_error("Missing function name in definition", File_Position(m_position.line, m_position.col), *m_filename); } bool is_method = false; - if (Symbol("::", false)) { + if (Symbol("::")) { //We're now a method is_method = true; - if (!Id()) { + if (!Id(true)) { throw exception::eval_error("Missing method name in definition", File_Position(m_position.line, m_position.col), *m_filename); } } @@ -1576,14 +1600,11 @@ namespace chaiscript } if (is_method || t_class_context) { - build_match(prev_stack_top); + build_match>(prev_stack_top); } else { - build_match(prev_stack_top); + build_match>(prev_stack_top); } - if (annotation) { - m_match_stack.back()->annotation = std::move(annotation); - } } return retval; @@ -1608,7 +1629,7 @@ namespace chaiscript while (has_matches) { while (Eol()) {} has_matches = false; - if (Keyword("catch", false)) { + if (Keyword("catch")) { const auto catch_stack_top = m_match_stack.size(); if (Char('(')) { if (!(Arg() && Char(')'))) { @@ -1626,12 +1647,12 @@ namespace chaiscript if (!Block()) { throw exception::eval_error("Incomplete 'catch' block", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(catch_stack_top); + build_match>(catch_stack_top); has_matches = true; } } while (Eol()) {} - if (Keyword("finally", false)) { + if (Keyword("finally")) { const auto finally_stack_top = m_match_stack.size(); while (Eol()) {} @@ -1639,10 +1660,10 @@ namespace chaiscript if (!Block()) { throw exception::eval_error("Incomplete 'finally' block", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(finally_stack_top); + build_match>(finally_stack_top); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } return retval; @@ -1661,7 +1682,13 @@ namespace chaiscript throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename); } - if (!(Operator() && Char(')'))) { + if (!Equation()) { + throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + const bool is_if_init = Eol() && Equation(); + + if (!Char(')')) { throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename); } @@ -1675,25 +1702,8 @@ namespace chaiscript while (has_matches) { while (Eol()) {} has_matches = false; - if (Keyword("else", true)) { - if (Keyword("if")) { - const AST_NodePtr back(m_match_stack.back()); - m_match_stack.back() = - chaiscript::make_shared("else if", back->location, back->children); - m_match_stack.back()->annotation = back->annotation; - if (!Char('(')) { - throw exception::eval_error("Incomplete 'else if' expression", File_Position(m_position.line, m_position.col), *m_filename); - } - - if (!(Operator() && Char(')'))) { - throw exception::eval_error("Incomplete 'else if' expression", File_Position(m_position.line, m_position.col), *m_filename); - } - - while (Eol()) {} - - if (!Block()) { - throw exception::eval_error("Incomplete 'else if' block", File_Position(m_position.line, m_position.col), *m_filename); - } + if (Keyword("else")) { + if (If()) { has_matches = true; } else { while (Eol()) {} @@ -1706,7 +1716,19 @@ namespace chaiscript } } - build_match(prev_stack_top); + const auto num_children = m_match_stack.size() - prev_stack_top; + + if ((is_if_init && num_children == 3) + || (!is_if_init && num_children == 2)) { + m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + } + + if (!is_if_init) { + build_match>(prev_stack_top); + } else { + build_match>(prev_stack_top+1); + build_match>(prev_stack_top); + } } return retval; @@ -1721,18 +1743,19 @@ namespace chaiscript if (Keyword("class")) { retval = true; - if (!Id()) { + if (!Id(true)) { throw exception::eval_error("Missing class name in definition", File_Position(m_position.line, m_position.col), *m_filename); } + const auto class_name = m_match_stack.back()->text; while (Eol()) {} - if (!Class_Block()) { + if (!Class_Block(class_name)) { throw exception::eval_error("Incomplete 'class' block", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } return retval; @@ -1762,22 +1785,28 @@ namespace chaiscript throw exception::eval_error("Incomplete 'while' block", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } return retval; } + /// Reads the ranged `for` conditions from input + bool Range_Expression() { + // the first element will have already been captured by the For_Guards() call that preceeds it + return Char(':') && Equation(); + } - /// Reads the C-style for conditions from input + + /// Reads the C-style `for` conditions from input bool For_Guards() { if (!(Equation() && Eol())) { if (!Eol()) { - throw exception::eval_error("'for' loop initial statment missing", File_Position(m_position.line, m_position.col), *m_filename); + return false; } else { - m_match_stack.push_back(chaiscript::make_shared()); + m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); } } @@ -1785,20 +1814,21 @@ namespace chaiscript { if (!Eol()) { - throw exception::eval_error("'for' loop condition missing", File_Position(m_position.line, m_position.col), *m_filename); + return false; } else { - m_match_stack.push_back(chaiscript::make_shared()); + m_match_stack.push_back(chaiscript::make_shared, eval::Constant_AST_Node>(Boxed_Value(true))); } } if (!Equation()) { - m_match_stack.push_back(chaiscript::make_shared()); + m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); } return true; } + /// Reads a for block from input bool For() { bool retval = false; @@ -1812,7 +1842,8 @@ namespace chaiscript throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename); } - if (!(For_Guards() && Char(')'))) { + const bool classic_for = For_Guards() && Char(')'); + if (!classic_for && !(Range_Expression() && Char(')'))) { throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename); } @@ -1822,12 +1853,17 @@ namespace chaiscript throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + if (classic_for) { + build_match>(prev_stack_top); + } else { + build_match>(prev_stack_top); + } } return retval; } + /// Reads a case block from input bool Case() { bool retval = false; @@ -1851,7 +1887,7 @@ namespace chaiscript throw exception::eval_error("Incomplete 'case' block", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } else if (Keyword("default")) { retval = true; @@ -1861,7 +1897,7 @@ namespace chaiscript throw exception::eval_error("Incomplete 'default' block", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } return retval; @@ -1901,7 +1937,7 @@ namespace chaiscript throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); return true; } else { @@ -1912,7 +1948,7 @@ namespace chaiscript /// Reads a curly-brace C-style class block from input - bool Class_Block() { + bool Class_Block(const std::string &t_class_name) { bool retval = false; const auto prev_stack_top = m_match_stack.size(); @@ -1920,16 +1956,16 @@ namespace chaiscript if (Char('{')) { retval = true; - Class_Statements(); + Class_Statements(t_class_name); if (!Char('}')) { throw exception::eval_error("Incomplete class block", File_Position(m_position.line, m_position.col), *m_filename); } if (m_match_stack.size() == prev_stack_top) { - m_match_stack.push_back(chaiscript::make_shared()); + m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } return retval; @@ -1950,10 +1986,10 @@ namespace chaiscript } if (m_match_stack.size() == prev_stack_top) { - m_match_stack.push_back(chaiscript::make_shared()); + m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } return retval; @@ -1965,7 +2001,7 @@ namespace chaiscript if (Keyword("return")) { Operator(); - build_match(prev_stack_top); + build_match>(prev_stack_top); return true; } else { return false; @@ -1977,7 +2013,7 @@ namespace chaiscript const auto prev_stack_top = m_match_stack.size(); if (Keyword("break")) { - build_match(prev_stack_top); + build_match>(prev_stack_top); return true; } else { return false; @@ -1989,7 +2025,7 @@ namespace chaiscript const auto prev_stack_top = m_match_stack.size(); if (Keyword("continue")) { - build_match(prev_stack_top); + build_match>(prev_stack_top); return true; } else { return false; @@ -2002,7 +2038,7 @@ namespace chaiscript const auto prev_stack_top = m_match_stack.size(); if (Lambda() || Num(true) || Quoted_String(true) || Single_Quoted_String(true) || - Paren_Expression() || Inline_Container() || Id()) + Paren_Expression() || Inline_Container() || Id(false)) { retval = true; bool has_more = true; @@ -2018,21 +2054,21 @@ namespace chaiscript throw exception::eval_error("Incomplete function call", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); /// \todo Work around for method calls until we have a better solution if (!m_match_stack.back()->children.empty()) { if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Dot_Access) { if (m_match_stack.empty()) throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); if (m_match_stack.back()->children.empty()) throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); - AST_NodePtr dot_access = m_match_stack.back()->children[0]; - AST_NodePtr func_call = m_match_stack.back(); + auto dot_access = m_match_stack.back()->children[0]; + auto func_call = m_match_stack.back(); m_match_stack.pop_back(); func_call->children.erase(func_call->children.begin()); if (dot_access->children.empty()) throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); func_call->children.insert(func_call->children.begin(), dot_access->children.back()); dot_access->children.pop_back(); dot_access->children.push_back(std::move(func_call)); - if (dot_access->children.size() != 3) throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); + if (dot_access->children.size() != 2) throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); m_match_stack.push_back(std::move(dot_access)); } } @@ -2043,18 +2079,18 @@ namespace chaiscript throw exception::eval_error("Incomplete array access", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } - else if (Symbol(".", true)) { + else if (Symbol(".")) { has_more = true; - if (!(Id())) { + if (!(Id(true))) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); } - if ( std::distance(m_match_stack.begin() + static_cast(prev_stack_top), m_match_stack.end()) != 3) { + if ( std::distance(m_match_stack.begin() + static_cast(prev_stack_top), m_match_stack.end()) != 2) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } } } @@ -2063,7 +2099,7 @@ namespace chaiscript } /// Reads a variable declaration from input - bool Var_Decl(const bool t_class_context = false) { + bool Var_Decl(const bool t_class_context = false, const std::string &t_class_name = "") { bool retval = false; const auto prev_stack_top = m_match_stack.size(); @@ -2071,42 +2107,47 @@ namespace chaiscript if (t_class_context && (Keyword("attr") || Keyword("auto") || Keyword("var"))) { retval = true; - if (!Id()) { + m_match_stack.push_back(make_node>(t_class_name, m_position.line, m_position.col)); + + if (!Id(true)) { throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } else if (Keyword("auto") || Keyword("var") ) { retval = true; - if (!(Reference() || Id())) { + if (Reference()) { + // we built a reference node - continue + } else if (Id(true)) { + build_match>(prev_stack_top); + } else { throw exception::eval_error("Incomplete variable declaration", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); } else if (Keyword("GLOBAL") || Keyword("global")) { retval = true; - if (!(Reference() || Id())) { + if (!(Reference() || Id(true))) { throw exception::eval_error("Incomplete global declaration", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } else if (Keyword("attr")) { retval = true; - if (!Id()) { + if (!Id(true)) { throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); } - if (!Symbol("::", false)) { + if (!Symbol("::")) { throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); } - if (!Id()) { + if (!Id(true)) { throw exception::eval_error("Missing attribute name in definition", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } return retval; @@ -2139,17 +2180,17 @@ namespace chaiscript } if ((prev_stack_top != m_match_stack.size()) && (m_match_stack.back()->children.size() > 0)) { if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Value_Range) { - build_match(prev_stack_top); + build_match>(prev_stack_top); } else if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Map_Pair) { - build_match(prev_stack_top); + build_match>(prev_stack_top); } else { - build_match(prev_stack_top); + build_match>(prev_stack_top); } } else { - build_match(prev_stack_top); + build_match>(prev_stack_top); } return true; @@ -2162,12 +2203,12 @@ namespace chaiscript bool Reference() { const auto prev_stack_top = m_match_stack.size(); - if (Symbol("&", false)) { - if (!Id()) { + if (Symbol("&")) { + if (!Id(true)) { throw exception::eval_error("Incomplete '&' expression", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); return true; } else { return false; @@ -2177,18 +2218,18 @@ namespace chaiscript /// Reads a unary prefixed expression from input bool Prefix() { const auto prev_stack_top = m_match_stack.size(); - const std::vector prefix_opers{"++", "--", "-", "+", "!", "~", "&"}; + constexpr const std::array prefix_opers{{"++", "--", "-", "+", "!", "~"}}; for (const auto &oper : prefix_opers) { - bool is_char = oper.size() == 1; - if ((is_char && Char(oper[0], true)) || (!is_char && Symbol(oper.c_str(), true))) + bool is_char = strlen(oper) == 1; + if ((is_char && Char(oper[0])) || (!is_char && Symbol(oper))) { if (!Operator(m_operators.size()-1)) { - throw exception::eval_error("Incomplete prefix '" + oper + "' expression", File_Position(m_position.line, m_position.col), *m_filename); + throw exception::eval_error("Incomplete prefix '" + std::string(oper) + "' expression", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top, oper); return true; } } @@ -2201,9 +2242,10 @@ namespace chaiscript return Var_Decl() || Dot_Fun_Array() || Prefix(); } - bool Operator_Helper(const size_t t_precedence) { + bool Operator_Helper(const size_t t_precedence, std::string &oper) { for (auto & elem : m_operator_matches[t_precedence]) { - if (Symbol(elem.c_str(), true)) { + if (Symbol(elem.c_str())) { + oper = elem; return true; } } @@ -2214,68 +2256,59 @@ namespace chaiscript bool retval = false; const auto prev_stack_top = m_match_stack.size(); - if (t_precedence < m_operators.size()) { + if (m_operators[t_precedence] != Operator_Precidence::Prefix) { if (Operator(t_precedence+1)) { retval = true; - if (Operator_Helper(t_precedence)) { - do { - while (Eol()) {} - if (!Operator(t_precedence+1)) { - throw exception::eval_error("Incomplete " - + std::string(ast_node_type_to_string(m_operators[t_precedence])) + " expression", - File_Position(m_position.line, m_position.col), *m_filename); - } + std::string oper; + while (Operator_Helper(t_precedence, oper)) { + while (Eol()) {} + if (!Operator(t_precedence+1)) { + throw exception::eval_error("Incomplete '" + oper + "' expression", + File_Position(m_position.line, m_position.col), *m_filename); + } - AST_NodePtr oper = m_match_stack.at(m_match_stack.size()-2); - - switch (m_operators[t_precedence]) { - case(AST_Node_Type::Ternary_Cond) : - m_match_stack.erase(advance_copy(m_match_stack.begin(), m_match_stack.size() - 2), - advance_copy(m_match_stack.begin(), m_match_stack.size() - 1)); - if (Symbol(":")) { - if (!Operator(t_precedence+1)) { - throw exception::eval_error("Incomplete " - + std::string(ast_node_type_to_string(m_operators[t_precedence])) + " expression", - File_Position(m_position.line, m_position.col), *m_filename); - } - build_match(prev_stack_top); - } - else { - throw exception::eval_error("Incomplete " - + std::string(ast_node_type_to_string(m_operators[t_precedence])) + " expression", + switch (m_operators[t_precedence]) { + case(Operator_Precidence::Ternary_Cond) : + if (Symbol(":")) { + if (!Operator(t_precedence+1)) { + throw exception::eval_error("Incomplete '" + oper + "' expression", File_Position(m_position.line, m_position.col), *m_filename); } - break; + build_match>(prev_stack_top); + } + else { + throw exception::eval_error("Incomplete '" + oper + "' expression", + File_Position(m_position.line, m_position.col), *m_filename); + } + break; - case(AST_Node_Type::Addition) : - case(AST_Node_Type::Multiplication) : - case(AST_Node_Type::Shift) : - case(AST_Node_Type::Equality) : - case(AST_Node_Type::Bitwise_And) : - case(AST_Node_Type::Bitwise_Xor) : - case(AST_Node_Type::Bitwise_Or) : - case(AST_Node_Type::Comparison) : - assert(m_match_stack.size() > 1); - m_match_stack.erase(advance_copy(m_match_stack.begin(), m_match_stack.size() - 2), - advance_copy(m_match_stack.begin(), m_match_stack.size() - 1)); - build_match(prev_stack_top, oper->text); - 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) : + build_match>(prev_stack_top, oper); + break; - case(AST_Node_Type::Logical_And) : - build_match(prev_stack_top); - break; - case(AST_Node_Type::Logical_Or) : - build_match(prev_stack_top); - break; + case(Operator_Precidence::Logical_And) : + build_match>(prev_stack_top, oper); + break; + case(Operator_Precidence::Logical_Or) : + build_match>(prev_stack_top, oper); + break; + case(Operator_Precidence::Prefix) : + assert(false); // cannot reach here because of if() statement at the top + break; - default: - throw exception::eval_error("Internal error: unhandled ast_node", File_Position(m_position.line, m_position.col), *m_filename); - } - } while (Operator_Helper(t_precedence)); +// default: +// throw exception::eval_error("Internal error: unhandled ast_node", File_Position(m_position.line, m_position.col), *m_filename); + } } } - } - else { + } else { return Value(); } @@ -2296,7 +2329,7 @@ namespace chaiscript throw exception::eval_error("Incomplete map pair", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } else { m_position = prev_pos; @@ -2323,7 +2356,7 @@ namespace chaiscript throw exception::eval_error("Incomplete value range", File_Position(m_position.line, m_position.col), *m_filename); } - build_match(prev_stack_top); + build_match>(prev_stack_top); } else { m_position = prev_pos; @@ -2338,30 +2371,29 @@ namespace chaiscript /// Parses a string of binary equation operators bool Equation() { - bool retval = false; - const auto prev_stack_top = m_match_stack.size(); if (Operator()) { - retval = true; - if (Symbol("=", true, true) || Symbol(":=", true, true) || Symbol("+=", true, true) || - Symbol("-=", true, true) || Symbol("*=", true, true) || Symbol("/=", true, true) || - Symbol("%=", true, true) || Symbol("<<=", true, true) || Symbol(">>=", true, true) || - Symbol("&=", true, true) || Symbol("^=", true, true) || Symbol("|=", true, true)) { - SkipWS(true); - if (!Equation()) { - throw exception::eval_error("Incomplete equation", File_Position(m_position.line, m_position.col), *m_filename); - } + for (const auto sym : {"=", ":=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", "&=", "^=", "|="}) + { + if (Symbol(sym, true)) { + SkipWS(true); + if (!Equation()) { + throw exception::eval_error("Incomplete equation", File_Position(m_position.line, m_position.col), *m_filename); + } - build_match(prev_stack_top); + build_match>(prev_stack_top, sym); + return true; + } } + return true; } - return retval; + return false; } /// Parses statements allowed inside of a class block - bool Class_Statements() { + bool Class_Statements(const std::string &t_class_name) { bool retval = false; bool has_more = true; @@ -2369,7 +2401,7 @@ namespace chaiscript while (has_more) { const auto start = m_position; - if (Def(true) || Var_Decl(true)) { + if (Def(true, t_class_name) || Var_Decl(true, t_class_name)) { if (!saw_eol) { throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename); } @@ -2426,8 +2458,30 @@ namespace chaiscript return retval; } + AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) override + { + ChaiScript_Parser parser(*this); + parser.m_match_stack.clear(); + return parser.parse_internal(t_input, t_fname); + } + + eval::AST_Node_Impl_Ptr parse_instr_eval(const std::string &t_input) + { + const auto last_position = m_position; + const auto last_filename = m_filename; + const auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){}); + + const auto retval = parse_internal(t_input, "instr eval"); + + m_position = std::move(last_position); + m_filename = std::move(last_filename); + m_match_stack = std::move(last_match_stack); + + return std::dynamic_pointer_cast>(retval); + } + /// Parses the given input string, tagging parsed ast_nodes with the given m_filename. - bool parse(const std::string &t_input, std::string t_fname) { + AST_NodePtr parse_internal(const std::string &t_input, std::string t_fname) { m_position = Position(t_input.begin(), t_input.end()); m_filename = std::make_shared(std::move(t_fname)); @@ -2442,13 +2496,13 @@ namespace chaiscript if (m_position.has_more()) { throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), t_fname); } else { - build_match(0); - //debug_print(ast()); - return true; + build_match>(0); } } else { - return false; + m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); } + + return m_match_stack.front(); } }; } diff --git a/include/chaiscript/language/chaiscript_posix.hpp b/include/chaiscript/language/chaiscript_posix.hpp new file mode 100644 index 00000000..c22fe87c --- /dev/null +++ b/include/chaiscript/language/chaiscript_posix.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-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_POSIX_HPP_ +#define CHAISCRIPT_POSIX_HPP_ + +namespace chaiscript +{ + namespace detail + { + struct Loadable_Module + { + struct DLModule + { + DLModule(const std::string &t_filename) + : m_data(dlopen(t_filename.c_str(), RTLD_NOW)) + { + if (!m_data) + { + throw chaiscript::exception::load_module_error(dlerror()); + } + } + + DLModule(DLModule &&) = default; + DLModule &operator=(DLModule &&) = default; + DLModule(const DLModule &) = delete; + DLModule &operator=(const DLModule &) = delete; + + ~DLModule() + { + dlclose(m_data); + } + + void *m_data; + }; + + template + struct DLSym + { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(cast_symbol(dlsym(t_mod.m_data, t_symbol.c_str()))) + { + if (!m_symbol) + { + throw chaiscript::exception::load_module_error(dlerror()); + } + } + + static T cast_symbol(void *p) + { + union cast_union + { + T func_ptr; + void *in_ptr; + }; + + cast_union c; + c.in_ptr = p; + return c.func_ptr; + } + + T m_symbol; + }; + + Loadable_Module(const std::string &t_module_name, const std::string &t_filename) + : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), + m_moduleptr(m_func.m_symbol()) + { + } + + DLModule m_dlmodule; + DLSym m_func; + ModulePtr m_moduleptr; + }; + } +} +#endif + diff --git a/include/chaiscript/language/chaiscript_tracer.hpp b/include/chaiscript/language/chaiscript_tracer.hpp new file mode 100644 index 00000000..938b6670 --- /dev/null +++ b/include/chaiscript/language/chaiscript_tracer.hpp @@ -0,0 +1,43 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_TRACER_HPP_ +#define CHAISCRIPT_TRACER_HPP_ + +namespace chaiscript { + namespace eval { + + struct Noop_Tracer + { + template + static void trace(const chaiscript::detail::Dispatch_State &, const AST_Node_Impl *) + { + } + }; + + template + struct Tracer : T... + { + Tracer() = default; + Tracer(T ... t) + : T(std::move(t))... + { + } + + void do_trace(const chaiscript::detail::Dispatch_State &ds, const AST_Node_Impl> *node) { + (void)std::initializer_list{ (T::trace(ds, node), 0)... }; + } + + static void trace(const chaiscript::detail::Dispatch_State &ds, const AST_Node_Impl> *node) { + ds->get_parser().get_tracer>().do_trace(ds, node); + } + }; + + } +} + +#endif + diff --git a/include/chaiscript/language/chaiscript_unknown.hpp b/include/chaiscript/language/chaiscript_unknown.hpp new file mode 100644 index 00000000..2e5c2283 --- /dev/null +++ b/include/chaiscript/language/chaiscript_unknown.hpp @@ -0,0 +1,27 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_UNKNOWN_HPP_ +#define CHAISCRIPT_UNKNOWN_HPP_ + + +namespace chaiscript +{ + namespace detail + { + struct Loadable_Module + { + Loadable_Module(const std::string &, const std::string &) + { + throw chaiscript::exception::load_module_error("Loadable module support not available for your platform"); + } + + ModulePtr m_moduleptr; + }; + } +} +#endif + diff --git a/include/chaiscript/language/chaiscript_windows.hpp b/include/chaiscript/language/chaiscript_windows.hpp new file mode 100644 index 00000000..049e3416 --- /dev/null +++ b/include/chaiscript/language/chaiscript_windows.hpp @@ -0,0 +1,133 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_WINDOWS_HPP_ +#define CHAISCRIPT_WINDOWS_HPP_ + +#include + +#ifdef CHAISCRIPT_WINDOWS +#define VC_EXTRA_LEAN +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + + +namespace chaiscript +{ + namespace detail + { + struct Loadable_Module + { + template + static std::wstring to_wstring(const T &t_str) + { + return std::wstring(t_str.begin(), t_str.end()); + } + + template + static std::string to_string(const T &t_str) + { + return std::string(t_str.begin(), t_str.end()); + } + +#if defined(_UNICODE) || defined(UNICODE) + template + static std::wstring to_proper_string(const T &t_str) + { + return to_wstring(t_str); + } +#else + template + static std::string to_proper_string(const T &t_str) + { + return to_string(t_str); + } +#endif + + static std::string get_error_message(DWORD t_err) + { + typedef LPTSTR StringType; + +#if defined(_UNICODE) || defined(UNICODE) + std::wstring retval = L"Unknown Error"; +#else + std::string retval = "Unknown Error"; +#endif + StringType lpMsgBuf = nullptr; + + if (FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + t_err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&lpMsgBuf), + 0, nullptr ) != 0 && lpMsgBuf) + { + retval = lpMsgBuf; + LocalFree(lpMsgBuf); + } + + return to_string(retval); + } + + struct DLModule + { + DLModule(const std::string &t_filename) + : m_data(LoadLibrary(to_proper_string(t_filename).c_str())) + { + if (!m_data) + { + throw chaiscript::exception::load_module_error(get_error_message(GetLastError())); + } + } + + DLModule(DLModule &&) = default; + DLModule &operator=(DLModule &&) = default; + DLModule(const DLModule &) = delete; + DLModule &operator=(const DLModule &) = delete; + + ~DLModule() + { + FreeLibrary(m_data); + } + + HMODULE m_data; + }; + + template + struct DLSym + { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(reinterpret_cast(GetProcAddress(t_mod.m_data, t_symbol.c_str()))) + { + if (!m_symbol) + { + throw chaiscript::exception::load_module_error(get_error_message(GetLastError())); + } + } + + T m_symbol; + }; + + Loadable_Module(const std::string &t_module_name, const std::string &t_filename) + : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), + m_moduleptr(m_func.m_symbol()) + { + } + + DLModule m_dlmodule; + DLSym m_func; + ModulePtr m_moduleptr; + }; + } +} +#endif + diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 294901e1..561b93e5 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -47,6 +47,79 @@ class JSON }; private: + + struct QuickFlatMap + { + auto find(const std::string &s) { + return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); + } + + auto find(const std::string &s) const { + return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); + } + + auto size() const { + return data.size(); + } + + auto begin() const { + return data.begin(); + } + + auto end() const { + return data.end(); + } + + + auto begin() { + return data.begin(); + } + + auto end() { + return data.end(); + } + + + JSON &operator[](const std::string &s) { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + data.emplace_back(s, JSON()); + return data.back().second; + } + } + + JSON &at(const std::string &s) { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + throw std::out_of_range("Unknown key: " + s); + } + } + + const JSON &at(const std::string &s) const { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + throw std::out_of_range("Unknown key: " + s); + } + } + + size_t count(const std::string &s) const { + return (find(s) != data.end())?1:0; + } + + std::vector> data; + + using iterator = decltype(data)::iterator; + using const_iterator = decltype(data)::const_iterator; + + + }; + struct Internal { template auto clone(const std::unique_ptr &ptr) { @@ -100,7 +173,7 @@ class JSON String.reset(); switch( type ) { - case Class::Object: Map = std::make_unique>(); break; + case Class::Object: Map = std::make_unique(); break; case Class::Array: List = std::make_unique>(); break; case Class::String: String = std::make_unique(); break; case Class::Floating: Float = 0.0; break; @@ -116,7 +189,7 @@ class JSON Internal &operator=(Internal &&) = default; std::unique_ptr> List; - std::unique_ptr> Map; + std::unique_ptr Map; std::unique_ptr String; double Float = 0; long Int = 0; @@ -229,7 +302,7 @@ class JSON bool has_key( const std::string &key ) const { if( internal.Type == Class::Object ) { - return internal.Map->find( key ) != internal.Map->end(); + return internal.Map->count(key) != 0; } return false; @@ -274,11 +347,11 @@ class JSON return ok ? internal.Bool : false; } - JSONWrapper> object_range() { + JSONWrapper object_range() { if( internal.Type == Class::Object ) { - return JSONWrapper>( internal.Map.get() ); + return JSONWrapper( internal.Map.get() ); } else { - return JSONWrapper>( nullptr ); + return JSONWrapper( nullptr ); } } @@ -290,11 +363,11 @@ class JSON } } - JSONConstWrapper> object_range() const { + JSONConstWrapper object_range() const { if( internal.Type == Class::Object ) { - return JSONConstWrapper>( internal.Map.get() ); + return JSONConstWrapper( internal.Map.get() ); } else { - return JSONConstWrapper>( nullptr ); + return JSONConstWrapper( nullptr ); } } diff --git a/include/chaiscript/utility/utility.hpp b/include/chaiscript/utility/utility.hpp index 1ca0dfbb..fa2bbdbe 100644 --- a/include/chaiscript/utility/utility.hpp +++ b/include/chaiscript/utility/utility.hpp @@ -12,9 +12,8 @@ #include #include -#include "../chaiscript.hpp" -#include "../dispatchkit/proxy_functions.hpp" -#include "../dispatchkit/type_info.hpp" +#include "../language/chaiscript_common.hpp" +#include "../dispatchkit/register_function.hpp" #include "../dispatchkit/operators.hpp" diff --git a/readme.md b/readme.md index 339b8052..ddb0ed3d 100644 --- a/readme.md +++ b/readme.md @@ -43,10 +43,9 @@ languages: Requirements ============ -ChaiScript requires a C++11 compiler to build with support for variadic -templates. It has been tested with gcc 4.6 and clang 3.1 (with libcxx). MacOS -10.8 (Mountain Lion) is also known to support the C++11 build with Apple's -clang 4.0. MSVC 2013 or newer is supports also. For more information see the build +ChaiScript requires a C++14 compiler to build with support for variadic +templates. It has been tested with gcc 4.9 and clang 3.6 (with libcxx). +For more information see the build [dashboard](http://chaiscript.com/ChaiScript-BuildResults/index.html). Usage diff --git a/releasenotes.md b/releasenotes.md index d448be4a..36220309 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -1,6 +1,57 @@ Notes: ======= -Current Version: 5.8.2 +Current Version: 6.0.0 + +### Changes since 5.8.6 + +*6.0.0 is a massive rework compared to 5.x. It now requires a C++14 enabled compiler* + +#### Compiler Requirements + +* MSVC 2015 or greater +* g++ 4.9 or greater +* clang 3.6 or greater + +#### Breaking Changes + +* Instantiating a ChaiScript object now, by default, builds the stdlib in + * This was done to address the most common support issues of loading stdlib dynamically at runtime + * If you want the old behavior, use include/chaiscript/chaiscript_basic.hpp +* Headers have been reorganized to fully separate stdlib/parser/engine from each other (some faster builds) +* Bootstrap functions no longer return a reference to the module added to (compile time savings) +* It's now no longer possible modify AST_Nodes (compile time, runtime efficiency) +* Function annotations no longer exist (simplifies code, reduces compile time, compile size) + +#### New Features Added + +* Modular optimization system; this can be accessed via the ChaiScript_Basic interface +* Execution tracing capability; also accessed via ChaiScript_Basic interface +* range-based for loops `for( id : container ) { }` + +#### Improvements + +* Compile time improvements +* Compile size improvements +* Significant runtime improvements (see "Modular optimization system") + +#### Improvements Still Need To Be Made + +* File location tracking has been rewritten; this currently means error location reporting is not as good as it was +* Tracing capability needs to be tested and vetted + +### Changes since 5.8.5 +* Optimize away `return` statements in lambdas also + +### Changes since 5.8.4 +* Fix order of operations for prefix operators +* Make sure atomics are initialized properly +* Remove parsing of unused prefix `&` operator + +### Changes since 5.8.3 +* Fix case with some numeric conversions mixed with numerics that do not need conversion + +### Changes since 5.8.2 +* Add support for reference of pointer return types ### Changes since 5.8.1 * Allow casting to non-const & std::shared_ptr diff --git a/samples/example.cpp b/samples/example.cpp index 507c5c0e..0316ebf6 100644 --- a/samples/example.cpp +++ b/samples/example.cpp @@ -73,7 +73,7 @@ int main(int /*argc*/, char * /*argv*/[]) { //Create a new system object and share it with the chaiscript engine System system; - chai.add(var(&system), "system"); + chai.add_global(var(&system), "system"); //Add a bound callback method chai.add(fun(&System::add_callback, std::ref(system)), "add_callback_bound"); diff --git a/samples/factory.cpp b/samples/factory.cpp index ac8eb476..976868b5 100644 --- a/samples/factory.cpp +++ b/samples/factory.cpp @@ -1,5 +1,4 @@ #include -#include class Entity { @@ -57,7 +56,7 @@ class Factory int main() { - chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); + chaiscript::ChaiScript chai; chai.add(chaiscript::fun(&Entity::width), "width"); chai.add(chaiscript::fun(&Entity::height), "height"); diff --git a/samples/fun_call_performance.cpp b/samples/fun_call_performance.cpp index 6b91fed9..90fd7d94 100644 --- a/samples/fun_call_performance.cpp +++ b/samples/fun_call_performance.cpp @@ -13,7 +13,6 @@ #endif #include -#include #ifdef READLINE_AVAILABLE #include @@ -291,7 +290,7 @@ int main(int argc, char *argv[]) } //chaiscript::ChaiScript chai(modulepaths, usepaths); - chaiscript::ChaiScript chai(chaiscript::Std_Lib::library(), usepaths); + chaiscript::ChaiScript chai(usepaths); chai.add(chaiscript::fun(&myexit), "exit"); chai.add(chaiscript::fun(&myexit), "quit"); diff --git a/samples/inheritance.cpp b/samples/inheritance.cpp index 133feb95..aba619a4 100644 --- a/samples/inheritance.cpp +++ b/samples/inheritance.cpp @@ -1,5 +1,4 @@ #include -#include class BaseClass { @@ -70,7 +69,7 @@ class ChaiScriptDerived : public BaseClass int main() { - chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); + chaiscript::ChaiScript chai; chai.add(chaiscript::fun(&BaseClass::doSomething), "doSomething"); chai.add(chaiscript::fun(&BaseClass::setValue), "setValue"); chai.add(chaiscript::fun(&BaseClass::getValue), "getValue"); diff --git a/samples/memory_leak_test.cpp b/samples/memory_leak_test.cpp index 13afbb45..513656b7 100644 --- a/samples/memory_leak_test.cpp +++ b/samples/memory_leak_test.cpp @@ -1,7 +1,6 @@ #include #include -#include #ifdef READLINE_AVAILABLE #include @@ -32,16 +31,9 @@ void function(void) class test { chaiscript::ChaiScript chai; - chaiscript::ChaiScript::State backupState; + chaiscript::ChaiScript::State backupState = chai.get_state(); public: - test() - : chai(chaiscript::Std_Lib::library()) - { - backupState = chai.get_state(); - } - ~test(){} - void ResetState() { chai.set_state(backupState); diff --git a/samples/test_num_exceptions.cpp b/samples/test_num_exceptions.cpp index 3a35fb7c..8347c4a0 100644 --- a/samples/test_num_exceptions.cpp +++ b/samples/test_num_exceptions.cpp @@ -1,11 +1,10 @@ #include -#include #include #include int main( int /*argc*/ , char * /*argv*/[] ) { - chaiscript::ChaiScript ch( chaiscript::Std_Lib::library( ) ); + chaiscript::ChaiScript ch; try diff --git a/src/chaiscript_stdlib.cpp b/src/chaiscript_stdlib_module.cpp similarity index 100% rename from src/chaiscript_stdlib.cpp rename to src/chaiscript_stdlib_module.cpp diff --git a/src/main.cpp b/src/main.cpp index 7ac9b2b6..61ca7c11 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,7 +12,10 @@ #define _CRT_SECURE_NO_WARNINGS #endif -#include +#include +#include "../static_libs/chaiscript_parser.hpp" +#include "../static_libs/chaiscript_stdlib.hpp" + #ifdef READLINE_AVAILABLE #include @@ -125,7 +128,7 @@ std::vector default_search_paths() size_t secondtolastslash = exepath.rfind('/', lastslash - 1); if (lastslash != std::string::npos) { - paths.push_back(exepath.substr(0, lastslash)); + paths.push_back(exepath.substr(0, lastslash+1)); } if (secondtolastslash != std::string::npos) @@ -217,7 +220,7 @@ void myexit(int return_val) { exit(return_val); } -void interactive(chaiscript::ChaiScript& chai) +void interactive(chaiscript::ChaiScript_Basic& chai) { using_history(); @@ -288,7 +291,7 @@ int main(int argc, char *argv[]) modulepaths.push_back(modulepath); } - chaiscript::ChaiScript chai(modulepaths,usepaths); + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(),modulepaths,usepaths); chai.add(chaiscript::fun(&myexit), "exit"); chai.add(chaiscript::fun(&myexit), "quit"); diff --git a/src/stl_extra.cpp b/src/stl_extra.cpp index d3ee3754..07bc0609 100644 --- a/src/stl_extra.cpp +++ b/src/stl_extra.cpp @@ -4,7 +4,7 @@ // Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com -#include +#include #include #include #include diff --git a/src/test_module.cpp b/src/test_module.cpp index 731e3271..fcb255e5 100644 --- a/src/test_module.cpp +++ b/src/test_module.cpp @@ -1,5 +1,5 @@ -#include +#include #include #include diff --git a/static_libs/chaiscript_parser.cpp b/static_libs/chaiscript_parser.cpp new file mode 100644 index 00000000..15199bf3 --- /dev/null +++ b/static_libs/chaiscript_parser.cpp @@ -0,0 +1,8 @@ +#include "../include/chaiscript/language/chaiscript_parser.hpp" +#include "chaiscript_parser.hpp" + +std::unique_ptr create_chaiscript_parser() +{ + return std::make_unique>(); +} + diff --git a/static_libs/chaiscript_parser.hpp b/static_libs/chaiscript_parser.hpp new file mode 100644 index 00000000..6086749d --- /dev/null +++ b/static_libs/chaiscript_parser.hpp @@ -0,0 +1,13 @@ + +#ifndef CHAISCRIPT_PARSER_LIB +#define CHAISCRIPT_PARSER_LIB + +namespace chaiscript { + namespace parser { + class ChaiScript_Parser_Base; + } +} + +std::unique_ptr create_chaiscript_parser(); + +#endif diff --git a/static_libs/chaiscript_stdlib.cpp b/static_libs/chaiscript_stdlib.cpp new file mode 100644 index 00000000..c786ece8 --- /dev/null +++ b/static_libs/chaiscript_stdlib.cpp @@ -0,0 +1,8 @@ +#include "../include/chaiscript/chaiscript_stdlib.hpp" +#include "chaiscript_stdlib.hpp" + +std::shared_ptr create_chaiscript_stdlib() +{ + return chaiscript::Std_Lib::library(); +} + diff --git a/static_libs/chaiscript_stdlib.hpp b/static_libs/chaiscript_stdlib.hpp new file mode 100644 index 00000000..3bd0d83a --- /dev/null +++ b/static_libs/chaiscript_stdlib.hpp @@ -0,0 +1,11 @@ + +#ifndef CHAISCRIPT_STDLIB +#define CHAISCRIPT_STDLIB + +namespace chaiscript { + class Module; +} + +std::shared_ptr create_chaiscript_stdlib(); + +#endif diff --git a/unittests/3.x/dispatch_functions.chai b/unittests/3.x/dispatch_functions.chai index 528d5b30..f25ae017 100644 --- a/unittests/3.x/dispatch_functions.chai +++ b/unittests/3.x/dispatch_functions.chai @@ -1,7 +1,6 @@ assert_equal(`==`, `==`); assert_not_equal(`==`, `<`); assert_equal(`<`.get_arity(), 2); -assert_equal(`+`.get_annotation(), "Multiple method dispatch function wrapper."); assert_equal(get_arity.get_contained_functions().size(), 0); assert_equal(get_arity.get_arity(), 1); assert_equal(get_arity.get_param_types().size(), 2); diff --git a/unittests/3.x/eval_error.chai b/unittests/3.x/eval_error.chai index 39a6541e..b5814450 100644 --- a/unittests/3.x/eval_error.chai +++ b/unittests/3.x/eval_error.chai @@ -35,4 +35,4 @@ def while_doing() var f = fun() { while_doing(); } -assert_equal(get_eval_error(f).call_stack.size(), 16) +assert_true(get_eval_error(f).call_stack.size() <= 16) diff --git a/unittests/3.x/function_introspection.chai b/unittests/3.x/function_introspection.chai index 5ad76fc7..96b2ec67 100644 --- a/unittests/3.x/function_introspection.chai +++ b/unittests/3.x/function_introspection.chai @@ -10,7 +10,6 @@ def test_function(a) // test_function tests assert_equal(test_function.get_arity(), 1); -assert_equal(trim(test_function.get_annotation()), "#Test Function Description"); assert_equal(test_function.get_contained_functions().size(), 0); assert_equal(test_function.get_param_types().size(), 2); diff --git a/unittests/3.x/reflection_test.chai b/unittests/3.x/reflection_test.chai deleted file mode 100644 index 25a24168..00000000 --- a/unittests/3.x/reflection_test.chai +++ /dev/null @@ -1,36 +0,0 @@ -var parser := ChaiScript_Parser() -var parse_success = parser.parse("3 + 4", "INPUT") -var a := parser.ast() - -assert_equal(eval(a), 7) - -var childs := a.children.front().children -var node := childs[0] - -var parser2 := ChaiScript_Parser() -parser2.parse("9", "INPUT") - - -a.children.front().replace_child(childs[0], parser2.ast()) - -assert_equal(eval(a), 13) -assert_equal(node.filename, "INPUT") - - - -def my_fun() -{ - return 1; -} - - -assert_equal(true, my_fun.has_parse_tree()); -assert_equal(false, `+`.has_parse_tree()); - -assert_throws("Function does not have a parse tree", fun() { `+`.get_parse_tree(); } ); - -var parsetree := my_fun.get_parse_tree(); - -assert_equal(1, eval(parsetree)); - -print(parsetree.text()); diff --git a/unittests/c_linkage_test.cpp b/unittests/c_linkage_test.cpp index 28ec17aa..180686af 100644 --- a/unittests/c_linkage_test.cpp +++ b/unittests/c_linkage_test.cpp @@ -1,4 +1,6 @@ -#include +#include +#include "../static_libs/chaiscript_parser.hpp" +#include "../static_libs/chaiscript_stdlib.hpp" extern "C" @@ -11,8 +13,7 @@ extern "C" int main() { - - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::fun(&do_something), "do_something"); return chai.eval("do_something(101)") == 101 % 2?EXIT_SUCCESS:EXIT_FAILURE; diff --git a/unittests/catch.hpp b/unittests/catch.hpp index 2554d91b..a11ecce3 100644 --- a/unittests/catch.hpp +++ b/unittests/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.3.5 - * Generated: 2016-02-29 08:16:42.342094 + * Catch v1.5.7 + * Generated: 2016-09-27 10:45:46.824849 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -62,7 +62,11 @@ #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif #define INTERNAL_CATCH_STRINGIFY2( expr ) #expr #define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) @@ -89,7 +93,7 @@ // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? - +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too @@ -102,8 +106,16 @@ // All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 -#if defined(__cplusplus) && __cplusplus >= 201103L -# define CATCH_CPP11_OR_GREATER +#ifdef __cplusplus + +# if __cplusplus >= 201103L +# define CATCH_CPP11_OR_GREATER +# endif + +# if __cplusplus >= 201402L +# define CATCH_CPP14_OR_GREATER +# endif + #endif #ifdef __clang__ @@ -185,6 +197,15 @@ #endif +// Use __COUNTER__ if the compiler supports it +#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ + ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ + ( defined __clang__ && __clang_major__ >= 3 ) + +#define CATCH_INTERNAL_CONFIG_COUNTER + +#endif + //////////////////////////////////////////////////////////////////////////////// // C++ language feature support @@ -256,6 +277,9 @@ #if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_UNIQUE_PTR #endif +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS @@ -686,24 +710,28 @@ void registerTestCaseFunction #ifdef CATCH_CONFIG_VARIADIC_MACROS /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\ + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ namespace{ \ - struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ + struct TestName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ } \ - void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ @@ -711,24 +739,28 @@ void registerTestCaseFunction #else /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ + static void TestName(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + static void TestName() #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ namespace{ \ - struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ + struct TestCaseName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ } \ - void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + void TestCaseName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ @@ -2041,7 +2073,7 @@ namespace Catch { __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && static_cast(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ @@ -2578,10 +2610,12 @@ namespace Catch { } /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \ - static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\ - static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ) +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) // #included from: internal/catch_approx.hpp #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED @@ -3189,10 +3223,11 @@ namespace Catch { bool matches( TestCaseInfo const& testCase ) const { // All patterns in a filter must match for the filter to be a match - for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) + for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { if( !(*it)->matches( testCase ) ) return false; - return true; + } + return true; } }; @@ -3424,7 +3459,7 @@ namespace Catch { }; class DebugOutStream : public IStream { - std::auto_ptr m_streamBuf; + CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; mutable std::ostream m_os; public: DebugOutStream(); @@ -3572,7 +3607,7 @@ namespace Catch { } ConfigData m_data; - std::auto_ptr m_stream; + CATCH_AUTO_PTR( IStream const ) m_stream; TestSpec m_testSpec; }; @@ -3592,7 +3627,7 @@ namespace Catch { #define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { // #included from: ../external/clara.h -// Version 0.0.1.1 +// Version 0.0.2.4 // Only use header guard if we are not using an outer namespace #if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) @@ -3908,6 +3943,10 @@ namespace Tbc { #include #include +#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CLARA_PLATFORM_WINDOWS +#endif + // Use optional outer namespace #ifdef STITCH_CLARA_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE @@ -3931,9 +3970,6 @@ namespace Clara { const unsigned int consoleWidth = 80; #endif - // Use this to try and stop compiler from warning about unreachable code - inline bool isTrue( bool value ) { return value; } - using namespace Tbc; inline bool startsWith( std::string const& str, std::string const& prefix ) { @@ -3969,14 +4005,6 @@ namespace Clara { else throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); } - inline void convertInto( bool _source, bool& _dest ) { - _dest = _source; - } - template - inline void convertInto( bool, T& ) { - if( isTrue( true ) ) - throw std::runtime_error( "Invalid conversion" ); - } template struct IArgFunction { @@ -3986,7 +4014,6 @@ namespace Clara { IArgFunction( IArgFunction const& ) = default; #endif virtual void set( ConfigT& config, std::string const& value ) const = 0; - virtual void setFlag( ConfigT& config ) const = 0; virtual bool takesArg() const = 0; virtual IArgFunction* clone() const = 0; }; @@ -4008,9 +4035,6 @@ namespace Clara { void set( ConfigT& config, std::string const& value ) const { functionObj->set( config, value ); } - void setFlag( ConfigT& config ) const { - functionObj->setFlag( config ); - } bool takesArg() const { return functionObj->takesArg(); } bool isSet() const { @@ -4023,7 +4047,6 @@ namespace Clara { template struct NullBinder : IArgFunction{ virtual void set( C&, std::string const& ) const {} - virtual void setFlag( C& ) const {} virtual bool takesArg() const { return true; } virtual IArgFunction* clone() const { return new NullBinder( *this ); } }; @@ -4034,9 +4057,6 @@ namespace Clara { virtual void set( C& p, std::string const& stringValue ) const { convertInto( stringValue, p.*member ); } - virtual void setFlag( C& p ) const { - convertInto( true, p.*member ); - } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } M C::* member; @@ -4049,11 +4069,6 @@ namespace Clara { convertInto( stringValue, value ); (p.*member)( value ); } - virtual void setFlag( C& p ) const { - typename RemoveConstRef::type value; - convertInto( true, value ); - (p.*member)( value ); - } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } void (C::*member)( M ); @@ -4067,9 +4082,6 @@ namespace Clara { if( value ) (p.*member)(); } - virtual void setFlag( C& p ) const { - (p.*member)(); - } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } void (C::*member)(); @@ -4084,9 +4096,6 @@ namespace Clara { if( value ) function( obj ); } - virtual void setFlag( C& p ) const { - function( p ); - } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } void (*function)( C& ); @@ -4100,11 +4109,6 @@ namespace Clara { convertInto( stringValue, value ); function( obj, value ); } - virtual void setFlag( C& obj ) const { - typename RemoveConstRef::type value; - convertInto( true, value ); - function( obj, value ); - } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } void (*function)( C&, T ); @@ -4112,8 +4116,20 @@ namespace Clara { } // namespace Detail - struct Parser { - Parser() : separators( " \t=:" ) {} + inline std::vector argsToVector( int argc, char const* const* const argv ) { + std::vector args( static_cast( argc ) ); + for( std::size_t i = 0; i < static_cast( argc ); ++i ) + args[i] = argv[i]; + + return args; + } + + class Parser { + enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; + Mode mode; + std::size_t from; + bool inQuotes; + public: struct Token { enum Type { Positional, ShortOpt, LongOpt }; @@ -4122,38 +4138,75 @@ namespace Clara { std::string data; }; - void parseIntoTokens( int argc, char const* const argv[], std::vector& tokens ) const { + Parser() : mode( None ), from( 0 ), inQuotes( false ){} + + void parseIntoTokens( std::vector const& args, std::vector& tokens ) { const std::string doubleDash = "--"; - for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) - parseIntoTokens( argv[i] , tokens); + for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) + parseIntoTokens( args[i], tokens); } - void parseIntoTokens( std::string arg, std::vector& tokens ) const { - while( !arg.empty() ) { - Parser::Token token( Parser::Token::Positional, arg ); - arg = ""; - if( token.data[0] == '-' ) { - if( token.data.size() > 1 && token.data[1] == '-' ) { - token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); - } - else { - token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); - if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { - arg = "-" + token.data.substr( 1 ); - token.data = token.data.substr( 0, 1 ); - } - } - } - if( token.type != Parser::Token::Positional ) { - std::size_t pos = token.data.find_first_of( separators ); - if( pos != std::string::npos ) { - arg = token.data.substr( pos+1 ); - token.data = token.data.substr( 0, pos ); - } - } - tokens.push_back( token ); + + void parseIntoTokens( std::string const& arg, std::vector& tokens ) { + for( std::size_t i = 0; i <= arg.size(); ++i ) { + char c = arg[i]; + if( c == '"' ) + inQuotes = !inQuotes; + mode = handleMode( i, c, arg, tokens ); } } - std::string separators; + Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + switch( mode ) { + case None: return handleNone( i, c ); + case MaybeShortOpt: return handleMaybeShortOpt( i, c ); + case ShortOpt: + case LongOpt: + case SlashOpt: return handleOpt( i, c, arg, tokens ); + case Positional: return handlePositional( i, c, arg, tokens ); + default: throw std::logic_error( "Unknown mode" ); + } + } + + Mode handleNone( std::size_t i, char c ) { + if( inQuotes ) { + from = i; + return Positional; + } + switch( c ) { + case '-': return MaybeShortOpt; +#ifdef CLARA_PLATFORM_WINDOWS + case '/': from = i+1; return SlashOpt; +#endif + default: from = i; return Positional; + } + } + Mode handleMaybeShortOpt( std::size_t i, char c ) { + switch( c ) { + case '-': from = i+1; return LongOpt; + default: from = i; return ShortOpt; + } + } + Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) + return mode; + + std::string optName = arg.substr( from, i-from ); + if( mode == ShortOpt ) + for( std::size_t j = 0; j < optName.size(); ++j ) + tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); + else if( mode == SlashOpt && optName.size() == 1 ) + tokens.push_back( Token( Token::ShortOpt, optName ) ); + else + tokens.push_back( Token( Token::LongOpt, optName ) ); + return None; + } + Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) + return mode; + + std::string data = arg.substr( from, i-from ); + tokens.push_back( Token( Token::Positional, data ) ); + return None; + } }; template @@ -4456,21 +4509,21 @@ namespace Clara { return oss.str(); } - ConfigT parse( int argc, char const* const argv[] ) const { + ConfigT parse( std::vector const& args ) const { ConfigT config; - parseInto( argc, argv, config ); + parseInto( args, config ); return config; } - std::vector parseInto( int argc, char const* argv[], ConfigT& config ) const { - std::string processName = argv[0]; + std::vector parseInto( std::vector const& args, ConfigT& config ) const { + std::string processName = args[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); if( lastSlash != std::string::npos ) processName = processName.substr( lastSlash+1 ); m_boundProcessName.set( config, processName ); std::vector tokens; Parser parser; - parser.parseIntoTokens( argc, argv, tokens ); + parser.parseIntoTokens( args, tokens ); return populate( tokens, config ); } @@ -4501,7 +4554,7 @@ namespace Clara { arg.boundField.set( config, tokens[++i].data ); } else { - arg.boundField.setFlag( config ); + arg.boundField.set( config, "true" ); } break; } @@ -4667,8 +4720,11 @@ namespace Catch { std::string line; while( std::getline( f, line ) ) { line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) - addTestOrTags( config, "\"" + line + "\"," ); + if( !line.empty() && !startsWith( line, "#" ) ) { + if( !startsWith( line, "\"" ) ) + line = "\"" + line + "\""; + addTestOrTags( config, line + "," ); + } } } @@ -5209,6 +5265,8 @@ namespace Catch bool aborting; }; + class MultipleReporters; + struct IStreamingReporter : IShared { virtual ~IStreamingReporter(); @@ -5236,6 +5294,8 @@ namespace Catch virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } }; struct IReporterFactory : IShared { @@ -5312,7 +5372,10 @@ namespace Catch { ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Catch::cout() << testCaseInfo.name << std::endl; + if( startsWith( testCaseInfo.name, "#" ) ) + Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl; + else + Catch::cout() << testCaseInfo.name << std::endl; } return matchedTests; } @@ -5453,6 +5516,10 @@ namespace TestCaseTracking { virtual void addChild( Ptr const& child ) = 0; virtual ITracker* findChild( std::string const& name ) = 0; virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isIndexTracker() const = 0; }; class TrackerContext { @@ -5577,6 +5644,10 @@ namespace TestCaseTracking { m_parent->openChild(); } } + + virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } + virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } + void open() { m_runState = Executing; moveToThis(); @@ -5640,13 +5711,16 @@ namespace TestCaseTracking { {} virtual ~SectionTracker(); + virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } + static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( name ) ) { - section = dynamic_cast( childTracker ); - assert( section ); + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = static_cast( childTracker ); } else { section = new SectionTracker( name, ctx, ¤tTracker ); @@ -5671,13 +5745,16 @@ namespace TestCaseTracking { {} virtual ~IndexTracker(); + virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } + static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( name ) ) { - tracker = dynamic_cast( childTracker ); - assert( tracker ); + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = static_cast( childTracker ); } else { tracker = new IndexTracker( name, ctx, ¤tTracker, size ); @@ -5884,6 +5961,11 @@ namespace Catch { while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); Totals deltaTotals = m_totals.delta( prevTotals ); + if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } m_totals.testCases += deltaTotals.testCases; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, @@ -6275,10 +6357,10 @@ namespace Catch { Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; } - int applyCommandLine( int argc, char const* argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { + int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); - m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); + m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); if( m_configData.showHelp ) showHelp( m_configData.processName ); m_config.reset(); @@ -6302,16 +6384,13 @@ namespace Catch { m_config.reset(); } - int run( int argc, char const* argv[] ) { + int run( int argc, char const* const* const argv ) { int returnCode = applyCommandLine( argc, argv ); if( returnCode == 0 ) returnCode = run(); return returnCode; } - int run( int argc, char* argv[] ) { - return run( argc, const_cast( argv ) ); - } int run() { if( m_configData.showHelp ) @@ -6375,13 +6454,31 @@ namespace Catch { #include #include +#ifdef CATCH_CPP14_OR_GREATER +#include +#endif + namespace Catch { - struct LexSort { - bool operator() (TestCase i,TestCase j) const { return (i + static void shuffle( V& vector ) { + RandomNumberGenerator rng; +#ifdef CATCH_CPP14_OR_GREATER + std::shuffle( vector.begin(), vector.end(), rng ); +#else + std::random_shuffle( vector.begin(), vector.end(), rng ); +#endif + } }; inline std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { @@ -6390,14 +6487,12 @@ namespace Catch { switch( config.runOrder() ) { case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end(), LexSort() ); + std::sort( sorted.begin(), sorted.end() ); break; case RunTests::InRandomOrder: { seedRng( config ); - - RandomNumberGenerator rng; - std::random_shuffle( sorted.begin(), sorted.end(), rng ); + RandomNumberGenerator::shuffle( sorted ); } break; case RunTests::InDeclarationOrder: @@ -6416,13 +6511,15 @@ namespace Catch { it != itEnd; ++it ) { std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); - if( !prev.second ){ - Catch::cerr() - << Colour( Colour::Red ) - << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" - << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; - exit(1); + if( !prev.second ) { + std::ostringstream ss; + + ss << Colour( Colour::Red ) + << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; + + throw std::runtime_error(ss.str()); } } } @@ -7481,7 +7578,7 @@ namespace Catch { return os; } - Version libraryVersion( 1, 3, 5, "", 0 ); + Version libraryVersion( 1, 5, 7, "", 0 ); } @@ -8460,13 +8557,18 @@ public: // IStreamingReporter ++it ) (*it)->skipTest( testInfo ); } + + virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { + return this; + } + }; Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { Ptr resultingReporter; if( existingReporter ) { - MultipleReporters* multi = dynamic_cast( existingReporter.get() ); + MultipleReporters* multi = existingReporter->tryAsMulti(); if( !multi ) { multi = new MultipleReporters; resultingReporter = Ptr( multi ); @@ -8646,7 +8748,7 @@ namespace Catch { virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - virtual bool assertionEnded( AssertionStats const& assertionStats ) { + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { assert( !m_sectionStack.empty() ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back( assertionStats ); @@ -8856,9 +8958,10 @@ namespace Catch { break; default: - // Escape control chars - based on contribution by @espenalb in PR #465 + // Escape control chars - based on contribution by @espenalb in PR #465 and + // by @mrpi PR #588 if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) - os << "&#x" << std::uppercase << std::hex << static_cast( c ); + os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast( c ) << ';'; else os << c; } @@ -8913,13 +9016,20 @@ namespace Catch { : m_tagIsOpen( false ), m_needsNewline( false ), m_os( &Catch::cout() ) - {} + { + // We encode control characters, which requires + // XML 1.1 + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + *m_os << "\n"; + } XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), m_os( &os ) - {} + { + *m_os << "\n"; + } ~XmlWriter() { while( !m_tags.empty() ) @@ -9086,7 +9196,7 @@ namespace Catch { virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); + m_xml.startElement( "TestCase" ).writeAttribute( "name", testInfo.name ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); @@ -9777,7 +9887,7 @@ namespace Catch { if( totals.testCases.total() == 0 ) { stream << Colour( Colour::Warning ) << "No tests ran\n"; } - else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) { + else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 80339d0f..f3bc1a21 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -16,9 +16,12 @@ #include +#include #include #include +#include "../static_libs/chaiscript_parser.hpp" +#include "../static_libs/chaiscript_stdlib.hpp" @@ -34,7 +37,7 @@ TEST_CASE("C++11 Lambdas Can Be Registered") // We cannot deduce the type of a lambda expression, you must either wrap it // in an std::function or provide the signature - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::fun([]()->std::string { return "hello"; } ), "f1"); @@ -49,7 +52,7 @@ TEST_CASE("C++11 Lambdas Can Be Registered") // dynamic_object tests TEST_CASE("Dynamic_Object attributes can be shared with C++") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai("attr bob::z; def bob::bob() { this.z = 10 }; auto x = bob()"); @@ -77,7 +80,7 @@ TEST_CASE("Dynamic_Object attributes can be shared with C++") TEST_CASE("Function objects can be created from chaiscript functions") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.eval("def func() { print(\"Hello World\"); } "); @@ -91,7 +94,7 @@ TEST_CASE("Function objects can be created from chaiscript functions") TEST_CASE("ChaiScript can be created and destroyed on heap") { - chaiscript::ChaiScript *chai = new chaiscript::ChaiScript(); + auto *chai = new chaiscript::ChaiScript_Basic(create_chaiscript_stdlib(),create_chaiscript_parser()); delete chai; } @@ -123,7 +126,7 @@ void arithmetic_conversions_f_func_return(const std::function()); @@ -189,7 +192,7 @@ TEST_CASE("Throw an int") TEST_CASE("Throw int or double") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); try { chai.eval("throw(1.0)", chaiscript::exception_specification()); @@ -201,7 +204,7 @@ TEST_CASE("Throw int or double") TEST_CASE("Throw a runtime_error") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); try { chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification()); @@ -221,7 +224,7 @@ TEST_CASE("Throw a runtime_error") TEST_CASE("Throw unhandled type") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); try { chai.eval("throw(\"error\")", chaiscript::exception_specification()); @@ -250,7 +253,7 @@ int expected_eval_errors_test_one(const int &) TEST_CASE("No unexpected exceptions leak") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::fun(&expected_eval_errors_test_one), "test_fun"); chai.eval("def guard_fun(i) : i.get_type_info().is_type_arithmetic() {} "); @@ -313,7 +316,7 @@ int function_ordering_test_two(int &) TEST_CASE("Function ordering") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.eval("def test_fun(x) { return 3; }"); chai.eval("def test_fun(x) : x == \"hi\" { return 4; }"); chai.eval("def test_fun(double d) { return 5; }"); @@ -339,7 +342,7 @@ int functor_cast_test_call(const std::function &f, int val) TEST_CASE("Functor cast") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::fun(&functor_cast_test_call), "test_call"); @@ -358,10 +361,10 @@ int set_state_test_myfun() TEST_CASE("Set and restore chai state") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); // save the initial state of globals and locals - chaiscript::ChaiScript::State firststate = chai.get_state(); + auto firststate = chai.get_state(); std::map locals = chai.get_locals(); // add some new globals and locals @@ -404,7 +407,7 @@ class Short_Comparison_Test { TEST_CASE("Short comparison with int") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::user_type(), "Test"); chai.add(chaiscript::constructor(), "Test"); @@ -427,7 +430,7 @@ class Type_Name_MyClass TEST_CASE("Test lookup of type names") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); auto type = chaiscript::user_type(); chai.add(type, "MyClass"); @@ -453,13 +456,13 @@ int simultaneous_chaiscript_do_something_else(int i) TEST_CASE("Simultaneous ChaiScript tests") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::fun(&simultaneous_chaiscript_do_something), "do_something"); chai.add(chaiscript::var(1), "i"); for (int i = 0; i < 10; ++i) { - chaiscript::ChaiScript chai2; + chaiscript::ChaiScript_Basic chai2(create_chaiscript_stdlib(),create_chaiscript_parser()); chai2.add(chaiscript::fun(&simultaneous_chaiscript_do_something_else), "do_something_else"); CHECK(chai.eval("do_something(" + std::to_string(i) + ")") == i + 2); @@ -508,7 +511,7 @@ TEST_CASE("Utility_Test utility class wrapper") ); - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(m); CHECK(chai.eval("auto t = Utility_Test(); t.function2(); ") == "Function2"); @@ -551,7 +554,7 @@ TEST_CASE("Utility_Test utility class wrapper for enum") ); - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(m); CHECK(chai.eval("ONE ") == 0); @@ -646,7 +649,7 @@ TEST_CASE("Object copy counts") m->add(chaiscript::constructor(), "Object_Copy_Count_Test"); m->add(chaiscript::fun(&object_copy_count_create), "create"); - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(m); chai.eval(" { auto i = create(); } "); @@ -702,7 +705,7 @@ TEST_CASE("Object lifetime tests") m->add(chaiscript::constructor(), "Object_Lifetime_Test"); m->add(chaiscript::fun(&Object_Lifetime_Test::count), "count"); - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(m); CHECK(chai.eval("count()") == 0); @@ -755,7 +758,7 @@ Object_Lifetime_Vector2 Object_Lifetime_Vector2_GetValue() TEST_CASE("Object lifetime test 2") { - chaiscript::ChaiScript _script; + chaiscript::ChaiScript_Basic _script(create_chaiscript_stdlib(),create_chaiscript_parser()); //Registering stuff _script.add(chaiscript::user_type>(), "Object_Lifetime_Vector2f"); @@ -796,7 +799,7 @@ int myfunction(Non_Poly_Base *) TEST_CASE("Test Derived->Base with non-polymorphic classes") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::base_class()); Non_Poly_Derived d; chai.add(chaiscript::var(&d), "d"); @@ -815,7 +818,7 @@ struct TestCppVariableScope TEST_CASE("Variable Scope When Calling From C++") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::user_type(), "Test"); chai.add(chaiscript::constructor(), "Test"); chai.add(chaiscript::fun(&TestCppVariableScope::print), "print"); @@ -838,7 +841,7 @@ TEST_CASE("Variable Scope When Calling From C++") TEST_CASE("Variable Scope When Calling From C++ 2") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.eval("var obj = 2;"); auto func = chai.eval>("fun(){ return obj; }"); CHECK_THROWS(func()); @@ -855,7 +858,7 @@ void longlong(long long i) { TEST_CASE("Test long long dispatch") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::fun(&longlong), "longlong"); chai.add(chaiscript::fun(&ulonglong), "ulonglong"); chai.eval("longlong(15)"); @@ -875,7 +878,7 @@ struct Returned_Converted_Config TEST_CASE("Return of converted type from script") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::constructor(), "Returned_Converted_Config"); chai.add(chaiscript::fun(&Returned_Converted_Config::num_iterations), "num_iterations"); @@ -923,7 +926,7 @@ int get_value_a(const std::map &t_m) TEST_CASE("Map conversions") { - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); chai.add(chaiscript::map_conversion>()); chai.add(chaiscript::fun(&get_value_a), "get_value_a"); @@ -943,7 +946,7 @@ TEST_CASE("Parse floats with non-posix locale") #else std::setlocale(LC_ALL, "en_ZA.utf8"); #endif - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); const double parsed = chai.eval("print(1.3); 1.3"); CHECK(parsed == Approx(1.3)); const std::string str = chai.eval("to_string(1.3)"); @@ -952,4 +955,110 @@ TEST_CASE("Parse floats with non-posix locale") +bool FindBitmap(int &ox, int &oy, long) { + ox = 1; + oy = 2; + return true; +} + +TEST_CASE("Mismatched numeric types only convert necessary params") +{ + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + + chai.add(chaiscript::fun(&FindBitmap), "FindBitmap"); + int x = 0; + int y = 0; + chai.add(chaiscript::var(&x), "x"); + chai.add(chaiscript::var(&y), "y"); + chai.eval( "if ( FindBitmap ( x, y, 0) ) { print(\"found at \" + to_string(x) + \", \" + to_string(y))}" ); + CHECK(x == 1); + CHECK(y == 2); + +} + +TEST_CASE("type_conversion to bool") +{ + auto module = std::make_shared(); + struct T { + operator bool() const { return true; } + }; + module->add(chaiscript::type_conversion()); +} + +TEST_CASE("Make sure ChaiScript object still compiles / executes") +{ + chaiscript::ChaiScript chai; +} + +TEST_CASE("Test stdlib options") +{ + const auto test_has_external_scripts = [](chaiscript::ChaiScript_Basic &chai) { + CHECK_NOTHROW(chai.eval("`use`")); + CHECK_NOTHROW(chai.eval("`eval_file`")); + }; + + const auto test_no_external_scripts = [](chaiscript::ChaiScript_Basic &chai) { + CHECK_THROWS(chai.eval("`use`")); + CHECK_THROWS(chai.eval("`eval_file`")); + }; + + const auto test_has_load_modules = [](chaiscript::ChaiScript_Basic &chai) { + CHECK_NOTHROW(chai.eval("`load_module`")); + }; + + const auto test_no_load_modules = [](chaiscript::ChaiScript_Basic &chai) { + CHECK_THROWS(chai.eval("`load_module`")); + }; + + SECTION( "Defaults" ) { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + test_has_external_scripts(chai); + test_has_load_modules(chai); + } + + SECTION( "Load_Modules, External_Scripts" ) { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, + {chaiscript::Options::Load_Modules, chaiscript::Options::External_Scripts} ); + test_has_external_scripts(chai); + test_has_load_modules(chai); + } + + SECTION( "No_Load_Modules, No_External_Scripts" ) { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts} ); + test_no_external_scripts(chai); + test_no_load_modules(chai); + } + + SECTION( "No_Load_Modules, Load_Modules" ) { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::Load_Modules} ); + test_no_external_scripts(chai); + test_no_load_modules(chai); + } + + SECTION( "No_External_Scripts, External_Scripts" ) { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, + {chaiscript::Options::No_External_Scripts, chaiscript::Options::External_Scripts} ); + test_no_external_scripts(chai); + test_no_load_modules(chai); + } + + SECTION( "No_External_Scripts, Load_Modules" ) { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, + {chaiscript::Options::No_External_Scripts, chaiscript::Options::Load_Modules} ); + test_no_external_scripts(chai); + test_has_load_modules(chai); + } + + SECTION( "External_Scripts, No_Load_Modules" ) { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, + {chaiscript::Options::External_Scripts, chaiscript::Options::No_Load_Modules} ); + test_has_external_scripts(chai); + test_no_load_modules(chai); + } + + +} + diff --git a/unittests/conversion_to_bool.chai b/unittests/conversion_to_bool.chai new file mode 100644 index 00000000..d5512bf1 --- /dev/null +++ b/unittests/conversion_to_bool.chai @@ -0,0 +1,17 @@ + +// all we care is that this executes, really + +add_type_conversion(type("int"), type("bool"), fun(int i) { return i != 0; }); + +if (0) { + assert_true(false); +} + +while (0) { + assert_true(false); +} + +for (; 0; ) { + assert_true(false); +} + diff --git a/unittests/dispatch_functions.chai b/unittests/dispatch_functions.chai index 1887844e..67805164 100644 --- a/unittests/dispatch_functions.chai +++ b/unittests/dispatch_functions.chai @@ -1,7 +1,6 @@ assert_equal(`==`, `==`); assert_not_equal(`==`, `<`); assert_equal(`<`.get_arity(), 2); -assert_equal(`+`.get_annotation(), "Multiple method dispatch function wrapper."); assert_equal(get_arity.get_contained_functions().size(), 0); assert_equal(get_arity.get_arity(), 1); assert_equal(get_arity.get_param_types().size(), 2); diff --git a/unittests/eval_error.chai b/unittests/eval_error.chai index fde7847b..756cd15c 100644 --- a/unittests/eval_error.chai +++ b/unittests/eval_error.chai @@ -35,4 +35,4 @@ def while_doing() auto f = fun() { while_doing(); } -assert_equal(get_eval_error(f).call_stack.size(), 16) +assert_true(get_eval_error(f).call_stack.size() <= 16) diff --git a/unittests/execution_context.chai b/unittests/execution_context.chai new file mode 100644 index 00000000..2944827d --- /dev/null +++ b/unittests/execution_context.chai @@ -0,0 +1,42 @@ + + +assert_equal(__LINE__, 3) + +def f() { + [__LINE__, __CLASS__, __FUNC__] +} + +var res = f() + +assert_equal(res[0], 6) +assert_equal(res[1], "NOT_IN_CLASS") +assert_equal(res[2], "f") + +assert_equal(__CLASS__, "NOT_IN_CLASS") +assert_equal(__FUNC__, "NOT_IN_FUNCTION") + +class C +{ + def C() {} + def member() { [__LINE__, __CLASS__, __FUNC__]; } +} + +var c = C(); + +var res2 = c.member(); + +assert_equal(res2[0], 21) +assert_equal(res2[1], "C") +assert_equal(res2[2], "member") + +def C::member2() { [__LINE__, __CLASS__, __FUNC__]; } + +var res3 = c.member2(); + +assert_equal(res3[0], 32) +assert_equal(res3[1], "C") +assert_equal(res3[2], "member2") + +assert_true(__FILE__.find("execution_context.chai") != -1) + + diff --git a/unittests/function_introspection.chai b/unittests/function_introspection.chai index 6d808dfe..fbfee5a4 100644 --- a/unittests/function_introspection.chai +++ b/unittests/function_introspection.chai @@ -10,7 +10,6 @@ def test_function(a) // test_function tests assert_equal(test_function.get_arity(), 1); -assert_equal(trim(test_function.get_annotation()), "#Test Function Description"); assert_equal(test_function.get_contained_functions().size(), 0); assert_equal(test_function.get_param_types().size(), 2); diff --git a/unittests/fuzzy_tests-2015-07-16.tar.bz2 b/unittests/fuzzy_tests-2015-07-16.tar.bz2 deleted file mode 100644 index 9768b8e4..00000000 Binary files a/unittests/fuzzy_tests-2015-07-16.tar.bz2 and /dev/null differ diff --git a/unittests/fuzzy_tests-2016-06-29.tar.bz2 b/unittests/fuzzy_tests-2016-06-29.tar.bz2 new file mode 100644 index 00000000..95fd20ae Binary files /dev/null and b/unittests/fuzzy_tests-2016-06-29.tar.bz2 differ diff --git a/unittests/global_lcase.chai b/unittests/global_lcase.chai index 7f8959a6..21fdecdb 100644 --- a/unittests/global_lcase.chai +++ b/unittests/global_lcase.chai @@ -16,3 +16,8 @@ def f() { f(); + +global &h = v; +assert_true(h == v) +v = 3; +assert_true(h == 3) diff --git a/unittests/init_if.chai b/unittests/init_if.chai new file mode 100644 index 00000000..01c05c7e --- /dev/null +++ b/unittests/init_if.chai @@ -0,0 +1,7 @@ +if (var x = 2; x == 3) { + assert_true(false); +} else if (var y = 3; y == 6) { + assert_true(false); +} else { + assert_equal(5, y + x); +} diff --git a/unittests/integer_literal_test.cpp b/unittests/integer_literal_test.cpp index 58e954a9..0cb41c37 100644 --- a/unittests/integer_literal_test.cpp +++ b/unittests/integer_literal_test.cpp @@ -1,4 +1,7 @@ -#include +#include +#include "../static_libs/chaiscript_parser.hpp" +#include "../static_libs/chaiscript_stdlib.hpp" + #define TEST_LITERAL(v) test_literal(v, #v) @@ -6,7 +9,7 @@ template bool test_literal(T val, const std::string &str) { std::cout << "Comparing : " << val; - chaiscript::ChaiScript chai; + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); T val2 = chai.eval(str); std::cout << " " << val2 << '\n'; return val == val2; diff --git a/unittests/multifile_test_chai.cpp b/unittests/multifile_test_chai.cpp index ac288ffc..f57157d4 100644 --- a/unittests/multifile_test_chai.cpp +++ b/unittests/multifile_test_chai.cpp @@ -1,14 +1,16 @@ #include "multifile_test_chai.hpp" #include +#include Multi_Test_Chai::Multi_Test_Chai() - : m_chai(new chaiscript::ChaiScript(chaiscript::Std_Lib::library())) + : m_chai(new chaiscript::ChaiScript_Basic(chaiscript::Std_Lib::library(), + std::make_unique>())) { } -std::shared_ptr Multi_Test_Chai::get_chai() +std::shared_ptr Multi_Test_Chai::get_chai() { return m_chai; } diff --git a/unittests/multifile_test_chai.hpp b/unittests/multifile_test_chai.hpp index c2e306d5..681c2c2a 100644 --- a/unittests/multifile_test_chai.hpp +++ b/unittests/multifile_test_chai.hpp @@ -1,14 +1,14 @@ -#include +#include class Multi_Test_Chai { public: Multi_Test_Chai(); - std::shared_ptr get_chai(); + std::shared_ptr get_chai(); private: - std::shared_ptr m_chai; + std::shared_ptr m_chai; }; diff --git a/unittests/multifile_test_main.cpp b/unittests/multifile_test_main.cpp index 94bbb6f9..24b03b2d 100644 --- a/unittests/multifile_test_main.cpp +++ b/unittests/multifile_test_main.cpp @@ -1,14 +1,14 @@ #include "multifile_test_chai.hpp" #include "multifile_test_module.hpp" -#include +#include int main() { Multi_Test_Chai chaitest; Multi_Test_Module chaimodule; - std::shared_ptr chai = chaitest.get_chai(); + std::shared_ptr chai = chaitest.get_chai(); chai->add(chaimodule.get_module()); return chai->eval("get_module_value()"); } diff --git a/unittests/multifile_test_module.cpp b/unittests/multifile_test_module.cpp index 2457416f..ec1df238 100644 --- a/unittests/multifile_test_module.cpp +++ b/unittests/multifile_test_module.cpp @@ -1,4 +1,4 @@ -#include +#include #include "multifile_test_module.hpp" diff --git a/unittests/multifile_test_module.hpp b/unittests/multifile_test_module.hpp index f01d8f1a..b09dca39 100644 --- a/unittests/multifile_test_module.hpp +++ b/unittests/multifile_test_module.hpp @@ -1,4 +1,4 @@ -#include +#include class Multi_Test_Module { diff --git a/unittests/multithreaded_test.cpp b/unittests/multithreaded_test.cpp index 225353c0..ff06620a 100644 --- a/unittests/multithreaded_test.cpp +++ b/unittests/multithreaded_test.cpp @@ -2,7 +2,8 @@ #include -#include +#include +#include int expected_value(int num_iters) { @@ -15,7 +16,7 @@ int expected_value(int num_iters) return i; } -void do_work(chaiscript::ChaiScript &c, const size_t id) +void do_work(chaiscript::ChaiScript_Basic &c, const size_t id) { try{ std::stringstream ss; @@ -62,7 +63,12 @@ int main() modulepaths.push_back(modulepath); } - chaiscript::ChaiScript chai(modulepaths,usepaths); + + // For this test we are going to load the dynamic stdlib + // to make sure it continues to work + chaiscript::ChaiScript_Basic chai( + std::make_unique>(), + modulepaths,usepaths); std::vector > threads; diff --git a/unittests/precedence_4.chai b/unittests/precedence_4.chai new file mode 100644 index 00000000..b55c83d1 --- /dev/null +++ b/unittests/precedence_4.chai @@ -0,0 +1,4 @@ + +var i = 2; + +assert_equal(++i * i, 9) diff --git a/unittests/ranged_for.chai b/unittests/ranged_for.chai new file mode 100644 index 00000000..9ef34fed --- /dev/null +++ b/unittests/ranged_for.chai @@ -0,0 +1,21 @@ +var sum = 0; + +for (x : [1,2,3,4]) { + sum += x; +} + +assert_equal(sum, 10) + +var result = 0.0; + +for (x : retro(range([1,2,3,4]))) { + if (result == 0.0) { + result = x; + } else { + result /= x; + } +} + +assert_true(result > .6 && result < .7); + + diff --git a/unittests/reflection_test.chai b/unittests/reflection_test.chai index 16ef7e9f..c7bc89d6 100644 --- a/unittests/reflection_test.chai +++ b/unittests/reflection_test.chai @@ -1,22 +1,9 @@ -auto& parser = ChaiScript_Parser() -auto parse_success = parser.parse("3 + 4", "INPUT") -auto& a = parser.ast() +auto a = parse("3 + 4") assert_equal(eval(a), 7) -auto& childs = a.children.front().children -auto& node = childs[0] - -auto& parser2 = ChaiScript_Parser() -parser2.parse("9", "INPUT") - - -a.children.front().replace_child(childs[0], parser2.ast()) - -assert_equal(eval(a), 13) -assert_equal(node.filename, "INPUT") - - +assert_equal(1, a.children.size()); +assert_equal("3 + 4", a.children[0].text()); def my_fun() { diff --git a/unittests/string_unicode_ascii.chai b/unittests/string_unicode_ascii.chai new file mode 100644 index 00000000..aca62d8f --- /dev/null +++ b/unittests/string_unicode_ascii.chai @@ -0,0 +1,8 @@ +assert_equal('\u0020', ' ') +assert_equal('\u0021', '!') +assert_equal('\u0030', '0') +assert_equal('\u0040', '@') +assert_equal('\u005B', '[') +assert_equal('\u005d', ']') +assert_equal('\u0061', 'a') +assert_equal('\u007e', '~') diff --git a/unittests/string_unicode_parse.chai b/unittests/string_unicode_parse.chai new file mode 100644 index 00000000..50da68bb --- /dev/null +++ b/unittests/string_unicode_parse.chai @@ -0,0 +1,11 @@ +assert_equal('\u00aa', '\u00AA') +assert_equal('\u00bb', '\uBB') +assert_equal('\ucc', '\u00CC') +assert_equal('\udd', '\uDD') + +assert_equal('\u0ee', '\uEE') +assert_equal('\ue', '\u000E') + +assert_equal("\u30\u31\u32", "012") +assert_equal("\u33Test", "3Test") +assert_equal("Test\u0040", "Test@") diff --git a/unittests/string_unicode_unicode.chai b/unittests/string_unicode_unicode.chai new file mode 100644 index 00000000..2a237ed8 --- /dev/null +++ b/unittests/string_unicode_unicode.chai @@ -0,0 +1,5 @@ +assert_equal("\uc39c", "Ü") +assert_equal("U for \uc39cmlauts", "U for Ümlauts") +assert_equal("More \uc39cml\uc3a4\uc3bcts", "More Ümläüts") + +assert_equal("Thorn \uc3be sign", "Thorn þ sign")