diff --git a/.travis.yml b/.travis.yml index c7a33cf2..888922b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ addons: packages: - g++-4.9 - g++-5 + - g++-6 coverity_scan: project: name: "ChaiScript/ChaiScript" @@ -25,12 +26,23 @@ matrix: compiler: gcc - os: linux sudo: false - env: GCC_VER="5" + env: GCC_VER="4.9" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 + compiler: gcc + - os: linux + sudo: false + env: GCC_VER="5" CPPCHECK=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" + compiler: gcc + - os: linux + sudo: false + env: GCC_VER="6" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" compiler: gcc - os: osx compiler: clang osx_image: xcode8 - + - os: osx + compiler: clang + osx_image: xcode8 + env: CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 env: global: @@ -40,14 +52,13 @@ env: before_install: - if [ "${GCC_VER}" != "" ]; then export CXX="g++-$GCC_VER" CC="gcc-$GCC_VER" GCOV="gcov-$GCC_VER" ; fi - - if [ "${GCC_VER}" == "5" ]; then export CPPCHECK=1 COVERAGE=1 FUZZY_CMD="-D RUN_FUZZY_TESTS:BOOL=TRUE" ; fi - pip install --user cpp-coveralls script: - - cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $FUZZY_CMD . + - cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $CMAKE_OPTIONS . - cmake --build . -- -j2 - - ctest - - if [ ${COVERAGE} = 1 ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; fi + - if [ "${BUILD_ONLY}" != "1" ]; then ctest; fi + - if [ "${COVERAGE}" = "1" ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; fi #after_script: # - if [ ${CPPCHECK} = 1 ]; then contrib/codeanalysis/runcppcheck.sh ; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index fbba252b..effc7aed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ ELSE() project(chaiscript) option(MULTITHREAD_SUPPORT_ENABLED "Multithreaded Support Enabled" TRUE) +option(DYNLOAD_ENABLED "Dynamic Loading Support Enabled" TRUE) option(BUILD_MODULES "Build Extra Modules (stl)" TRUE) @@ -21,6 +22,7 @@ option(BUILD_SAMPLES "Build Samples Folder" FALSE) option(RUN_FUZZY_TESTS "Run tests generated by AFL" FALSE) option(USE_STD_MAKE_SHARED "Use std::make_shared instead of chaiscript::make_shared" FALSE) option(RUN_PERFORMANCE_TESTS "Run Performance Tests" FALSE) +option(BUILD_IN_CPP17_MODE "Build with C++17 flags" FALSE) mark_as_advanced(USE_STD_MAKE_SHARED) @@ -149,12 +151,20 @@ if(CMAKE_COMPILER_IS_GNUCC) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if(GCC_VERSION VERSION_LESS 4.9) - set(CPP11_FLAG "-std=c++1y") + set(CPP14_FLAG "-std=c++1y") else() - set(CPP11_FLAG "-std=c++14") + if (BUILD_IN_CPP17_MODE) + set(CPP14_FLAG "-std=c++1z") + else() + set(CPP14_FLAG "-std=c++14") + endif() endif() else() - set(CPP11_FLAG "-std=c++14") + if (BUILD_IN_CPP17_MODE) + set(CPP14_FLAG "-std=c++1z") + else() + set(CPP14_FLAG "-std=c++14") + endif() endif() if(MSVC) @@ -178,7 +188,7 @@ if(MSVC) # how to workaround or fix the error. So I'm disabling it globally. add_definitions(/wd4503) else() - add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP11_FLAG}) + add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP14_FLAG}) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command) @@ -196,12 +206,12 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") if(USE_LIBCXX) add_definitions(-stdlib=libc++) - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG} -stdlib=libc++") + set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG} -stdlib=libc++") else() - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG}") + set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}") endif() elseif(CMAKE_COMPILER_IS_GNUCC) - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG}") + set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}") endif() # limitations in MinGW require us to make an optimized build @@ -221,9 +231,15 @@ if(NOT MULTITHREAD_SUPPORT_ENABLED) add_definitions(-DCHAISCRIPT_NO_THREADS) endif() +if(NOT DYNLOAD_ENABLED) + add_definitions(-DCHAISCRIPT_NO_DYNLOAD) +endif() + if(CMAKE_HOST_UNIX) - if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku") - list(APPEND LIBS "dl") + if(DYNLOAD_ENABLED) + if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku") + list(APPEND LIBS "dl") + endif() endif() if(MULTITHREAD_SUPPORT_ENABLED) @@ -298,32 +314,17 @@ 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-2016-06-29.tar.bz2 + COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2017-07-20.tar.bz2 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests ) - file(GLOB FUZZY_CRASH_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/id*) - list(SORT FUZZY_CRASH_TESTS) + file(GLOB FUZZY_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/MINIMIZED/*) + list(SORT FUZZY_TESTS) - file(GLOB FUZZY_EXCEPTION_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/id*) - list(SORT FUZZY_EXCEPTION_TESTS) - - - foreach(filename ${FUZZY_CRASH_TESTS}) + foreach(filename ${FUZZY_TESTS}) message(STATUS "Adding test ${filename}") - add_test(${filename} chai "-e" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename}) - endforeach() - - set_property(TEST ${FUZZY_CRASH_TESTS} - PROPERTY ENVIRONMENT - "CHAI_USE_PATH=${CMAKE_BINARY_DIR}/unittests/" - "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" - ) - - foreach(filename ${FUZZY_EXCEPTION_TESTS}) - message(STATUS "Adding test ${filename}") - add_test(${filename} chai "--exception" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename}) + add_test(fuzz.${filename} chai "-e" "--exception" "--any-exception" ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzz_unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename}) endforeach() set_property(TEST ${FUZZY_EXCEPTION_TESTS} @@ -427,6 +428,9 @@ if(BUILD_TESTING) target_link_libraries(compiled_tests ${LIBS} ${CHAISCRIPT_LIBS}) ADD_CATCH_TESTS(compiled_tests) + add_executable(static_chaiscript_test unittests/static_chaiscript.cpp) + target_link_libraries(static_chaiscript_test ${LIBS}) + add_test(NAME Static_ChaiScript_Test COMMAND static_chaiscript_test) add_executable(boxed_cast_test unittests/boxed_cast_test.cpp) target_link_libraries(boxed_cast_test ${LIBS}) diff --git a/appveyor.yml b/appveyor.yml index 50c59211..042e1e59 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,15 +1,17 @@ version: 5.8.x.{build} -os: Visual Studio 2015 +image: + - Visual Studio 2017 environment: matrix: - - {} + - VS_VERSION: "Visual Studio 14" + - VS_VERSION: "Visual Studio 15" build_script: - cmd: >- mkdir build cd build - cmake c:\Projects\chaiscript -G "Visual Studio 14" + cmake c:\Projects\chaiscript -G "%VS_VERSION%" cmake --build . --config Debug test_script: diff --git a/cheatsheet.md b/cheatsheet.md index 2e2c218f..8cf3d3f4 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -15,6 +15,9 @@ chaiscript::ChaiScript chai; // loads stdlib from loadable module on file system chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); // compiles in stdlib ``` +Note that ChaiScript cannot be used as a global / static object unless it is being compiled with `CHAISCRIPT_NO_THREADS`. + + # Adding Things To The Engine ## Adding a Function / Method / Member diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 2d28c962..dbf6d73b 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -832,7 +832,7 @@ namespace chaiscript public: ChaiScript(std::vector t_modulepaths = {}, std::vector t_usepaths = {}, - const std::vector &t_opts = {}) + const std::vector &t_opts = chaiscript::default_options()) : ChaiScript_Basic( chaiscript::Std_Lib::library(), std::make_unique>(), diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index cc349a0b..5b2e84f4 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -93,6 +93,16 @@ namespace chaiscript { #endif } + template + inline std::unique_ptr make_unique(Arg && ... arg) + { +#ifdef CHAISCRIPT_USE_STD_MAKE_SHARED + return std::make_unique(std::forward(arg)...); +#else + return std::unique_ptr(static_cast(new D(std::forward(arg)...))); +#endif + } + struct Build_Info { static int version_major() { @@ -216,7 +226,11 @@ namespace chaiscript { static inline std::vector default_options() { +#ifdef CHAISCRIPT_NO_DYNLOAD + return {Options::No_Load_Modules, Options::External_Scripts}; +#else return {Options::Load_Modules, Options::External_Scripts}; +#endif } } #endif diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index cb5e129c..79feaa1e 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -67,44 +67,44 @@ namespace chaiscript class Thread_Storage { public: - - explicit Thread_Storage(void *t_key) - : m_key(t_key) - { - } + Thread_Storage() = default; + Thread_Storage(const Thread_Storage &) = delete; + Thread_Storage(Thread_Storage &&) = delete; + Thread_Storage &operator=(const Thread_Storage &) = delete; + Thread_Storage &operator=(Thread_Storage &&) = delete; ~Thread_Storage() { - t().erase(m_key); + t().erase(this); } inline const T *operator->() const { - return &(t()[m_key]); + return &(t()[this]); } inline const T &operator*() const { - return t()[m_key]; + return t()[this]; } inline T *operator->() { - return &(t()[m_key]); + return &(t()[this]); } inline T &operator*() { - return t()[m_key]; + return t()[this]; } void *m_key; private: - static std::unordered_map &t() + static std::unordered_map &t() { - thread_local static std::unordered_map my_t; + thread_local std::unordered_map my_t; return my_t; } }; @@ -144,7 +144,7 @@ namespace chaiscript class Thread_Storage { public: - explicit Thread_Storage(void *) + explicit Thread_Storage() { } diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index cbb91a51..8aedb353 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -305,13 +305,13 @@ namespace chaiscript static bool has_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) { const auto pf = std::dynamic_pointer_cast(t_pf); - return pf && pf->get_parse_tree(); + return bool(pf); } - static chaiscript::AST_NodePtr get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) + static const chaiscript::AST_Node &get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) { const auto pf = std::dynamic_pointer_cast(t_pf); - if (pf && pf->get_parse_tree()) + if (pf) { return pf->get_parse_tree(); } else { @@ -545,7 +545,7 @@ namespace chaiscript std::vector retval; std::transform(t_eval_error.call_stack.begin(), t_eval_error.call_stack.end(), std::back_inserter(retval), - &chaiscript::var &>); + &chaiscript::var); return retval; }), "call_stack"} } ); @@ -574,7 +574,7 @@ namespace chaiscript const auto children = t_node.get_children(); std::transform(children.begin(), children.end(), std::back_inserter(retval), - &chaiscript::var &>); + &chaiscript::var &>); return retval; }), "children"} } diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 6710ed00..7f409a3a 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -335,9 +335,24 @@ namespace chaiscript template void back_insertion_sequence_type(const std::string &type, Module& m) { - typedef typename ContainerType::reference (ContainerType::*backptr)(); - - m.add(fun(static_cast(&ContainerType::back)), "back"); + m.add(fun([](ContainerType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.back()); + } + } + ) + , "back"); + m.add(fun([](const ContainerType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.back()); + } + } + ) + , "back"); typedef void (ContainerType::*push_back)(const typename ContainerType::value_type &); @@ -380,13 +395,29 @@ namespace chaiscript template void front_insertion_sequence_type(const std::string &type, Module& m) { - typedef typename ContainerType::reference (ContainerType::*front_ptr)(); - typedef typename ContainerType::const_reference (ContainerType::*const_front_ptr)() const; typedef void (ContainerType::*push_ptr)(typename ContainerType::const_reference); typedef void (ContainerType::*pop_ptr)(); - m.add(fun(static_cast(&ContainerType::front)), "front"); - m.add(fun(static_cast(&ContainerType::front)), "front"); + m.add(fun([](ContainerType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + } + ) + , "front"); + + m.add(fun([](const ContainerType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + } + ) + , "front"); + m.add(fun(static_cast(&ContainerType::push_front)), [&]()->std::string{ @@ -577,11 +608,27 @@ namespace chaiscript { m.add(user_type(), type); - typedef typename VectorType::reference (VectorType::*frontptr)(); - typedef typename VectorType::const_reference (VectorType::*constfrontptr)() const; + m.add(fun([](VectorType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + } + ) + , "front"); + + m.add(fun([](const VectorType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + } + ) + , "front"); + - m.add(fun(static_cast(&VectorType::front)), "front"); - m.add(fun(static_cast(&VectorType::front)), "front"); back_insertion_sequence_type(type, m); diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index c4e61983..d8bfb25b 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -452,7 +452,7 @@ namespace chaiscript }; explicit Dispatch_Engine(chaiscript::parser::ChaiScript_Parser_Base &parser) - : m_stack_holder(this), + : m_stack_holder(), m_parser(parser) { } diff --git a/include/chaiscript/dispatchkit/handle_return.hpp b/include/chaiscript/dispatchkit/handle_return.hpp index 8243333b..8570c8d0 100644 --- a/include/chaiscript/dispatchkit/handle_return.hpp +++ b/include/chaiscript/dispatchkit/handle_return.hpp @@ -181,9 +181,9 @@ namespace chaiscript template struct Handle_Return { - static Boxed_Value handle(const Ret &r) + static Boxed_Value handle(Ret r) { - return Boxed_Value(std::cref(r)); + return Boxed_Value(std::move(r)); } }; diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index bb57c698..0c60315f 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -41,7 +41,7 @@ namespace chaiscript class Boxed_Number; struct AST_Node; - typedef std::shared_ptr AST_NodePtr; + typedef std::unique_ptr AST_NodePtr; namespace dispatch { @@ -346,14 +346,15 @@ namespace chaiscript { public: Dynamic_Proxy_Function( - int t_arity=-1, - AST_NodePtr t_parsenode = AST_NodePtr(), + const int t_arity, + std::shared_ptr t_parsenode, Param_Types t_param_types = Param_Types(), 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)) { + // assert(t_parsenode); } @@ -379,9 +380,17 @@ namespace chaiscript return m_guard; } - AST_NodePtr get_parse_tree() const + bool has_parse_tree() const { + return static_cast(m_parsenode); + } + + const AST_Node &get_parse_tree() const { - return m_parsenode; + if (m_parsenode) { + return *m_parsenode; + } else { + throw std::runtime_error("Dynamic_Proxy_Function does not have parse_tree"); + } } @@ -445,7 +454,7 @@ namespace chaiscript private: Proxy_Function m_guard; - AST_NodePtr m_parsenode; + std::shared_ptr m_parsenode; }; @@ -457,7 +466,7 @@ namespace chaiscript Dynamic_Proxy_Function_Impl( Callable t_f, int t_arity=-1, - AST_NodePtr t_parsenode = AST_NodePtr(), + std::shared_ptr t_parsenode = AST_NodePtr(), Param_Types t_param_types = Param_Types(), Proxy_Function t_guard = Proxy_Function()) : Dynamic_Proxy_Function( diff --git a/include/chaiscript/dispatchkit/register_function.hpp b/include/chaiscript/dispatchkit/register_function.hpp index ef17fb86..e660790d 100644 --- a/include/chaiscript/dispatchkit/register_function.hpp +++ b/include/chaiscript/dispatchkit/register_function.hpp @@ -48,6 +48,7 @@ namespace chaiscript chaiscript::make_shared>(t)); } + template Proxy_Function fun(Ret (*func)(Param...)) { @@ -77,13 +78,45 @@ namespace chaiscript } - template::value>::type*/> Proxy_Function fun(T Class::* m /*, typename std::enable_if::value>::type* = 0*/ ) { return Proxy_Function(chaiscript::make_shared>(m)); } +// only compile this bit if noexcept is part of the type system +// +#if __cpp_noexcept_function_type >= 201510 + template + Proxy_Function fun(Ret (*func)(Param...) noexcept) + { + auto fun_call = dispatch::detail::Fun_Caller(func); + + return Proxy_Function( + chaiscript::make_shared>(fun_call)); + + } + + template + Proxy_Function fun(Ret (Class::*t_func)(Param...) const noexcept) + { + auto call = dispatch::detail::Const_Caller(t_func); + + return Proxy_Function( + chaiscript::make_shared>(call)); + } + + template + Proxy_Function fun(Ret (Class::*t_func)(Param...) noexcept) + { + auto call = dispatch::detail::Caller(t_func); + + return Proxy_Function( + chaiscript::make_shared>(call)); + + } +#endif + diff --git a/include/chaiscript/dispatchkit/type_conversions.hpp b/include/chaiscript/dispatchkit/type_conversions.hpp index e16693a5..d9d2f374 100644 --- a/include/chaiscript/dispatchkit/type_conversions.hpp +++ b/include/chaiscript/dispatchkit/type_conversions.hpp @@ -338,9 +338,7 @@ namespace chaiscript : m_mutex(), m_conversions(), m_convertableTypes(), - m_num_types(0), - m_thread_cache(this), - m_conversion_saves(this) + m_num_types(0) { } diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 602efd54..b157cac2 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -124,8 +124,10 @@ namespace chaiscript /// \brief Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree - typedef std::shared_ptr AST_NodePtr; - typedef std::shared_ptr AST_NodePtr_Const; + typedef std::unique_ptr AST_NodePtr; + typedef std::unique_ptr AST_NodePtr_Const; + + struct AST_Node_Trace; /// \brief Classes which may be thrown during error cases when ChaiScript is executing. @@ -168,7 +170,7 @@ namespace chaiscript File_Position start_position; std::string filename; std::string detail; - std::vector call_stack; + std::vector call_stack; eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname, const std::vector &t_parameters, const std::vector &t_functions, @@ -228,26 +230,26 @@ namespace chaiscript template static AST_Node_Type id(const T& t) { - return t->identifier; + return t.identifier; } template static std::string pretty(const T& t) { - return t->pretty_print(); + return t.pretty_print(); } template static const std::string &fname(const T& t) { - return t->filename(); + return t.filename(); } template static std::string startpos(const T& t) { std::ostringstream oss; - oss << t->start().line << ", " << t->start().column; + oss << t.start().line << ", " << t.start().column; return oss.str(); } @@ -260,6 +262,7 @@ namespace chaiscript bool t_dot_notation, const chaiscript::detail::Dispatch_Engine &t_ss) { + assert(t_func); int arity = t_func->get_arity(); std::vector types = t_func->get_param_types(); @@ -308,20 +311,20 @@ namespace chaiscript std::shared_ptr dynfun = std::dynamic_pointer_cast(t_func); - if (dynfun) + if (dynfun && dynfun->has_parse_tree()) { Proxy_Function f = dynfun->get_guard(); if (f) { auto dynfunguard = std::dynamic_pointer_cast(f); - if (dynfunguard) + if (dynfunguard && dynfunguard->has_parse_tree()) { retval += " : " + format_guard(dynfunguard->get_parse_tree()); } } - retval += "\n Defined at " + format_location(dynfun->get_parse_tree()); + retval += "\n Defined at " + format_location(dynfun->get_parse_tree()); } return retval; @@ -330,20 +333,15 @@ namespace chaiscript template static std::string format_guard(const T &t) { - return t->pretty_print(); + return t.pretty_print(); } template static std::string format_location(const T &t) { - if (t) { - std::ostringstream oss; - oss << "(" << t->filename() << " " << t->start().line << ", " << t->start().column << ")"; - return oss.str(); - } else { - return "(internal)"; - } - + std::ostringstream oss; + oss << "(" << t.filename() << " " << t.start().line << ", " << t.start().column << ")"; + return oss.str(); } static std::string format_detail(const std::vector &t_functions, @@ -353,6 +351,7 @@ namespace chaiscript std::stringstream ss; if (t_functions.size() == 1) { + assert(t_functions[0]); ss << " Expected: " << format_types(t_functions[0], t_dot_notation, t_ss) << '\n'; } else { ss << " " << t_functions.size() << " overloads available:\n"; @@ -492,7 +491,7 @@ namespace chaiscript /// \brief Struct that doubles as both a parser ast_node and an AST node. - struct AST_Node : std::enable_shared_from_this { + struct AST_Node { public: const AST_Node_Type identifier; const std::string text; @@ -516,14 +515,14 @@ namespace chaiscript oss << text; - for (auto & elem : this->get_children()) { - oss << elem->pretty_print() << ' '; + for (auto & elem : get_children()) { + oss << elem.get().pretty_print() << ' '; } return oss.str(); } - virtual std::vector get_children() const = 0; + virtual std::vector> get_children() const = 0; virtual Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const = 0; @@ -534,8 +533,8 @@ 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->get_children()) { - oss << elem->to_string(t_prepend + " "); + for (auto & elem : get_children()) { + oss << elem.get().to_string(t_prepend + " "); } return oss.str(); } @@ -568,12 +567,60 @@ namespace chaiscript }; + struct AST_Node_Trace + { + const AST_Node_Type identifier; + const std::string text; + Parse_Location location; + + const std::string &filename() const { + return *location.filename; + } + + const File_Position &start() const { + return location.start; + } + + const File_Position &end() const { + return location.end; + } + + std::string pretty_print() const + { + std::ostringstream oss; + + oss << text; + + for (const auto & elem : children) { + oss << elem.pretty_print() << ' '; + } + + return oss.str(); + } + + std::vector get_children(const AST_Node &node) + { + const auto node_children = node.get_children(); + return std::vector(node_children.begin(), node_children.end()); + } + + AST_Node_Trace(const AST_Node &node) + : identifier(node.identifier), text(node.text), + location(node.location), children(get_children(node)) + { + } + + + std::vector children; + + }; + 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 debug_print(const AST_Node &t, std::string prepend = "") const = 0; virtual void *get_tracer_ptr() = 0; virtual ~ChaiScript_Parser_Base() = default; ChaiScript_Parser_Base() = default; diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 5d8b6f0d..0e07f4ce 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -36,12 +36,13 @@ #include #endif -#if defined(_POSIX_VERSION) && !defined(__CYGWIN__) +#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__) #include #endif - -#ifdef CHAISCRIPT_WINDOWS +#if defined(CHAISCRIPT_NO_DYNLOAD) +#include "chaiscript_unknown.hpp" +#elif defined(CHAISCRIPT_WINDOWS) #include "chaiscript_windows.hpp" #elif _POSIX_VERSION #include "chaiscript_posix.hpp" @@ -185,7 +186,7 @@ namespace chaiscript } 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([this](const AST_Node &t_ast){ return eval(t_ast); }), "eval"); 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"); @@ -242,7 +243,7 @@ namespace chaiscript m_parser(std::move(parser)), m_engine(*m_parser) { -#if defined(_POSIX_VERSION) && !defined(__CYGWIN__) +#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__) // If on Unix, add the path of the current executable to the module search path // as windows would do @@ -278,6 +279,7 @@ namespace chaiscript build_eval_system(t_lib, t_opts); } +#ifndef CHAISCRIPT_NO_DYNLOAD /// \brief Constructor for ChaiScript. /// /// This version of the ChaiScript constructor attempts to find the stdlib module to load @@ -307,16 +309,22 @@ namespace chaiscript throw; } } +#else // CHAISCRIPT_NO_DYNLOAD +explicit ChaiScript_Basic(std::unique_ptr &&parser, + std::vector t_module_paths = {}, + std::vector t_use_paths = {}, + const std::vector &t_opts = chaiscript::default_options()) = delete; +#endif parser::ChaiScript_Parser_Base &get_parser() { return *m_parser; } - const Boxed_Value eval(const AST_NodePtr &t_ast) + const Boxed_Value eval(const AST_Node &t_ast) { try { - return t_ast->eval(chaiscript::detail::Dispatch_State(m_engine)); + return t_ast.eval(chaiscript::detail::Dispatch_State(m_engine)); } catch (const exception::eval_error &t_ee) { throw Boxed_Value(t_ee); } @@ -324,9 +332,9 @@ namespace chaiscript AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false) { - const auto ast = m_parser->parse(t_input, "PARSE"); + auto ast = m_parser->parse(t_input, "PARSE"); if (t_debug_print) { - m_parser->debug_print(ast); + m_parser->debug_print(*ast); } return ast; } @@ -548,6 +556,10 @@ namespace chaiscript /// \throw chaiscript::exception::load_module_error In the event that no matching module can be found. std::string load_module(const std::string &t_module_name) { +#ifdef CHAISCRIPT_NO_DYNLOAD + (void)t_module_name; // -Wunused-parameter + throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)"); +#else std::vector errors; std::string version_stripped_name = t_module_name; size_t version_pos = version_stripped_name.find("-" + Build_Info::version()); @@ -581,6 +593,7 @@ namespace chaiscript } throw chaiscript::exception::load_module_error(t_module_name, errors); +#endif } /// \brief Load a binary module from a dynamic library. Works on platforms that support diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 95e2218e..a9819440 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -47,13 +47,13 @@ namespace chaiscript { template struct AST_Node_Impl; - template using AST_Node_Impl_Ptr = typename std::shared_ptr>; + template using AST_Node_Impl_Ptr = typename std::unique_ptr>; namespace detail { /// Helper function that will set up the scope around a function call, including handling the named function parameters template - static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl_Ptr &t_node, const std::vector &t_param_names, const std::vector &t_vals, const std::map *t_locals=nullptr, bool has_this_capture = false) { + static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl &t_node, const std::vector &t_param_names, const std::vector &t_vals, const std::map *t_locals=nullptr, bool has_this_capture = false) { chaiscript::detail::Dispatch_State state(t_ss); const Boxed_Value *thisobj = [&]() -> const Boxed_Value *{ @@ -83,7 +83,7 @@ namespace chaiscript } try { - return t_node->eval(state); + return t_node.eval(state); } catch (detail::Return_Value &rv) { return std::move(rv.retval); } @@ -106,8 +106,14 @@ namespace chaiscript } - std::vector get_children() const final { - return {children.begin(), children.end()}; + std::vector> get_children() const final { + std::vector> retval; + retval.reserve(children.size()); + for (auto &&child : children) { + retval.emplace_back(*child); + } + + return retval; } Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const final @@ -116,7 +122,7 @@ namespace chaiscript T::trace(t_e, this); return eval_internal(t_e); } catch (exception::eval_error &ee) { - ee.call_stack.push_back(shared_from_this()); + ee.call_stack.push_back(*this); throw; } } @@ -282,7 +288,9 @@ namespace chaiscript template struct Fun_Call_AST_Node : 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)) { } + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) { + assert(!this->children.empty()); + } template Boxed_Value do_eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const @@ -364,44 +372,44 @@ namespace chaiscript AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { } - static std::string get_arg_name(const AST_Node_Impl_Ptr &t_node) { - if (t_node->children.empty()) + static std::string get_arg_name(const AST_Node_Impl &t_node) { + if (t_node.children.empty()) { - return t_node->text; - } else if (t_node->children.size() == 1) { - return t_node->children[0]->text; + return t_node.text; + } else if (t_node.children.size() == 1) { + return t_node.children[0]->text; } else { - return t_node->children[1]->text; + return t_node.children[1]->text; } } - static std::vector get_arg_names(const AST_Node_Impl_Ptr &t_node) { + static std::vector get_arg_names(const AST_Node_Impl &t_node) { std::vector retval; - for (const auto &node : t_node->children) + for (const auto &node : t_node.children) { - retval.push_back(get_arg_name(node)); + retval.push_back(get_arg_name(*node)); } return retval; } - static std::pair get_arg_type(const AST_Node_Impl_Ptr &t_node, const chaiscript::detail::Dispatch_State &t_ss) + static std::pair get_arg_type(const AST_Node_Impl &t_node, const chaiscript::detail::Dispatch_State &t_ss) { - if (t_node->children.size() < 2) + if (t_node.children.size() < 2) { return {}; } else { - return {t_node->children[0]->text, t_ss->get_type(t_node->children[0]->text, false)}; + return {t_node.children[0]->text, t_ss->get_type(t_node.children[0]->text, false)}; } } - static dispatch::Param_Types get_arg_types(const AST_Node_Impl_Ptr &t_node, const chaiscript::detail::Dispatch_State &t_ss) { + static dispatch::Param_Types get_arg_types(const AST_Node_Impl &t_node, const chaiscript::detail::Dispatch_State &t_ss) { std::vector> retval; - for (const auto &child : t_node->children) + for (const auto &child : t_node.children) { - retval.push_back(get_arg_type(child, t_ss)); + retval.push_back(get_arg_type(*child, t_ss)); } return dispatch::Param_Types(std::move(retval)); @@ -621,9 +629,15 @@ namespace chaiscript 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])), - m_this_capture(has_this_capture(this->children[0]->children)) + AST_Node_Impl(t_ast_node_text, + AST_Node_Type::Lambda, + std::move(t_loc), + std::vector>(std::make_move_iterator(t_children.begin()), + std::make_move_iterator(std::prev(t_children.end()))) + ), + m_param_names(Arg_List_AST_Node::get_arg_names(*this->children[1])), + m_this_capture(has_this_capture(this->children[0]->children)), + m_lambda_node(std::move(t_children.back())) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { @@ -637,18 +651,18 @@ namespace chaiscript }(); 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); return Boxed_Value( dispatch::make_dynamic_proxy_function( - [engine, lambda_node, param_names = this->m_param_names, captures, this_capture = this->m_this_capture](const std::vector &t_params) + [engine, lambda_node = this->m_lambda_node, param_names = this->m_param_names, captures, + this_capture = this->m_this_capture] (const std::vector &t_params) { - return detail::eval_function(engine, lambda_node, param_names, t_params, &captures, this_capture); + return detail::eval_function(engine, *lambda_node, param_names, t_params, &captures, this_capture); }, - static_cast(numparams), lambda_node, param_types + static_cast(numparams), m_lambda_node, param_types ) ); } @@ -664,7 +678,7 @@ namespace chaiscript private: const std::vector m_param_names; const bool m_this_capture = false; - + const std::shared_ptr> m_lambda_node; }; template @@ -699,55 +713,81 @@ namespace chaiscript template struct Def_AST_Node final : AST_Node_Impl { + + std::shared_ptr> m_body_node; + std::shared_ptr> m_guard_node; + 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)) { } + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), + std::vector>(std::make_move_iterator(t_children.begin()), + std::make_move_iterator(std::prev(t_children.end(), has_guard(t_children, 1)?2:1))) + ), + m_body_node(get_body_node(std::move(t_children))), + m_guard_node(get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2)) + + { } + + static std::shared_ptr> get_guard_node(std::vector> &&vec, bool has_guard) + { + if (has_guard) { + return std::move(*std::prev(vec.end(), 2)); + } else { + return {}; + } + } + + static std::shared_ptr> get_body_node(std::vector> &&vec) + { + return std::move(vec.back()); + } + + static bool has_guard(const std::vector> &t_children, const std::size_t offset) + { + if ((t_children.size() > 2 + offset) && (t_children[1+offset]->identifier == AST_Node_Type::Arg_List)) { + if (t_children.size() > 3 + offset) { + return true; + } + } + else { + if (t_children.size() > 2 + offset) { + return true; + } + } + return false; + } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ std::vector t_param_names; size_t numparams = 0; - AST_Node_Impl_Ptr guardnode; dispatch::Param_Types param_types; - if ((this->children.size() > 2) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) { + if ((this->children.size() > 1) && (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); - - if (this->children.size() > 3) { - guardnode = this->children[2]; - } - } - else { - //no parameters - numparams = 0; - - if (this->children.size() > 2) { - guardnode = this->children[1]; - } + 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); } std::reference_wrapper engine(*t_ss); std::shared_ptr guard; - if (guardnode) { + if (m_guard_node) { guard = dispatch::make_dynamic_proxy_function( - [engine, guardnode, t_param_names](const std::vector &t_params) + [engine, guardnode = m_guard_node, t_param_names](const std::vector &t_params) { - return detail::eval_function(engine, guardnode, t_param_names, t_params); + return detail::eval_function(engine, *guardnode, t_param_names, t_params); }, - static_cast(numparams), guardnode); + static_cast(numparams), m_guard_node); } try { const std::string & l_function_name = this->children[0]->text; - const auto & func_node = this->children.back(); t_ss->add( dispatch::make_dynamic_proxy_function( - [engine, guardnode, func_node, t_param_names](const std::vector &t_params) + [engine, func_node = m_body_node, t_param_names](const std::vector &t_params) { - return detail::eval_function(engine, func_node, t_param_names, t_params); + return detail::eval_function(engine, *func_node, t_param_names, t_params); }, - static_cast(numparams), this->children.back(), + static_cast(numparams), m_body_node, param_types, guard), l_function_name); } catch (const exception::name_conflict_error &e) { throw exception::eval_error("Function redefined '" + e.name() + "'"); @@ -1230,32 +1270,32 @@ namespace chaiscript } for (size_t i = 1; i < end_point; ++i) { chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss); - AST_Node_Impl_Ptr catch_block = this->children[i]; + auto &catch_block = *this->children[i]; - if (catch_block->children.size() == 1) { + if (catch_block.children.size() == 1) { //No variable capture, no guards - retval = catch_block->children[0]->eval(t_ss); + 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]); + } 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]); 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()).first) { t_ss.add_object(name, t_except); - if (catch_block->children.size() == 2) { + if (catch_block.children.size() == 2) { //Variable capture, no guards - retval = catch_block->children[1]->eval(t_ss); + retval = catch_block.children[1]->eval(t_ss); break; } - else if (catch_block->children.size() == 3) { + else if (catch_block.children.size() == 3) { //Variable capture, guards bool guard = false; try { - guard = boxed_cast(catch_block->children[1]->eval(t_ss)); + guard = boxed_cast(catch_block.children[1]->eval(t_ss)); } catch (const exception::bad_boxed_cast &) { if (this->children.back()->identifier == AST_Node_Type::Finally) { this->children.back()->children[0]->eval(t_ss); @@ -1263,7 +1303,7 @@ namespace chaiscript throw exception::eval_error("Guard condition not boolean"); } if (guard) { - retval = catch_block->children[2]->eval(t_ss); + retval = catch_block.children[2]->eval(t_ss); break; } } @@ -1335,8 +1375,18 @@ namespace chaiscript template struct Method_AST_Node final : AST_Node_Impl { + std::shared_ptr> m_body_node; + std::shared_ptr> m_guard_node; + 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)) { } + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc), + std::vector>(std::make_move_iterator(t_children.begin()), + std::make_move_iterator(std::prev(t_children.end(), Def_AST_Node::has_guard(t_children, 1)?2:1))) + ), + m_body_node(Def_AST_Node::get_body_node(std::move(t_children))), + m_guard_node(Def_AST_Node::get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2)) + { + } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ @@ -1348,39 +1398,27 @@ namespace chaiscript std::vector t_param_names{"this"}; dispatch::Param_Types param_types; - if ((this->children.size() > 3) + if ((this->children.size() > 2) && (this->children[2]->identifier == AST_Node_Type::Arg_List)) { - auto args = Arg_List_AST_Node::get_arg_names(this->children[2]); + 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(this->children[2], t_ss); - - if (this->children.size() > 4) { - guardnode = this->children[3]; - } - } - else { - //no parameters - - if (this->children.size() > 3) { - guardnode = this->children[2]; - } + param_types = Arg_List_AST_Node::get_arg_types(*this->children[2], t_ss); } const size_t numparams = t_param_names.size(); std::shared_ptr guard; std::reference_wrapper engine(*t_ss); - if (guardnode) { + if (m_guard_node) { guard = dispatch::make_dynamic_proxy_function( - [engine, t_param_names, guardnode](const std::vector &t_params) { - return chaiscript::eval::detail::eval_function(engine, guardnode, t_param_names, t_params); + [engine, t_param_names, guardnode = m_guard_node](const std::vector &t_params) { + return chaiscript::eval::detail::eval_function(engine, *guardnode, t_param_names, t_params); }, - static_cast(numparams), guardnode); + static_cast(numparams), m_guard_node); } try { 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()); @@ -1388,10 +1426,10 @@ namespace chaiscript t_ss->add( std::make_shared(class_name, dispatch::make_dynamic_proxy_function( - [engine, t_param_names, node](const std::vector &t_params) { - return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params); + [engine, t_param_names, node = m_body_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, guard + static_cast(numparams), m_body_node, param_types, guard ) ), function_name); @@ -1402,15 +1440,17 @@ namespace chaiscript auto type = t_ss->get_type(class_name, false); param_types.push_front(class_name, type); - t_ss->add(std::make_shared(class_name, + t_ss->add( + std::make_shared(class_name, dispatch::make_dynamic_proxy_function( - [engine, t_param_names, node](const std::vector &t_params) { - return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params); + [engine, t_param_names, node = m_body_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, guard), type), + static_cast(numparams), m_body_node, param_types, guard), type), function_name); } } catch (const exception::name_conflict_error &e) { + std::cout << "Method!!" << std::endl; throw exception::eval_error("Method redefined '" + e.name() + "'"); } return void_var(); diff --git a/include/chaiscript/language/chaiscript_optimizer.hpp b/include/chaiscript/language/chaiscript_optimizer.hpp index 996b6971..675d092c 100644 --- a/include/chaiscript/language/chaiscript_optimizer.hpp +++ b/include/chaiscript/language/chaiscript_optimizer.hpp @@ -24,17 +24,26 @@ namespace chaiscript { template auto optimize(eval::AST_Node_Impl_Ptr p) { - (void)std::initializer_list{ (p = static_cast(*this).optimize(p), 0)... }; + (void)std::initializer_list{ (p = static_cast(*this).optimize(std::move(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; + eval::AST_Node_Impl &child_at(eval::AST_Node_Impl &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]; + return *node.children[offset]; + } + } + + template + const eval::AST_Node_Impl &child_at(const eval::AST_Node_Impl &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]; } @@ -48,24 +57,24 @@ namespace chaiscript { } 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(); + auto child_count(const eval::AST_Node_Impl &node) { + if (node.identifier == AST_Node_Type::Compiled) { + return dynamic_cast&>(node).m_original_node->children.size(); } else { - return node->children.size(); + return node.children.size(); } } template - auto make_compiled_node(const eval::AST_Node_Impl_Ptr &original_node, std::vector> children, Callable callable) + auto make_compiled_node(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)); + return chaiscript::make_unique, eval::Compiled_AST_Node>(std::move(original_node), std::move(children), std::move(callable)); } struct Return { template - auto optimize(const eval::AST_Node_Impl_Ptr &p) + auto optimize(eval::AST_Node_Impl_Ptr p) { if ( (p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda) && !p->children.empty()) @@ -75,7 +84,7 @@ namespace chaiscript { 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]; + last_child->children.back() = std::move(block_last_child->children[0]); } } } @@ -86,9 +95,9 @@ namespace chaiscript { }; template - bool contains_var_decl_in_scope(const T &node) + bool contains_var_decl_in_scope(const eval::AST_Node_Impl &node) { - if (node->identifier == AST_Node_Type::Var_Decl) { + if (node.identifier == AST_Node_Type::Var_Decl) { return true; } @@ -96,8 +105,8 @@ namespace chaiscript { 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 + if (child.identifier != AST_Node_Type::Block + && child.identifier != AST_Node_Type::For && contains_var_decl_in_scope(child)) { return true; } @@ -108,15 +117,16 @@ namespace chaiscript { struct Block { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { if (node->identifier == AST_Node_Type::Block) { - if (!contains_var_decl_in_scope(node)) + if (!contains_var_decl_in_scope(*node)) { if (node->children.size() == 1) { - return node->children[0]; + return std::move(node->children[0]); } else { - return chaiscript::make_shared, eval::Scopeless_Block_AST_Node>(node->text, node->location, node->children); + return chaiscript::make_unique, eval::Scopeless_Block_AST_Node>(node->text, node->location, + std::move(node->children)); } } } @@ -127,7 +137,7 @@ namespace chaiscript { struct Dead_Code { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { if (node->identifier == AST_Node_Type::Block) { std::vector keepers; @@ -135,10 +145,10 @@ namespace chaiscript { 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) + const 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); } @@ -147,12 +157,16 @@ namespace chaiscript { 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); + const auto new_children = [&](){ + std::vector> retval; + for (const auto x : keepers) + { + retval.push_back(std::move(node->children[x])); + } + return retval; + }; + + return chaiscript::make_unique, eval::Block_AST_Node>(node->text, node->location, new_children()); } } else { return node; @@ -162,29 +176,30 @@ namespace chaiscript { struct Unused_Return { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { if ((node->identifier == AST_Node_Type::Block || node->identifier == AST_Node_Type::Scopeless_Block) && !node->children.empty()) { for (size_t i = 0; i < node->children.size()-1; ++i) { - auto child = node->children[i]; + auto child = node->children[i].get(); 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)); + node->children[i] = chaiscript::make_unique, 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) + && 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)); + auto &sub_child = child_at(child, i); + if (sub_child.identifier == AST_Node_Type::Fun_Call) { + child.children[i] = chaiscript::make_unique, eval::Unused_Return_Fun_Call_AST_Node>(sub_child.text, sub_child.location, std::move(sub_child.children)); } } } @@ -195,17 +210,17 @@ namespace chaiscript { struct If { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { if ((node->identifier == AST_Node_Type::If) && node->children.size() >= 2 && node->children[0]->identifier == AST_Node_Type::Constant) { - const auto condition = std::dynamic_pointer_cast>(node->children[0])->m_value; + const auto condition = dynamic_cast *>(node->children[0].get())->m_value; if (condition.get_type_info().bare_equal_type_info(typeid(bool))) { if (boxed_cast(condition)) { - return node->children[1]; + return std::move(node->children[1]); } else if (node->children.size() == 3) { - return node->children[2]; + return std::move(node->children[2]); } } } @@ -216,7 +231,7 @@ namespace chaiscript { struct Partial_Fold { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { // Fold right side if (node->identifier == AST_Node_Type::Binary @@ -228,9 +243,10 @@ namespace chaiscript { 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; + const auto rhs = dynamic_cast *>(node->children[1].get())->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); + return chaiscript::make_unique, eval::Fold_Right_Binary_Operator_AST_Node>(node->text, node->location, + std::move(node->children), rhs); } } } catch (const std::exception &) { @@ -244,7 +260,7 @@ namespace chaiscript { struct Constant_Fold { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { if (node->identifier == AST_Node_Type::Prefix && node->children.size() == 1 @@ -253,14 +269,14 @@ namespace chaiscript { 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 lhs = dynamic_cast *>(node->children[0].get())->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)); + return chaiscript::make_unique, 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))); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, Boxed_Value(!boxed_cast(lhs))); } } catch (const std::exception &) { //failure to fold, that's OK @@ -271,8 +287,8 @@ namespace chaiscript { && 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; + const auto lhs = dynamic_cast &>(*node->children[0]).m_value; + const auto rhs = dynamic_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] { @@ -280,7 +296,7 @@ namespace chaiscript { else { return Boxed_Value(lhs_val || rhs_val); } }(); - return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); } } catch (const std::exception &) { //failure to fold, that's OK @@ -294,12 +310,12 @@ namespace chaiscript { 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; + const auto lhs = dynamic_cast &>(*node->children[0]).m_value; + const auto rhs = dynamic_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)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); } } } catch (const std::exception &) { @@ -312,13 +328,13 @@ namespace chaiscript { && 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; + const auto arg = dynamic_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)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, Boxed_Value(val)); }; if (fun_name == "double") { @@ -344,35 +360,36 @@ namespace chaiscript { struct For_Loop { template - auto optimize(const eval::AST_Node_Impl_Ptr &for_node) { + auto optimize(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); + 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 + if (child_count(*for_node) == 4 + && 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_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_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) + && 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; + const Boxed_Value &begin = dynamic_cast &>(child_at(eq_node, 1)).m_value; + const Boxed_Value &end = dynamic_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())) { @@ -380,9 +397,14 @@ namespace chaiscript { 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}, + // note that we are moving the last element out, then popping the empty shared_ptr + // from the vector + std::vector> body_vector; + auto body_child = std::move(for_node->children[3]); + for_node->children.pop_back(); + body_vector.emplace_back(std::move(body_child)); + + return make_compiled_node(std::move(for_node), std::move(body_vector), [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); diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 163d2bb3..06a51dfa 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -397,7 +397,7 @@ namespace chaiscript return m_optimizer; } - ChaiScript_Parser(const ChaiScript_Parser &) = default; + ChaiScript_Parser(const ChaiScript_Parser &) = delete; ChaiScript_Parser &operator=(const ChaiScript_Parser &) = delete; ChaiScript_Parser(ChaiScript_Parser &&) = default; ChaiScript_Parser &operator=(ChaiScript_Parser &&) = delete; @@ -406,10 +406,10 @@ namespace chaiscript 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 override { - std::cout << prepend << "(" << ast_node_type_to_string(t->identifier) << ") " << t->text << " : " << t->start().line << ", " << t->start().column << '\n'; - for (const auto &node : t->get_children()) { - debug_print(node, prepend + " "); + void debug_print(const AST_Node &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 (const auto &node : t.get_children()) { + debug_print(node.get(), prepend + " "); } } @@ -452,7 +452,7 @@ 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( m_optimizer.optimize( - chaiscript::make_shared, NodeType>( + chaiscript::make_unique, NodeType>( std::move(t_text), std::move(filepos), std::move(new_children))) @@ -779,9 +779,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::unique_ptr> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...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)...); + return chaiscript::make_unique, 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 @@ -1051,23 +1051,30 @@ namespace chaiscript Char_Parser &operator=(const Char_Parser &) = delete; ~Char_Parser(){ - if (is_octal) { - process_octal(); - } + try { + if (is_octal) { + process_octal(); + } - if (is_hex) { - process_hex(); - } + if (is_hex) { + process_hex(); + } - if (is_unicode) { - process_unicode(); + if (is_unicode) { + process_unicode(); + } + } catch (const std::invalid_argument &) { + // escape sequence was invalid somehow, we'll pick this + // up in the next part of parsing } } void process_hex() { - auto val = stoll(hex_matches, nullptr, 16); - match.push_back(char_type(val)); + if (!hex_matches.empty()) { + auto val = stoll(hex_matches, nullptr, 16); + match.push_back(char_type(val)); + } hex_matches.clear(); is_escaped = false; is_hex = false; @@ -1076,8 +1083,10 @@ namespace chaiscript void process_octal() { - auto val = stoll(octal_matches, nullptr, 8); - match.push_back(char_type(val)); + if (!octal_matches.empty()) { + auto val = stoll(octal_matches, nullptr, 8); + match.push_back(char_type(val)); + } octal_matches.clear(); is_escaped = false; is_octal = false; @@ -1086,9 +1095,11 @@ namespace chaiscript void process_unicode() { - auto val = stoll(hex_matches, nullptr, 16); - hex_matches.clear(); - match += detail::Char_Parser_Helper::str_from_ll(val); + if (!hex_matches.empty()) { + auto val = stoll(hex_matches, nullptr, 16); + hex_matches.clear(); + match += detail::Char_Parser_Helper::str_from_ll(val); + } is_escaped = false; is_unicode = false; } @@ -1254,6 +1265,7 @@ namespace chaiscript cparser.saw_interpolation_marker = false; } else { cparser.parse(*s, start.line, start.col, *m_filename); + ++s; } } @@ -1759,7 +1771,7 @@ namespace chaiscript 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>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } if (!is_if_init) { @@ -1849,7 +1861,7 @@ namespace chaiscript { return false; } else { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } } @@ -1859,13 +1871,13 @@ namespace chaiscript { return false; } else { - m_match_stack.push_back(chaiscript::make_shared, eval::Constant_AST_Node>(Boxed_Value(true))); + m_match_stack.push_back(chaiscript::make_unique, eval::Constant_AST_Node>(Boxed_Value(true))); } } if (!Equation()) { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } return true; @@ -1896,9 +1908,17 @@ namespace chaiscript throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename); } + const auto num_children = m_match_stack.size() - prev_stack_top; + if (classic_for) { + if (num_children != 4) { + throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename); + } build_match>(prev_stack_top); } else { + if (num_children != 3) { + throw exception::eval_error("Incomplete ranged-for expression", File_Position(m_position.line, m_position.col), *m_filename); + } build_match>(prev_stack_top); } } @@ -2005,7 +2025,7 @@ namespace chaiscript } if (m_match_stack.size() == prev_stack_top) { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } build_match>(prev_stack_top); @@ -2029,7 +2049,7 @@ namespace chaiscript } if (m_match_stack.size() == prev_stack_top) { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } build_match>(prev_stack_top); @@ -2105,13 +2125,13 @@ namespace chaiscript } 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); } - auto dot_access = m_match_stack.back()->children[0]; - auto func_call = m_match_stack.back(); + auto dot_access = std::move(m_match_stack.back()->children[0]); + auto func_call = std::move(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()); + func_call->children.insert(func_call->children.begin(), std::move(dot_access->children.back())); dot_access->children.pop_back(); dot_access->children.push_back(std::move(func_call)); 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); @@ -2172,7 +2192,7 @@ namespace chaiscript throw exception::eval_error("Incomplete variable declaration", File_Position(m_position.line, m_position.col), *m_filename); } - } else if (Keyword("GLOBAL") || Keyword("global")) { + } else if (Keyword("global")) { retval = true; if (!(Reference() || Id(true))) { @@ -2517,24 +2537,23 @@ namespace chaiscript AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) override { - ChaiScript_Parser parser(*this); - parser.m_match_stack.clear(); + ChaiScript_Parser parser(m_tracer, m_optimizer); 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){}); + auto last_position = m_position; + auto last_filename = m_filename; + auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){}); - const auto retval = parse_internal(t_input, "instr eval"); + 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); + return eval::AST_Node_Impl_Ptr(dynamic_cast*>(retval.release())); } /// Parses the given input string, tagging parsed ast_nodes with the given m_filename. @@ -2546,20 +2565,21 @@ namespace chaiscript while (m_position.has_more() && (!Eol())) { ++m_position; } - /// \todo respect // -*- coding: utf-8 -*- on line 1 or 2 see: http://evanjones.ca/python-utf8.html) } if (Statements(true)) { if (m_position.has_more()) { - throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), t_fname); + throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), *m_filename); } else { build_match>(0); } } else { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } - return m_match_stack.front(); + AST_NodePtr retval(std::move(m_match_stack.front())); + m_match_stack.clear(); + return retval; } }; } diff --git a/include/chaiscript/language/chaiscript_unknown.hpp b/include/chaiscript/language/chaiscript_unknown.hpp index 9e1d7afc..8fc1d494 100644 --- a/include/chaiscript/language/chaiscript_unknown.hpp +++ b/include/chaiscript/language/chaiscript_unknown.hpp @@ -16,7 +16,11 @@ namespace chaiscript { Loadable_Module(const std::string &, const std::string &) { +#ifdef CHAISCRIPT_NO_DYNLOAD + throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)"); +#else throw chaiscript::exception::load_module_error("Loadable module support not available for your platform"); +#endif } ModulePtr m_moduleptr; diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 96decc09..693f19da 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -463,7 +463,7 @@ struct JSONParser { } static void consume_ws( const std::string &str, size_t &offset ) { - while( isspace( str[offset] ) && offset <= str.size() ) { ++offset; } + while( isspace( str.at(offset) ) && offset <= str.size() ) { ++offset; } } static JSON parse_object( const std::string &str, size_t &offset ) { @@ -471,29 +471,29 @@ struct JSONParser { ++offset; consume_ws( str, offset ); - if( str[offset] == '}' ) { + if( str.at(offset) == '}' ) { ++offset; return Object; } for (;offset= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) { val += c; } else { @@ -567,12 +567,17 @@ struct JSONParser { std::string val, exp_str; char c = '\0'; bool isDouble = false; + bool isNegative = false; long exp = 0; + if( offset < str.size() && str.at(offset) == '-' ) { + isNegative = true; + ++offset; + } for (; offset < str.size() ;) { - c = str[offset++]; - if( (c == '-') || (c >= '0' && c <= '9') ) { + c = str.at(offset++); + if( c >= '0' && c <= '9' ) { val += c; - } else if( c == '.' ) { + } else if( c == '.' && !isDouble ) { val += c; isDouble = true; } else { @@ -580,7 +585,7 @@ struct JSONParser { } } if( offset < str.size() && (c == 'E' || c == 'e' )) { - c = str[ offset++ ]; + c = str.at(offset++); if( c == '-' ) { exp_str += '-'; } else if( c == '+' ) { @@ -590,7 +595,7 @@ struct JSONParser { } for (; offset < str.size() ;) { - c = str[ offset++ ]; + c = str.at(offset++); if( c >= '0' && c <= '9' ) { exp_str += c; } else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { @@ -608,12 +613,12 @@ struct JSONParser { --offset; if( isDouble ) { - return JSON(chaiscript::parse_num( val ) * std::pow( 10, exp )); + return JSON((isNegative?-1:1) * chaiscript::parse_num( val ) * std::pow( 10, exp )); } else { if( !exp_str.empty() ) { - return JSON(static_cast(chaiscript::parse_num( val )) * std::pow( 10, exp )); + return JSON((isNegative?-1:1) * static_cast(chaiscript::parse_num( val )) * std::pow( 10, exp )); } else { - return JSON(chaiscript::parse_num( val )); + return JSON((isNegative?-1:1) * chaiscript::parse_num( val )); } } } @@ -641,7 +646,7 @@ struct JSONParser { static JSON parse_next( const std::string &str, size_t &offset ) { char value; consume_ws( str, offset ); - value = str[offset]; + value = str.at(offset); switch( value ) { case '[' : return parse_array( str, offset ); case '{' : return parse_object( str, offset ); diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index c0af1cd8..2aa81f34 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -63,7 +63,11 @@ namespace chaiscript static Boxed_Value from_json(const std::string &t_json) { - return from_json( json::JSON::Load(t_json) ); + try { + return from_json( json::JSON::Load(t_json) ); + } catch (const std::out_of_range& ) { + throw std::runtime_error("Unparsed JSON input"); + } } static std::string to_json(const Boxed_Value &t_bv) diff --git a/samples/fun_call_performance.cpp b/samples/fun_call_performance.cpp index a519052e..31fa606c 100644 --- a/samples/fun_call_performance.cpp +++ b/samples/fun_call_performance.cpp @@ -68,6 +68,7 @@ std::vector default_search_paths() { std::vector paths; +#ifndef CHAISCRIPT_NO_DYNLOAD #ifdef CHAISCRIPT_WINDOWS // force no unicode CHAR path[4096]; int size = GetModuleFileNameA(0, path, sizeof(path) - 1); @@ -137,6 +138,7 @@ std::vector default_search_paths() paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/"); } #endif +#endif // ifndef CHAISCRIPT_NO_DYNLOAD return paths; } @@ -250,7 +252,7 @@ void interactive(chaiscript::ChaiScript& chai) catch (const chaiscript::exception::eval_error &ee) { std::cout << ee.what(); if (ee.call_stack.size() > 0) { - std::cout << "during evaluation at (" << ee.call_stack[0]->start().line << ", " << ee.call_stack[0]->start().column << ")"; + std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")"; } std::cout << std::endl; } diff --git a/src/libfuzzer_client.cpp b/src/libfuzzer_client.cpp new file mode 100644 index 00000000..1c90d0ba --- /dev/null +++ b/src/libfuzzer_client.cpp @@ -0,0 +1,342 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + + +#include +#include +#include + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include "../static_libs/chaiscript_parser.hpp" +#include "../static_libs/chaiscript_stdlib.hpp" + + +#ifdef READLINE_AVAILABLE +#include +#include +#else + +char *mystrdup (const char *s) { + size_t len = strlen(s); // Space for length plus nul + char *d = static_cast(malloc (len+1)); + if (d == nullptr) { return nullptr; } // No memory +#ifdef CHAISCRIPT_MSVC + strcpy_s(d, len+1, s); // Copy the characters +#else + strncpy(d,s,len); // Copy the characters +#endif + d[len] = '\0'; + return d; // Return the new string +} + +char* readline(const char* p) +{ + std::string retval; + std::cout << p ; + std::getline(std::cin, retval); + return std::cin.eof() ? nullptr : mystrdup(retval.c_str()); +} + + +void add_history(const char* /*unused*/){} +void using_history(){} +#endif + + + +void *cast_module_symbol(std::vector (*t_path)()) +{ + union cast_union + { + std::vector (*in_ptr)(); + void *out_ptr; + }; + + cast_union c; + c.in_ptr = t_path; + return c.out_ptr; +} + +std::vector default_search_paths() +{ + std::vector paths; + +#ifndef CHAISCRIPT_NO_DYNLOAD +#ifdef CHAISCRIPT_WINDOWS // force no unicode + CHAR path[4096]; + int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1); + + std::string exepath(path, size); + + size_t lastslash = exepath.rfind('\\'); + size_t secondtolastslash = exepath.rfind('\\', lastslash - 1); + if (lastslash != std::string::npos) + { + paths.push_back(exepath.substr(0, lastslash)); + } + + if (secondtolastslash != std::string::npos) + { + return {exepath.substr(0, secondtolastslash) + "\\lib\\chaiscript\\"}; + } +#else + + std::string exepath; + + std::vector buf(2048); + ssize_t size = -1; + + if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) >= 0) + { + exepath = std::string(&buf.front(), static_cast(size)); + } + + if (exepath.empty()) + { + if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) >= 0) + { + exepath = std::string(&buf.front(), static_cast(size)); + } + } + + if (exepath.empty()) + { + if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) >= 0) + { + exepath = std::string(&buf.front(), static_cast(size)); + } + } + + if (exepath.empty()) + { + Dl_info rInfo; + memset( &rInfo, 0, sizeof(rInfo) ); + if ( dladdr(cast_module_symbol(&default_search_paths), &rInfo) == 0 || rInfo.dli_fname == nullptr ) { + return paths; + } + + exepath = std::string(rInfo.dli_fname); + } + + size_t lastslash = exepath.rfind('/'); + + size_t secondtolastslash = exepath.rfind('/', lastslash - 1); + if (lastslash != std::string::npos) + { + paths.push_back(exepath.substr(0, lastslash+1)); + } + + if (secondtolastslash != std::string::npos) + { + paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/"); + } +#endif +#endif // ifndef CHAISCRIPT_NO_DYNLOAD + + return paths; +} + +void help(int n) { + if ( n >= 0 ) { + std::cout << "ChaiScript evaluator. To evaluate an expression, type it and press .\n"; + std::cout << "Additionally, you can inspect the runtime system using:\n"; + std::cout << " dump_system() - outputs all functions registered to the system\n"; + std::cout << " dump_object(x) - dumps information about the given symbol\n"; + } else { + std::cout << "usage : chai [option]+\n"; + std::cout << "option:" << '\n'; + std::cout << " -h | --help" << '\n'; + std::cout << " -i | --interactive" << '\n'; + std::cout << " -c | --command cmd" << '\n'; + std::cout << " -v | --version" << '\n'; + std::cout << " - --stdin" << '\n'; + std::cout << " filepath" << '\n'; + } +} + +bool throws_exception(const std::function &f) +{ + try { + f(); + } catch (...) { + return true; + } + + return false; +} + +chaiscript::exception::eval_error get_eval_error(const std::function &f) +{ + try { + f(); + } catch (const chaiscript::exception::eval_error &e) { + return e; + } + + throw std::runtime_error("no exception throw"); +} + +std::string get_next_command() { + std::string retval("quit"); + if ( ! std::cin.eof() ) { + char *input_raw = readline("eval> "); + if ( input_raw != nullptr ) { + add_history(input_raw); + + std::string val(input_raw); + size_t pos = val.find_first_not_of("\t \n"); + if (pos != std::string::npos) + { + val.erase(0, pos); + } + pos = val.find_last_not_of("\t \n"); + if (pos != std::string::npos) + { + val.erase(pos+1, std::string::npos); + } + + retval = val; + + ::free(input_raw); + } + } + if( retval == "quit" + || retval == "exit" + || retval == "help" + || retval == "version") + { + retval += "(0)"; + } + return retval; +} + +// We have to wrap exit with our own because Clang has a hard time with +// function pointers to functions with special attributes (system exit being marked NORETURN) +void myexit(int return_val) { + exit(return_val); +} + +void interactive(chaiscript::ChaiScript_Basic& chai) +{ + using_history(); + + for (;;) { + std::string input = get_next_command(); + try { + // evaluate input + chaiscript::Boxed_Value val = chai.eval(input); + + //Then, we try to print the result of the evaluation to the user + if (!val.get_type_info().bare_equal(chaiscript::user_type())) { + try { + std::cout << chai.eval >("to_string")(val) << '\n'; + } + catch (...) {} //If we can't, do nothing + } + } + catch (const chaiscript::exception::eval_error &ee) { + std::cout << ee.what(); + if ( !ee.call_stack.empty() ) { + std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")"; + } + std::cout << '\n'; + } + catch (const std::exception &e) { + std::cout << e.what(); + std::cout << '\n'; + } + } +} + +double now() +{ + using namespace std::chrono; + auto now = high_resolution_clock::now(); + return duration_cast>(now.time_since_epoch()).count(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + chaiscript::ChaiScript chai; + + chai.eval( R"chaiscript( +def assert_equal(x, y) +{ + if (x == y) + { + // Passes + } else { + // Fails + print("assert_equal failure: got '" + to_string(y) + "' expected '" + to_string(x) + "'"); +// exit(-1); + } +} + +def assert_false(f) +{ + if (f) + { + print("assert_false failure"); +// exit(-1); + } +} + +def assert_true(f) +{ + if (!f) + { + print("assert_true failure"); +// exit(-1); + } +} + +def assert_not_equal(x, y) +{ + if (!(x == y)) + { + // Passes + } else { + // Fails + print("assert_not_equal failure: got " + to_string(y) + " which was not expected."); +// exit(-1); + } +} + +def assert_throws(desc, x) +{ + if (throws_exception(x)) + { + // Passes + } else { + // Fails + print("assert_throws failure, function did not throw exception: " + to_string(desc)); +// exit(-1); + } +})chaiscript"); + + try { + chai.eval(std::string(reinterpret_cast(data), size)); + } catch (const chaiscript::exception::eval_error &ee) { + std::cout << ee.pretty_print(); + std::cout << '\n'; + } catch (const chaiscript::Boxed_Value &e) { + std::cout << "Unhandled exception thrown of type " << e.get_type_info().name() << '\n'; + } catch (const chaiscript::exception::load_module_error &e) { + std::cout << "Unhandled module load error\n" << e.what() << '\n'; + } catch (const std::exception &e) { + std::cout << "unhandled unknown exception: " << e.what() << '\n'; + } + + return 0; +} + + diff --git a/src/main.cpp b/src/main.cpp index 4179a6bf..f3bd2d82 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -71,6 +71,7 @@ std::vector default_search_paths() { std::vector paths; +#ifndef CHAISCRIPT_NO_DYNLOAD #ifdef CHAISCRIPT_WINDOWS // force no unicode CHAR path[4096]; int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1); @@ -140,6 +141,7 @@ std::vector default_search_paths() paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/"); } #endif +#endif // ifndef CHAISCRIPT_NO_DYNLOAD return paths; } @@ -245,7 +247,7 @@ void interactive(chaiscript::ChaiScript_Basic& chai) catch (const chaiscript::exception::eval_error &ee) { std::cout << ee.what(); if ( !ee.call_stack.empty() ) { - std::cout << "during evaluation at (" << ee.call_stack[0]->start().line << ", " << ee.call_stack[0]->start().column << ")"; + std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")"; } std::cout << '\n'; } @@ -306,6 +308,7 @@ int main(int argc, char *argv[]) bool eval_error_ok = false; bool boxed_exception_ok = false; + bool any_exception_ok = false; for (int i = 0; i < argc; ++i) { if ( i == 0 && argc > 1 ) { @@ -342,6 +345,9 @@ int main(int argc, char *argv[]) } else if ( arg == "--exception" ) { boxed_exception_ok = true; continue; + } else if ( arg == "--any-exception" ) { + any_exception_ok = true; + continue; } else if ( arg == "-i" || arg == "--interactive" ) { mode = eInteractive ; } else if ( arg.find('-') == 0 ) { @@ -381,11 +387,18 @@ int main(int argc, char *argv[]) catch (const chaiscript::exception::load_module_error &e) { std::cout << "Unhandled module load error\n" << e.what() << '\n'; } - -// catch (std::exception &e) { -// std::cout << e.what() << '\n'; -// return EXIT_FAILURE; -// } + catch (std::exception &e) { + std::cout << "Unhandled standard exception: " << e.what() << '\n'; + if (!any_exception_ok) { + throw; + } + } + catch (...) { + std::cout << "Unhandled unknown exception" << '\n'; + if (!any_exception_ok) { + throw; + } + } } return EXIT_SUCCESS; diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index c1716ec9..6e241572 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -12,6 +12,8 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunknown-pragmas" #pragma GCC diagnostic ignored "-Wparentheses" +// This one is necessary for the const return non-reference test +#pragma GCC diagnostic ignored "-Wignored-qualifiers" #endif @@ -1270,3 +1272,54 @@ TEST_CASE("Test reference member being registered") } +const int add_3(const int &i) +{ + return i + 3; +} + +TEST_CASE("Test returning by const non-reference") +{ + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + // Note, C++ will not allow us to do this: + // chai.add(chaiscript::fun(&Reference_MyClass::x) , "x"); + chai.add(chaiscript::fun(&add_3), "add_3"); + auto v = chai.eval("add_3(12)"); + CHECK(v == 15); +} + + +struct MyException : std::runtime_error +{ + using std::runtime_error::runtime_error; + int value = 5; +}; + +void throws_a_thing() +{ + throw MyException("Hello World"); +} + +TEST_CASE("Test throwing and catching custom exception") +{ + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + chai.add(chaiscript::user_type(), "MyException"); + chai.add(chaiscript::base_class()); // be sure to register base class relationship + chai.add(chaiscript::fun(&throws_a_thing), "throws_a_thing"); + chai.add(chaiscript::fun(&MyException::value), "value"); + + const auto s = chai.eval("fun(){ try { throws_a_thing(); } catch (MyException ex) { return ex.what(); } }()"); + CHECK(s == "Hello World"); + + // this has an explicit clone to prevent returning a pointer to the `value` from inside of MyException + const auto i = chai.eval("fun(){ try { throws_a_thing(); } catch (MyException ex) { var v = clone(ex.value); print(v); return v; } }()"); + CHECK(i == 5); +} + + +TEST_CASE("Test ability to get 'use' function from default construction") +{ + chaiscript::ChaiScript chai; + const auto use_function = chai.eval>("use"); +} + + diff --git a/unittests/fuzz_unit_test.inc b/unittests/fuzz_unit_test.inc new file mode 100644 index 00000000..87b8e518 --- /dev/null +++ b/unittests/fuzz_unit_test.inc @@ -0,0 +1,53 @@ +def assert_equal(x, y) +{ + if (x == y) + { + // Passes + } else { + // Fails + print("assert_equal failure: got '" + to_string(y) + "' expected '" + to_string(x) + "'"); +// exit(-1); + } +} + +def assert_false(f) +{ + if (f) + { + print("assert_false failure"); +// exit(-1); + } +} + +def assert_true(f) +{ + if (!f) + { + print("assert_true failure"); +// exit(-1); + } +} + +def assert_not_equal(x, y) +{ + if (!(x == y)) + { + // Passes + } else { + // Fails + print("assert_not_equal failure: got " + to_string(y) + " which was not expected."); +// exit(-1); + } +} + +def assert_throws(desc, x) +{ + if (throws_exception(x)) + { + // Passes + } else { + // Fails + print("assert_throws failure, function did not throw exception: " + to_string(desc)); +// exit(-1); + } +} diff --git a/unittests/fuzzy_tests-2016-06-29.tar.bz2 b/unittests/fuzzy_tests-2016-06-29.tar.bz2 deleted file mode 100644 index 95fd20ae..00000000 Binary files a/unittests/fuzzy_tests-2016-06-29.tar.bz2 and /dev/null differ diff --git a/unittests/fuzzy_tests-2017-07-20.tar.bz2 b/unittests/fuzzy_tests-2017-07-20.tar.bz2 new file mode 100644 index 00000000..35df734f Binary files /dev/null and b/unittests/fuzzy_tests-2017-07-20.tar.bz2 differ diff --git a/unittests/global.chai b/unittests/global.chai index 0374e6d3..7f8959a6 100644 --- a/unittests/global.chai +++ b/unittests/global.chai @@ -1,12 +1,12 @@ // Test global -GLOBAL g = 3; +global g = 3; assert_true(g == 3); var v := g; assert_true(v == 3); -GLOBAL g = 2; +global g = 2; assert_true(g == 2); assert_true(v == 2); diff --git a/unittests/json_3.chai b/unittests/json_3.chai index d3f222e4..11ce7dcb 100644 --- a/unittests/json_3.chai +++ b/unittests/json_3.chai @@ -1 +1,2 @@ assert_equal(from_json("100"), 100) +assert_equal(from_json("-100"), -100) diff --git a/unittests/json_4.chai b/unittests/json_4.chai index 22d388c7..ae070241 100644 --- a/unittests/json_4.chai +++ b/unittests/json_4.chai @@ -1 +1,22 @@ assert_equal(from_json("1.234"), 1.234) +assert_equal(from_json("-1.234"), -1.234) + +auto caught = false; +try { + from_json("-1-5.3"); +} +catch(e) { + assert_equal("JSON ERROR: Number: unexpected character '-'", e.what()); + caught = true; +} +assert_equal(caught, true); + +caught = false; +try { + from_json("-15.3.2"); +} +catch(e) { + assert_equal("JSON ERROR: Number: unexpected character '.'", e.what()); + caught = true; +} +assert_equal(caught, true); diff --git a/unittests/json_9.chai b/unittests/json_9.chai index 15127387..d8c64239 100644 --- a/unittests/json_9.chai +++ b/unittests/json_9.chai @@ -1,2 +1,2 @@ -assert_equal(from_json("[1,2,3]"), [1,2,3]) +assert_equal(from_json("[1,-2,3]"), [1,-2,3]) diff --git a/unittests/multithreaded_test.cpp b/unittests/multithreaded_test.cpp index ff06620a..e2d0240b 100644 --- a/unittests/multithreaded_test.cpp +++ b/unittests/multithreaded_test.cpp @@ -2,6 +2,9 @@ #include +#ifdef CHAISCRIPT_NO_DYNLOAD +#include +#endif #include #include @@ -57,18 +60,22 @@ int main() } std::vector modulepaths; + +#ifdef CHAISCRIPT_NO_DYNLOAD + chaiscript::ChaiScript chai(/* unused */modulepaths, usepaths); +#else modulepaths.push_back(""); if (modulepath) { modulepaths.push_back(modulepath); } - // 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); +#endif std::vector > threads; diff --git a/unittests/static_chaiscript.cpp b/unittests/static_chaiscript.cpp new file mode 100644 index 00000000..beeac2d1 --- /dev/null +++ b/unittests/static_chaiscript.cpp @@ -0,0 +1,11 @@ + +#define CHAISCRIPT_NO_THREADS + +/// ChaiScript as a static is unsupported with thread support enabled +/// + +#include + +static chaiscript::ChaiScript chai; + +int main() {}