diff --git a/.decent_ci-Linux.yaml b/.decent_ci-Linux.yaml index 24990bd..dfb5056 100644 --- a/.decent_ci-Linux.yaml +++ b/.decent_ci-Linux.yaml @@ -2,7 +2,8 @@ compilers: - name: "clang" version: "3.5" skip_packaging: true - cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON + cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DRUN_PERFORMANCE_TESTS:BOOL=ON + collect_performance_results: true - name: "clang" build_tag: "LibC++" version: "3.5" @@ -12,7 +13,7 @@ compilers: build_tag: AddressSanitizer version: "3.6" skip_packaging: true - cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_ADDRESS_SANITIZER:BOOL=ON + cmake_extra_flags: -DRUN_FUZZY_TESTS:BOOL=TRUE -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_ADDRESS_SANITIZER:BOOL=ON - name: "clang" build_tag: ThreadSanitizer version: "3.6" @@ -20,11 +21,21 @@ compilers: cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_THREAD_SANITIZER:BOOL=ON - name: "gcc" version: "4.8" - cmake_extra_flags: -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DBUILD_TESTING:BOOL=ON + build_tag: "NoThreads" + cmake_extra_flags: -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DBUILD_TESTING:BOOL=ON -DRUN_PERFORMANCE_TESTS:BOOL=ON -DMULTITHREAD_SUPPORT_ENABLED:BOOL=OFF + collect_performance_results: true + - name: "gcc" + version: "4.8" + cmake_extra_flags: -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DBUILD_TESTING:BOOL=ON -DRUN_PERFORMANCE_TESTS:BOOL=ON + collect_performance_results: true - name: "gcc" version: "4.6" skip_packaging: true - cmake_extra_flags: -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON + cmake_extra_flags: -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DRUN_PERFORMANCE_TESTS:BOOL=ON + collect_performance_results: true - name: cppcheck - compiler_extra_flags: --enable=all -I include --inline-suppr -Umax --suppress="*:cmake*" --suppress="*:unittests/catch.hpp" --force + compiler_extra_flags: --enable=all -I include --inline-suppr -Umax --suppress="*:unittests/catch.hpp" --force --suppress="unusedFunction:*" + - name: custom_check + commands: + - ./contrib/check_for_tabs.rb diff --git a/CMakeLists.txt b/CMakeLists.txt index 59c33a3..50b5e3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ option(BUILD_MODULES "Build Extra Modules (stl)" TRUE) 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) mark_as_advanced(USE_STD_MAKE_SHARED) @@ -68,6 +69,13 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(LINKER_FLAGS "${LINKER_FLAGS} -flto") endif() + option(GPROF_OUTPUT "Generate profile data" FALSE) + if (GPROF_OUTPUT) + add_definitions(-pg) + set(LINKER_FLAGS "${LINKER_FLAGS} -pg") + endif() + + option(PROFILE_GENERATE "Generate profile data" FALSE) if (PROFILE_GENERATE) add_definitions(-fprofile-generate) @@ -94,8 +102,8 @@ set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/readme.md") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/description.txt") set(CPACK_PACKAGE_VERSION_MAJOR 5) -set(CPACK_PACKAGE_VERSION_MINOR 7) -set(CPACK_PACKAGE_VERSION_PATCH 1) +set(CPACK_PACKAGE_VERSION_MINOR 8) +set(CPACK_PACKAGE_VERSION_PATCH 0) set(CPACK_PACKAGE_EXECUTABLES "chai;ChaiScript Eval") set(CPACK_PACKAGE_VENDOR "ChaiScript.com") @@ -115,16 +123,12 @@ configure_file(Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile) include(CTest) include(CPack) -include(cmake/CheckCXX11Features.cmake) - if(NOT MINGW) find_library(READLINE_LIBRARY NAMES readline PATH /usr/lib /usr/local/lib /opt/local/lib) endif() -if(HAS_CXX11_VARIADIC_TEMPLATES) - message(STATUS "Variadic Template support detected") -else() - message(SEND_ERROR "The selected compiler does not support the C++11 feature Variadic Templates.") +if (UNIX AND NOT APPLE) + find_program(VALGRIND NAMES valgrind PATH /usr/bin /usr/local/bin) endif() enable_testing() @@ -174,7 +178,7 @@ else() add_definitions(-Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP11_FLAG}) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - add_definitions(-Weverything -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-sign-conversion -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors) + add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-sign-conversion -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command) else() add_definitions(-Wnoexcept) endif() @@ -206,7 +210,7 @@ endif() include_directories(include) -set(Chai_INCLUDES include/chaiscript/chaiscript.hpp include/chaiscript/chaiscript_threading.hpp include/chaiscript/dispatchkit/bad_boxed_cast.hpp include/chaiscript/dispatchkit/bind_first.hpp include/chaiscript/dispatchkit/bootstrap.hpp include/chaiscript/dispatchkit/bootstrap_stl.hpp include/chaiscript/dispatchkit/boxed_cast.hpp include/chaiscript/dispatchkit/boxed_cast_helper.hpp include/chaiscript/dispatchkit/boxed_number.hpp include/chaiscript/dispatchkit/boxed_value.hpp include/chaiscript/dispatchkit/dispatchkit.hpp include/chaiscript/dispatchkit/type_conversions.hpp include/chaiscript/dispatchkit/dynamic_object.hpp include/chaiscript/dispatchkit/exception_specification.hpp include/chaiscript/dispatchkit/function_call.hpp include/chaiscript/dispatchkit/function_call_detail.hpp include/chaiscript/dispatchkit/handle_return.hpp include/chaiscript/dispatchkit/operators.hpp include/chaiscript/dispatchkit/proxy_constructors.hpp include/chaiscript/dispatchkit/proxy_functions.hpp include/chaiscript/dispatchkit/proxy_functions_detail.hpp include/chaiscript/dispatchkit/register_function.hpp include/chaiscript/dispatchkit/type_info.hpp include/chaiscript/language/chaiscript_algebraic.hpp include/chaiscript/language/chaiscript_common.hpp include/chaiscript/language/chaiscript_engine.hpp include/chaiscript/language/chaiscript_eval.hpp include/chaiscript/language/chaiscript_parser.hpp include/chaiscript/language/chaiscript_prelude.chai include/chaiscript/language/chaiscript_prelude_docs.hpp include/chaiscript/utility/utility.hpp) +set(Chai_INCLUDES include/chaiscript/chaiscript.hpp include/chaiscript/chaiscript_threading.hpp include/chaiscript/dispatchkit/bad_boxed_cast.hpp include/chaiscript/dispatchkit/bind_first.hpp include/chaiscript/dispatchkit/bootstrap.hpp include/chaiscript/dispatchkit/bootstrap_stl.hpp include/chaiscript/dispatchkit/boxed_cast.hpp include/chaiscript/dispatchkit/boxed_cast_helper.hpp include/chaiscript/dispatchkit/boxed_number.hpp include/chaiscript/dispatchkit/boxed_value.hpp include/chaiscript/dispatchkit/dispatchkit.hpp include/chaiscript/dispatchkit/type_conversions.hpp include/chaiscript/dispatchkit/dynamic_object.hpp include/chaiscript/dispatchkit/exception_specification.hpp include/chaiscript/dispatchkit/function_call.hpp include/chaiscript/dispatchkit/function_call_detail.hpp include/chaiscript/dispatchkit/handle_return.hpp include/chaiscript/dispatchkit/operators.hpp include/chaiscript/dispatchkit/proxy_constructors.hpp include/chaiscript/dispatchkit/proxy_functions.hpp include/chaiscript/dispatchkit/proxy_functions_detail.hpp include/chaiscript/dispatchkit/register_function.hpp include/chaiscript/dispatchkit/type_info.hpp include/chaiscript/language/chaiscript_algebraic.hpp include/chaiscript/language/chaiscript_common.hpp include/chaiscript/language/chaiscript_engine.hpp include/chaiscript/language/chaiscript_eval.hpp include/chaiscript/language/chaiscript_parser.hpp include/chaiscript/language/chaiscript_prelude.chai include/chaiscript/language/chaiscript_prelude_docs.hpp include/chaiscript/utility/utility.hpp include/chaiscript/utility/json.hpp include/chaiscript/utility/json_wrap.hpp) set_source_files_properties(${Chai_INCLUDES} PROPERTIES HEADER_FILE_ONLY TRUE) @@ -253,10 +257,14 @@ add_dependencies(chai chaiscript_stdlib-${CHAI_VERSION}) 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}) add_executable(memory_leak_test samples/memory_leak_test.cpp) target_link_libraries(memory_leak_test ${LIBS}) add_executable(inheritance samples/inheritance.cpp) target_link_libraries(inheritance ${LIBS}) + add_executable(factory samples/factory.cpp) + target_link_libraries(factory ${LIBS}) add_executable(fun_call_performance samples/fun_call_performance.cpp) target_link_libraries(fun_call_performance ${LIBS}) endif() @@ -272,6 +280,9 @@ endif() file(GLOB UNIT_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/unittests/ ${CMAKE_CURRENT_SOURCE_DIR}/unittests/*.chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/3.x/*.chai) list(SORT UNIT_TESTS) +file(GLOB PERFORMANCE_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/performance_tests/ ${CMAKE_CURRENT_SOURCE_DIR}/performance_tests/*.chai) +list(SORT PERFORMANCE_TESTS) + if (RUN_FUZZY_TESTS) @@ -336,8 +347,8 @@ if(BUILD_TESTING) string(REGEX MATCHALL "TEST_CASE\\([ ]*\"[^\"]+\"" found_tests ${contents}) foreach(hit ${found_tests}) string(REGEX REPLACE "TEST_CASE\\([ ]*(\"[^\"]+\").*" "\\1" test_name ${hit}) - add_test(${test_name} "${executable}" ${test_name}) - set_tests_properties(${test_name} PROPERTIES TIMEOUT 660 ENVIRONMENT "PATH=${NEWPATH}") + add_test(compiled.${test_name} "${executable}" ${test_name}) + set_tests_properties(compiled.${test_name} PROPERTIES TIMEOUT 660 ENVIRONMENT "PATH=${NEWPATH}") endforeach() endif() endif() @@ -371,12 +382,32 @@ if(BUILD_TESTING) ) + set(TESTS "") + foreach(filename ${UNIT_TESTS}) - message(STATUS "Adding test ${filename}") - add_test(${filename} chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/unit_test.inc ${CMAKE_CURRENT_SOURCE_DIR}/unittests/${filename}) + message(STATUS "Adding unit test ${filename}") + add_test(unit.${filename} chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/unit_test.inc ${CMAKE_CURRENT_SOURCE_DIR}/unittests/${filename}) + list(APPEND TESTS unit.${filename}) endforeach() - set_property(TEST ${UNIT_TESTS} + if (RUN_PERFORMANCE_TESTS) + foreach(filename ${PERFORMANCE_TESTS}) + message(STATUS "Adding performance test ${filename}") + + add_test(NAME performance.${filename} COMMAND ${VALGRIND} --tool=callgrind --callgrind-out-file=callgrind.performance.${filename} $ ${CMAKE_CURRENT_SOURCE_DIR}/performance_tests/${filename}) + list(APPEND TESTS performance.${filename}) + endforeach() + + add_executable(profile_cpp_calls_2 performance_tests/profile_cpp_calls_2.cpp) + target_link_libraries(profile_cpp_calls_2 ${LIBS}) + add_test(NAME performance.profile_cpp_calls_2 COMMAND ${VALGRIND} --tool=callgrind --callgrind-out-file=callgrind.performance.profile_cpp_calls_2 $) + + add_executable(profile_fun_wrappers performance_tests/profile_fun_wrappers.cpp) + target_link_libraries(profile_fun_wrappers ${LIBS}) + add_test(NAME performance.profile_fun_wrappers COMMAND ${VALGRIND} --tool=callgrind --callgrind-out-file=callgrind.performance.profile_fun_wrappers $) + endif() + + set_property(TEST ${TESTS} PROPERTY ENVIRONMENT "CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/" "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" diff --git a/LICENSE b/LICENSE index 48f5292..105662d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2009-2015 Jason Turner +Copyright 2009-2016 Jason Turner Copyright 2009-2012 Jonathan Turner. All Rights Reserved. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..c402efe --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,16 @@ +version: 5.7.2.{build} +os: Visual Studio 2015 +environment: + matrix: + - {} +build_script: +- cmd: >- + mkdir build + + cd build + + cmake c:\Projects\chaiscript -G "Visual Studio 14" + + cmake --build . --config Debug +test_script: +- cmd: ctest -C Debug diff --git a/cheatsheet.md b/cheatsheet.md index 9ff4ad4..ce8e04a 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -77,7 +77,7 @@ chai.add(chaiscript::constructor(), "MyType"); It's not strictly necessary to add types, but it helps with many things. Cloning, better errors, etc. ``` -chai.add(chaiscript::user_type, "MyClass"); +chai.add(chaiscript::user_type(), "MyClass"); ``` ## Adding Type Conversions @@ -90,6 +90,13 @@ A helper function exists for strongly typed and ChaiScript `Vector` function con chai.add(chaiscript::vector_conversion>()); ``` +A helper function also exists for strongly typed and ChaiScript `Map` function conversion definition: + +``` +chai.add(chaiscript::map_conversion>()); +``` + + This allows you to pass a ChaiScript function to a function requiring `std::vector` ## Adding Objects @@ -100,8 +107,9 @@ chai.add(chaiscript::var(std::ref(somevar), "somevar"); // by reference, shared auto shareddouble = std::make_shared(4.3); chai.add(chaiscript::var(shareddouble), "shareddouble"); // by shared_ptr, shared between c++ and chai chai.add(chaiscript::const_var(somevar), "somevar"); // copied in and made const -chai.add_global_const(chaiscript::const_var(somevar), "somevar"); // global const. Throws if value is non-const -chai.add_global(chaiscript::var(somevar), "somevar"); // global non-const +chai.add_global_const(chaiscript::const_var(somevar), "somevar"); // global const. Throws if value is non-const, throws if object exists +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. @@ -221,11 +229,13 @@ var k = 5; // initialized to 5 (integer) var l := k; // reference to k auto &m = k; // reference to k -GLOBAL g = 5; // creates a global variable. If global already exists, it is not re-added -GLOBAL g = 2; // global 'g' now equals 2 +global g = 5; // creates a global variable. If global already exists, it is not re-added +global g = 2; // global 'g' now equals 2 -GLOBAL g2; -if (g2.is_var_undef()) { g2 = 4; } // only initialize g2 once, if GLOBAL decl hit more than once +global g2; +if (g2.is_var_undef()) { g2 = 4; } // only initialize g2 once, if global decl hit more than once + +GLOBAL g3; // all upper case version also accepted ``` ## Built in Types @@ -354,6 +364,19 @@ o.f = fun(y) { print(this.x + y); } o.f(10); // prints 13 ``` +### Option Explicit + +If you want to disable dynamic parameter definitions, you can `set_explicit`. + +``` +class My_Class { + def My_Class() { + this.set_explicit(true); + this.x = 2; // this would fail with explicit set to true + } +}; +``` + ## method_missing A function of the signature `method_missing(object, name, param1, param2, param3)` will be called if an appropriate diff --git a/cmake/CheckCXX11Features.cmake b/cmake/CheckCXX11Features.cmake deleted file mode 100644 index 27b823d..0000000 --- a/cmake/CheckCXX11Features.cmake +++ /dev/null @@ -1,103 +0,0 @@ -# Checks for C++11 features -# CXX11_FEATURE_LIST - a list containing all supported features -# HAS_CXX11_AUTO - auto keyword -# HAS_CXX11_NULLPTR - nullptr -# HAS_CXX11_LAMBDA - lambdas -# HAS_CXX11_STATIC_ASSERT - static_assert() -# HAS_CXX11_RVALUE_REFERENCES - rvalue references -# HAS_CXX11_DECLTYPE - decltype keyword -# HAS_CXX11_CSTDINT_H - cstdint header -# HAS_CXX11_LONG_LONG - long long signed & unsigned types -# HAS_CXX11_VARIADIC_TEMPLATES - variadic templates -# HAS_CXX11_CONSTEXPR - constexpr keyword -# HAS_CXX11_SIZEOF_MEMBER - sizeof() non-static members -# HAS_CXX11_FUNC - __func__ preprocessor constant -# -# Original script by Rolf Eike Beer -# Modifications by Andreas Weis -# -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.3) - -SET(CHECK_CXX11_OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) -IF(CMAKE_COMPILER_IS_GNUCXX) - SET(CMAKE_CXX_FLAGS "-std=c++0x") -endif() - -MACRO(CXX11_CHECK_FEATURE FEATURE_NAME FEATURE_NUMBER RESULT_VAR) - IF (NOT DEFINED ${RESULT_VAR}) - SET(_bindir "${CMAKE_CURRENT_BINARY_DIR}/cxx11/cxx11_${FEATURE_NAME}") - - IF (${FEATURE_NUMBER}) - SET(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/c++11-test-${FEATURE_NAME}-N${FEATURE_NUMBER}) - SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})") - ELSE (${FEATURE_NUMBER}) - SET(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/c++11-test-${FEATURE_NAME}) - SET(_LOG_NAME "\"${FEATURE_NAME}\"") - ENDIF (${FEATURE_NUMBER}) - MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME}") - - SET(_SRCFILE "${_SRCFILE_BASE}.cpp") - SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.cpp") - SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.cpp") - - IF (CROSS_COMPILING) - try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}") - IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) - try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}") - ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) - ELSE (CROSS_COMPILING) - try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR - "${_bindir}" "${_SRCFILE}") - IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) - SET(${RESULT_VAR} TRUE) - ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) - SET(${RESULT_VAR} FALSE) - ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) - IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) - try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR - "${_bindir}_fail" "${_SRCFILE_FAIL}") - IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) - SET(${RESULT_VAR} TRUE) - ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) - SET(${RESULT_VAR} FALSE) - ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) - ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) - ENDIF (CROSS_COMPILING) - IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE}) - try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}") - IF (_TMP_RESULT) - SET(${RESULT_VAR} FALSE) - ELSE (_TMP_RESULT) - SET(${RESULT_VAR} TRUE) - ENDIF (_TMP_RESULT) - ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE}) - - IF (${RESULT_VAR}) - MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME} -- works") - LIST(APPEND CXX11_FEATURE_LIST ${RESULT_VAR}) - ELSE (${RESULT_VAR}) - MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME} -- not supported") - ENDIF (${RESULT_VAR}) - SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "C++11 support for ${_LOG_NAME}") - ENDIF (NOT DEFINED ${RESULT_VAR}) -ENDMACRO(CXX11_CHECK_FEATURE) - -CXX11_CHECK_FEATURE("auto" 2546 HAS_CXX11_AUTO) -CXX11_CHECK_FEATURE("nullptr" 2431 HAS_CXX11_NULLPTR) -CXX11_CHECK_FEATURE("lambda" 2927 HAS_CXX11_LAMBDA) -CXX11_CHECK_FEATURE("static_assert" 1720 HAS_CXX11_STATIC_ASSERT) -CXX11_CHECK_FEATURE("rvalue_references" 2118 HAS_CXX11_RVALUE_REFERENCES) -CXX11_CHECK_FEATURE("decltype" 2343 HAS_CXX11_DECLTYPE) -CXX11_CHECK_FEATURE("cstdint" "" HAS_CXX11_CSTDINT_H) -CXX11_CHECK_FEATURE("long_long" 1811 HAS_CXX11_LONG_LONG) -CXX11_CHECK_FEATURE("variadic_templates" 2555 HAS_CXX11_VARIADIC_TEMPLATES) -CXX11_CHECK_FEATURE("constexpr" 2235 HAS_CXX11_CONSTEXPR) -CXX11_CHECK_FEATURE("sizeof_member" 2253 HAS_CXX11_SIZEOF_MEMBER) -CXX11_CHECK_FEATURE("__func__" 2340 HAS_CXX11_FUNC) - -SET(CXX11_FEATURE_LIST ${CXX11_FEATURE_LIST} CACHE STRING "C++11 feature support list") -MARK_AS_ADVANCED(FORCE CXX11_FEATURE_LIST) - -SET(CMAKE_CXX_FLAGS ${CHECK_CXX11_OLD_CMAKE_CXX_FLAGS}) -UNSET(CHECK_CXX11_OLD_CMAKE_CXX_FLAGS) - diff --git a/cmake/c++11-test-__func__-N2340.cpp b/cmake/c++11-test-__func__-N2340.cpp deleted file mode 100644 index d961df8..0000000 --- a/cmake/c++11-test-__func__-N2340.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -int main() -{ - if (!__func__) { return 1; } - if(std::strlen(__func__) <= 0) { return 1; } - return 0; -} diff --git a/cmake/c++11-test-auto-N2546.cpp b/cmake/c++11-test-auto-N2546.cpp deleted file mode 100644 index 948648e..0000000 --- a/cmake/c++11-test-auto-N2546.cpp +++ /dev/null @@ -1,12 +0,0 @@ - -int main() -{ - auto i = 5; - auto f = 3.14159f; - auto d = 3.14159; - bool ret = ( - (sizeof(f) < sizeof(d)) && - (sizeof(i) == sizeof(int)) - ); - return ret ? 0 : 1; -} diff --git a/cmake/c++11-test-constexpr-N2235.cpp b/cmake/c++11-test-constexpr-N2235.cpp deleted file mode 100644 index ed62451..0000000 --- a/cmake/c++11-test-constexpr-N2235.cpp +++ /dev/null @@ -1,19 +0,0 @@ -constexpr int square(int x) -{ - return x*x; -} - -constexpr int the_answer() -{ - return 42; -} - -int main() -{ - int test_arr[square(3)]; - bool ret = ( - (square(the_answer()) == 1764) && - (sizeof(test_arr)/sizeof(test_arr[0]) == 9) - ); - return ret ? 0 : 1; -} diff --git a/cmake/c++11-test-cstdint.cpp b/cmake/c++11-test-cstdint.cpp deleted file mode 100644 index be2878f..0000000 --- a/cmake/c++11-test-cstdint.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include -int main() -{ - bool test = - (sizeof(int8_t) == 1) && - (sizeof(int16_t) == 2) && - (sizeof(int32_t) == 4) && - (sizeof(int64_t) == 8); - return test ? 0 : 1; -} diff --git a/cmake/c++11-test-decltype-N2343.cpp b/cmake/c++11-test-decltype-N2343.cpp deleted file mode 100644 index 843f83a..0000000 --- a/cmake/c++11-test-decltype-N2343.cpp +++ /dev/null @@ -1,11 +0,0 @@ - -bool check_size(int i) -{ - return sizeof(int) == sizeof(decltype(i)); -} - -int main() -{ - bool ret = check_size(42); - return ret ? 0 : 1; -} diff --git a/cmake/c++11-test-lambda-N2927.cpp b/cmake/c++11-test-lambda-N2927.cpp deleted file mode 100644 index 4c33ed5..0000000 --- a/cmake/c++11-test-lambda-N2927.cpp +++ /dev/null @@ -1,5 +0,0 @@ -int main() -{ - int ret = 0; - return ([&ret]() -> int { return ret; })(); -} diff --git a/cmake/c++11-test-long_long-N1811.cpp b/cmake/c++11-test-long_long-N1811.cpp deleted file mode 100644 index 0911127..0000000 --- a/cmake/c++11-test-long_long-N1811.cpp +++ /dev/null @@ -1,7 +0,0 @@ -int main(void) -{ - long long l; - unsigned long long ul; - - return ((sizeof(l) >= 8) && (sizeof(ul) >= 8)) ? 0 : 1; -} diff --git a/cmake/c++11-test-nullptr-N2431.cpp b/cmake/c++11-test-nullptr-N2431.cpp deleted file mode 100644 index c78fac4..0000000 --- a/cmake/c++11-test-nullptr-N2431.cpp +++ /dev/null @@ -1,5 +0,0 @@ -int main() -{ - int* test = nullptr; - return test ? 1 : 0; -} diff --git a/cmake/c++11-test-nullptr-N2431_fail_compile.cpp b/cmake/c++11-test-nullptr-N2431_fail_compile.cpp deleted file mode 100644 index 7ab77a2..0000000 --- a/cmake/c++11-test-nullptr-N2431_fail_compile.cpp +++ /dev/null @@ -1,5 +0,0 @@ -int main() -{ - int i = nullptr; - return 1; -} diff --git a/cmake/c++11-test-rvalue_references-N2118.cpp b/cmake/c++11-test-rvalue_references-N2118.cpp deleted file mode 100644 index 75fb555..0000000 --- a/cmake/c++11-test-rvalue_references-N2118.cpp +++ /dev/null @@ -1,15 +0,0 @@ -int foo(int& lvalue) -{ - return 123; -} - -int foo(int&& rvalue) -{ - return 321; -} - -int main() -{ - int i = 42; - return ((foo(i) == 123) && (foo(42) == 321)) ? 0 : 1; -} diff --git a/cmake/c++11-test-sizeof_member-N2253.cpp b/cmake/c++11-test-sizeof_member-N2253.cpp deleted file mode 100644 index a55fc09..0000000 --- a/cmake/c++11-test-sizeof_member-N2253.cpp +++ /dev/null @@ -1,14 +0,0 @@ -struct foo { - char bar; - int baz; -}; - -int main(void) -{ - bool ret = ( - (sizeof(foo::bar) == 1) && - (sizeof(foo::baz) >= sizeof(foo::bar)) && - (sizeof(foo) >= sizeof(foo::bar)+sizeof(foo::baz)) - ); - return ret ? 0 : 1; -} diff --git a/cmake/c++11-test-static_assert-N1720.cpp b/cmake/c++11-test-static_assert-N1720.cpp deleted file mode 100644 index c3d74ca..0000000 --- a/cmake/c++11-test-static_assert-N1720.cpp +++ /dev/null @@ -1,5 +0,0 @@ -int main() -{ - static_assert(0 < 1, "your ordering of integers is screwed"); - return 0; -} diff --git a/cmake/c++11-test-static_assert-N1720_fail_compile.cpp b/cmake/c++11-test-static_assert-N1720_fail_compile.cpp deleted file mode 100644 index 4cb1183..0000000 --- a/cmake/c++11-test-static_assert-N1720_fail_compile.cpp +++ /dev/null @@ -1,5 +0,0 @@ -int main() -{ - static_assert(1 < 0, "this should fail"); - return 0; -} diff --git a/cmake/c++11-test-variadic_templates-N2555.cpp b/cmake/c++11-test-variadic_templates-N2555.cpp deleted file mode 100644 index 4518e88..0000000 --- a/cmake/c++11-test-variadic_templates-N2555.cpp +++ /dev/null @@ -1,23 +0,0 @@ -int Accumulate() -{ - return 0; -} - -template -int Accumulate(T v, Ts... vs) -{ - return v + Accumulate(vs...); -} - -template -int CountElements() -{ - return sizeof...(Is); -} - -int main() -{ - int acc = Accumulate(1, 2, 3, 4, -5); - int count = CountElements<1,2,3,4,5>(); - return ((acc == 5) && (count == 5)) ? 0 : 1; -} diff --git a/contrib/check_for_tabs.rb b/contrib/check_for_tabs.rb new file mode 100755 index 0000000..bee7d0d --- /dev/null +++ b/contrib/check_for_tabs.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +require 'json' + +`grep -rPIHn '\t' src/* include/* samples/*`.lines { |line| + if /(?.+(hpp|cpp|chai)):(?[0-9]+):(?.+)/ =~ line + puts(JSON.dump({:line => linenumber, :filename => filename, :tool => "tab_checker", :message => "Source Code Line Contains Tabs", :messagetype => "warning"})) + end +} + + diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 1b37e9b..79ef8cf 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_HPP_ diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 5b3bea5..8d250eb 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -1,18 +1,22 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_DEFINES_HPP_ #define CHAISCRIPT_DEFINES_HPP_ #ifdef _MSC_VER +#define CHAISCRIPT_STRINGIZE(x) "" #x +#define CHAISCRIPT_COMPILER_VERSION CHAISCRIPT_STRINGIZE(_MSC_FULL_VER) #define CHAISCRIPT_MSVC _MSC_VER #define CHAISCRIPT_HAS_DECLSPEC #if _MSC_VER <= 1800 #define CHAISCRIPT_MSVC_12 #endif +#else +#define CHAISCRIPT_COMPILER_VERSION __VERSION__ #endif #ifndef CHAISCRIPT_MSVC_12 @@ -29,7 +33,25 @@ #define CHAISCRIPT_WINDOWS #endif -#if (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || (defined(__llvm__) && !defined(CHAISCRIPT_LIBCPP)) +#if defined(_WIN32) +#if defined(__llvm__) +#define CHAISCRIPT_COMPILER_NAME "clang(windows)" +#elif defined(__GNUC__) +#define CHAISCRIPT_COMPILER_NAME "gcc(mingw)" +#else +#define CHAISCRIPT_COMPILER_NAME "msvc" +#endif +#else +#if defined(__llvm__) +#define CHAISCRIPT_COMPILER_NAME "clang" +#elif defined(__GNUC__) +#define CHAISCRIPT_COMPILER_NAME "gcc" +#else +#define CHAISCRIPT_COMPILER_NAME "unknown" +#endif +#endif + +#if (defined(CHAISCRIPT_MSVC) && !defined(CHAISCRIPT_MSVC_12)) || (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || (defined(__llvm__) && !defined(CHAISCRIPT_LIBCPP)) /// Currently only g++>=4.8 supports this natively /// \todo Make this support other compilers when possible #define CHAISCRIPT_HAS_THREAD_LOCAL @@ -39,7 +61,11 @@ #define CHAISCRIPT_GCC_4_6 #endif -#if (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || defined(CHAISCRIPT_MSVC) || defined(__llvm__) +#if defined(__llvm__) +#define CHAISCRIPT_CLANG +#endif + +#if (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || defined(CHAISCRIPT_MSVC) || defined(CHAISCRIPT_CLANG) #define CHAISCRIPT_OVERRIDE override #else #define CHAISCRIPT_OVERRIDE @@ -52,7 +78,7 @@ #define CHAISCRIPT_MODULE_EXPORT extern "C" #endif -#ifdef CHAISCRIPT_MSVC +#ifdef CHAISCRIPT_MSVC_12 #define CHAISCRIPT_NOEXCEPT throw() #define CHAISCRIPT_CONSTEXPR #else @@ -60,12 +86,22 @@ #define CHAISCRIPT_CONSTEXPR constexpr #endif +#ifdef _DEBUG +#define CHAISCRIPT_DEBUG true +#else +#define CHAISCRIPT_DEBUG false +#endif + #include namespace chaiscript { static const int version_major = 5; - static const int version_minor = 7; - static const int version_patch = 1; + static const int version_minor = 8; + static const int version_patch = 0; + + static const char *compiler_version = CHAISCRIPT_COMPILER_VERSION; + static const char *compiler_name = CHAISCRIPT_COMPILER_NAME; + static const bool debug_build = CHAISCRIPT_DEBUG; template inline std::shared_ptr make_shared(Arg && ... arg) diff --git a/include/chaiscript/chaiscript_stdlib.hpp b/include/chaiscript/chaiscript_stdlib.hpp index c07e64c..4b38f2c 100644 --- a/include/chaiscript/chaiscript_stdlib.hpp +++ b/include/chaiscript/chaiscript_stdlib.hpp @@ -19,6 +19,7 @@ #include "dispatchkit/bootstrap_stl.hpp" #include "dispatchkit/boxed_value.hpp" #include "language/chaiscript_prelude.chai" +#include "utility/json_wrap.hpp" #ifndef CHAISCRIPT_NO_THREADS #include @@ -51,6 +52,8 @@ namespace chaiscript lib->add(chaiscript::fun([](const std::function &t_func){ return std::async(std::launch::async, t_func);}), "async"); #endif + lib->add(json_wrap::library()); + lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ ); return lib; diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index 09fc617..2a0e63c 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_THREADING_HPP_ @@ -45,14 +45,14 @@ namespace chaiscript class unique_lock : public std::unique_lock { public: - unique_lock(T &t) : std::unique_lock(t) {} + explicit unique_lock(T &t) : std::unique_lock(t) {} }; template class shared_lock : public std::unique_lock { public: - shared_lock(T &t) : std::unique_lock(t) {} + explicit shared_lock(T &t) : std::unique_lock(t) {} void unlock() {} }; @@ -60,7 +60,7 @@ namespace chaiscript class lock_guard : public std::lock_guard { public: - lock_guard(T &t) : std::lock_guard(t) {} + explicit lock_guard(T &t) : std::lock_guard(t) {} }; class shared_mutex : public std::mutex { }; @@ -77,7 +77,7 @@ namespace chaiscript { public: - Thread_Storage(void *t_key) + explicit Thread_Storage(void *t_key) : m_key(t_key) { } @@ -129,7 +129,7 @@ namespace chaiscript { public: - Thread_Storage(void *) + explicit Thread_Storage(void *) { } @@ -160,13 +160,14 @@ namespace chaiscript { unique_lock lock(m_mutex); - auto itr = m_instances.find(std::this_thread::get_id()); + const auto id = std::this_thread::get_id(); + auto itr = m_instances.find(id); if (itr != m_instances.end()) { return itr->second; } std::shared_ptr new_instance(std::make_shared()); - m_instances.insert(std::make_pair(std::this_thread::get_id(), new_instance)); + m_instances.insert(std::make_pair(id, new_instance)); return new_instance; } @@ -182,7 +183,7 @@ namespace chaiscript class unique_lock { public: - unique_lock(T &) {} + explicit unique_lock(T &) {} void lock() {} void unlock() {} }; @@ -191,7 +192,7 @@ namespace chaiscript class shared_lock { public: - shared_lock(T &) {} + explicit shared_lock(T &) {} void lock() {} void unlock() {} }; @@ -200,7 +201,7 @@ namespace chaiscript class lock_guard { public: - lock_guard(T &) {} + explicit lock_guard(T &) {} }; class shared_mutex { }; @@ -212,7 +213,7 @@ namespace chaiscript class Thread_Storage { public: - Thread_Storage(void *) + explicit Thread_Storage(void *) { } diff --git a/include/chaiscript/dispatchkit/any.hpp b/include/chaiscript/dispatchkit/any.hpp index 8a624b1..e86ba0e 100644 --- a/include/chaiscript/dispatchkit/any.hpp +++ b/include/chaiscript/dispatchkit/any.hpp @@ -46,7 +46,7 @@ namespace chaiscript { private: struct Data { - Data(const std::type_info &t_type) + explicit Data(const std::type_info &t_type) : m_type(t_type) { } diff --git a/include/chaiscript/dispatchkit/bad_boxed_cast.hpp b/include/chaiscript/dispatchkit/bad_boxed_cast.hpp index c2be40f..0716749 100644 --- a/include/chaiscript/dispatchkit/bad_boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/bad_boxed_cast.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_BAD_BOXED_CAST_HPP_ @@ -40,7 +40,7 @@ namespace chaiscript { } - bad_boxed_cast(std::string t_what) CHAISCRIPT_NOEXCEPT + explicit bad_boxed_cast(std::string t_what) CHAISCRIPT_NOEXCEPT : to(nullptr), m_what(std::move(t_what)) { } diff --git a/include/chaiscript/dispatchkit/bind_first.hpp b/include/chaiscript/dispatchkit/bind_first.hpp index 5cd61da..fcba534 100644 --- a/include/chaiscript/dispatchkit/bind_first.hpp +++ b/include/chaiscript/dispatchkit/bind_first.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_BIND_FIRST_HPP_ diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 1624e02..ef9f032 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_BOOTSTRAP_HPP_ @@ -440,6 +440,8 @@ namespace chaiscript m->add(constructor(), "Dynamic_Object"); m->add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name"); m->add(fun(&dispatch::Dynamic_Object::get_attrs), "get_attrs"); + m->add(fun(&dispatch::Dynamic_Object::set_explicit), "set_explicit"); + m->add(fun(&dispatch::Dynamic_Object::is_explicit), "is_explicit"); m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); @@ -450,7 +452,13 @@ namespace chaiscript m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "[]"); m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "[]"); - m->eval("def Dynamic_Object::clone() { auto &new_o = Dynamic_Object(this.get_type_name()); for_each(this.get_attrs(), bind(fun(new_o, x) { new_o.get_attr(x.first) = x.second; }, new_o, _) ); return new_o; }"); + m->eval(R""( + def Dynamic_Object::clone() { + auto &new_o = Dynamic_Object(this.get_type_name()); + for_each(this.get_attrs(), fun[new_o](x) { new_o.get_attr(x.first) = x.second; } ); + new_o; + } + )""); m->add(fun(&has_guard), "has_guard"); m->add(fun(&get_guard), "get_guard"); @@ -460,9 +468,12 @@ namespace chaiscript m->add(fun(&Boxed_Value::is_const), "is_var_const"); m->add(fun(&Boxed_Value::is_ref), "is_var_reference"); m->add(fun(&Boxed_Value::is_pointer), "is_var_pointer"); + m->add(fun(&Boxed_Value::is_return_value), "is_var_return_value"); + m->add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value"); m->add(fun(&Boxed_Value::is_type), "is_type"); m->add(fun(&Boxed_Value::get_attr), "get_var_attr"); m->add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs"); + m->add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs"); m->add(fun(&Boxed_Value::get_type_info), "get_type_info"); m->add(user_type(), "Type_Info"); @@ -485,6 +496,7 @@ namespace chaiscript basic_constructors("bool", m); operators::assign(m); 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"); @@ -502,6 +514,8 @@ namespace chaiscript bootstrap_pod_type("long", m); bootstrap_pod_type("unsigned_int", m); bootstrap_pod_type("unsigned_long", m); + bootstrap_pod_type("long_long", m); + bootstrap_pod_type("unsigned_long_long", m); bootstrap_pod_type("size_t", m); bootstrap_pod_type("char", m); bootstrap_pod_type("wchar_t", m); @@ -556,13 +570,14 @@ namespace chaiscript "eval_error", { }, { {fun(&chaiscript::exception::eval_error::reason), "reason"}, - {fun(std::function (const chaiscript::exception::eval_error &t_eval_error)>([](const chaiscript::exception::eval_error &t_eval_error) -> std::vector { - 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"} } + {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 { + 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"} } ); @@ -588,7 +603,7 @@ namespace chaiscript std::vector retval; std::transform(t_node.children.begin(), t_node.children.end(), std::back_inserter(retval), - &chaiscript::var>); + &chaiscript::var &>); return retval; })), "children"}, {fun(&AST_Node::replace_child), "replace_child"} diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 1a03888..8c225c3 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com /// \file @@ -334,7 +334,12 @@ namespace chaiscript "# Pushes the second value onto the container while making a clone of the value\n" "def push_back(" + type + " container, x)\n" "{ \n" - " container.push_back_ref(clone(x)) \n" + " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" + " container.push_back_ref(x) \n" + " } else { \n" + " container.push_back_ref(clone(x)); \n" + " }\n" "} \n" ); @@ -370,7 +375,12 @@ namespace chaiscript "# Pushes the second value onto the front of container while making a clone of the value\n" "def push_front(" + type + " container, x)\n" "{ \n" - " container.push_front_ref(clone(x)) \n" + " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" + " container.push_front_ref(x) \n" + " } else { \n" + " container.push_front_ref(clone(x)); \n" + " }\n" "} \n" ); return "push_front_ref"; @@ -460,6 +470,30 @@ namespace chaiscript m->add(fun(static_cast(&MapType::at)), "at"); m->add(fun(static_cast(&MapType::at)), "at"); + if (typeid(MapType) == typeid(std::map)) + { + m->eval(R"( + def Map::`==`(Map rhs) { + if ( rhs.size() != this.size() ) { + return false; + } else { + auto r1 = range(this); + auto r2 = range(rhs); + while (!r1.empty()) + { + if (!eq(r1.front().first, r2.front().first) || !eq(r1.front().second, r2.front().second)) + { + return false; + } + r1.pop_front(); + r2.pop_front(); + } + true; + } + } )" + ); + } + container_type(type, m); default_constructible_type(type, m); assignable_type(type, m); @@ -515,7 +549,7 @@ namespace chaiscript if (typeid(VectorType) == typeid(std::vector)) { m->eval(R"( - def Vector::`==`(rhs) : type_match(rhs, this) { + def Vector::`==`(Vector rhs) { if ( rhs.size() != this.size() ) { return false; } else { diff --git a/include/chaiscript/dispatchkit/boxed_cast.hpp b/include/chaiscript/dispatchkit/boxed_cast.hpp index 1ddaacc..44450a0 100644 --- a/include/chaiscript/dispatchkit/boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_BOXED_CAST_HPP_ @@ -69,9 +69,9 @@ namespace chaiscript /// assert(i == 5); /// \endcode template - typename detail::Cast_Helper::Result_Type boxed_cast(const Boxed_Value &bv, const Type_Conversions *t_conversions = nullptr) + typename detail::Cast_Helper::Result_Type 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())) { + 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); } catch (const chaiscript::detail::exception::bad_any_cast &) { @@ -79,18 +79,18 @@ namespace chaiscript } - if (t_conversions && t_conversions->convertable_type()) + if (t_conversions && (*t_conversions)->convertable_type()) { try { // std::cout << "trying an up conversion " << typeid(Type).name() << '\n'; // 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(bv), t_conversions); + return detail::Cast_Helper::cast((*t_conversions)->boxed_type_conversion(t_conversions->saves(), bv), t_conversions); } catch (...) { try { // std::cout << "trying a down conversion " << typeid(Type).name() << '\n'; // try going the other way - down the inheritance graph - return detail::Cast_Helper::cast(t_conversions->boxed_type_down_conversion(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 8f478bf..505b1e1 100644 --- a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_BOXED_CAST_HELPER_HPP_ @@ -16,7 +16,7 @@ namespace chaiscript { - class Type_Conversions; + class Type_Conversions_State; namespace detail { @@ -33,14 +33,14 @@ namespace chaiscript template struct Cast_Helper_Inner { - typedef std::reference_wrapper::type > Result_Type; + typedef typename std::add_const::type Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) + static Result_Type 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 std::cref(*static_cast(p)); + return *static_cast(p); } else { throw chaiscript::detail::exception::bad_any_cast(); } @@ -52,23 +52,17 @@ namespace chaiscript { }; - /// Cast_Helper_Inner for casting to a const & type - template - struct Cast_Helper_Inner : Cast_Helper_Inner - { - }; - /// Cast_Helper_Inner for casting to a const * type template struct Cast_Helper_Inner { typedef const Result * Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) { if (ob.get_type_info().bare_equal_type_info(typeid(Result))) { - return static_cast(throw_if_null(ob.get_const_ptr())); + return static_cast(ob.get_const_ptr()); } else { throw chaiscript::detail::exception::bad_any_cast(); } @@ -80,11 +74,40 @@ namespace chaiscript struct Cast_Helper_Inner { typedef Result * Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) + static Result_Type 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(throw_if_null(ob.get_ptr())); + return static_cast(ob.get_ptr()); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + }; + + template + struct Cast_Helper_Inner : public Cast_Helper_Inner + { + }; + + template + struct Cast_Helper_Inner : public Cast_Helper_Inner + { + }; + + + /// Cast_Helper_Inner for casting to a & type + template + struct Cast_Helper_Inner + { + typedef const Result& Result_Type; + + static Result_Type 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(); } @@ -99,7 +122,7 @@ namespace chaiscript { typedef Result& Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) + static Result_Type 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))) { @@ -116,7 +139,7 @@ namespace chaiscript { typedef std::shared_ptr Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) { return ob.get().cast >(); } @@ -128,7 +151,7 @@ namespace chaiscript { typedef std::shared_ptr Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) { if (!ob.get_type_info().is_const()) { @@ -166,9 +189,9 @@ namespace chaiscript template<> struct Cast_Helper_Inner { - typedef const Boxed_Value & Result_Type; + typedef Boxed_Value Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) { return ob; } @@ -178,11 +201,11 @@ namespace chaiscript template<> struct Cast_Helper_Inner { - typedef Boxed_Value& Result_Type; + typedef std::reference_wrapper Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) { - return const_cast(ob); + return std::ref(const_cast(ob)); } }; @@ -236,7 +259,7 @@ namespace chaiscript { typedef typename Cast_Helper_Inner::Result_Type Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *t_conversions) + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *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 c953886..604d0d0 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_BOXED_NUMERIC_HPP_ @@ -100,6 +100,7 @@ namespace chaiscript :(Common_Types::t_uint64); } + static Common_Types get_common_type(const Boxed_Value &t_bv) { const Type_Info &inp_ = t_bv.get_type_info(); @@ -120,8 +121,12 @@ namespace chaiscript return get_common_type(sizeof(unsigned int), false); } else if (inp_ == typeid(long)) { return get_common_type(sizeof(long), true); + } else if (inp_ == typeid(long long)) { + return get_common_type(sizeof(long long), true); } else if (inp_ == typeid(unsigned long)) { return get_common_type(sizeof(unsigned long), false); + } else if (inp_ == typeid(unsigned long long)) { + return get_common_type(sizeof(unsigned long long), false); } else if (inp_ == typeid(std::int8_t)) { return Common_Types::t_int8; } else if (inp_ == typeid(std::int16_t)) { @@ -513,6 +518,21 @@ namespace chaiscript validate_boxed_number(bv); } + static bool is_floating_point(const Boxed_Value &t_bv) + { + const Type_Info &inp_ = t_bv.get_type_info(); + + if (inp_ == typeid(double)) { + return true; + } else if (inp_ == typeid(long double)) { + return true; + } else if (inp_ == typeid(float)) { + return true; + } else { + return false; + } + } + Boxed_Number get_as(const Type_Info &inp_) const { if (inp_.bare_equal_type_info(typeid(int))) { @@ -537,8 +557,12 @@ namespace chaiscript return Boxed_Number(get_as()); } else if (inp_.bare_equal_type_info(typeid(long))) { return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(long long))) { + return Boxed_Number(get_as()); } else if (inp_.bare_equal_type_info(typeid(unsigned long))) { return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(unsigned long long))) { + return Boxed_Number(get_as()); } else if (inp_.bare_equal_type_info(typeid(int8_t))) { return Boxed_Number(get_as()); } else if (inp_.bare_equal_type_info(typeid(int16_t))) { @@ -987,7 +1011,7 @@ namespace chaiscript { typedef Boxed_Number Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *) + static Result_Type 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 a6d9c5b..5a49119 100644 --- a/include/chaiscript/dispatchkit/boxed_value.hpp +++ b/include/chaiscript/dispatchkit/boxed_value.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_BOXED_VALUE_HPP_ @@ -297,6 +297,13 @@ namespace chaiscript return *this; } + Boxed_Value &clone_attrs(const Boxed_Value &t_obj) + { + copy_attrs(t_obj); + reset_return_value(); + return *this; + } + /// \returns true if the two Boxed_Values share the same internal type static bool type_match(const Boxed_Value &l, const Boxed_Value &r) CHAISCRIPT_NOEXCEPT @@ -330,9 +337,9 @@ namespace chaiscript /// /// @sa @ref adding_objects template - Boxed_Value var(T t) + Boxed_Value var(T &&t) { - return Boxed_Value(t); + return Boxed_Value(std::forward(t)); } namespace detail { diff --git a/include/chaiscript/dispatchkit/callable_traits.hpp b/include/chaiscript/dispatchkit/callable_traits.hpp index 9954eea..765e6d6 100644 --- a/include/chaiscript/dispatchkit/callable_traits.hpp +++ b/include/chaiscript/dispatchkit/callable_traits.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_CALLABLE_TRAITS_HPP_ @@ -61,6 +61,17 @@ namespace chaiscript { Ret (Class::*m_func)(Param...); }; + template + struct Arity + { + }; + + template + struct Arity + { + static const size_t arity = sizeof...(Params); + }; + template struct Function_Signature diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 43cb7b1..23dc7ee 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_DISPATCHKIT_HPP_ @@ -314,7 +314,7 @@ namespace chaiscript return arity; } - virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { return std::any_of(m_funcs.cbegin(), m_funcs.cend(), [&vals, &t_conversions](const Proxy_Function &f){ return f->call_match(vals, t_conversions); }); @@ -326,7 +326,7 @@ namespace chaiscript } protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { return dispatch::dispatch(m_funcs, params, t_conversions); } @@ -417,9 +417,9 @@ namespace chaiscript struct State { - std::map > m_functions; - std::map m_function_objects; - std::map m_boxed_functions; + std::vector>>> m_functions; + std::vector> m_function_objects; + std::vector> m_boxed_functions; std::map m_global_objects; Type_Name_Map m_types; std::set m_reserved_words; @@ -442,7 +442,8 @@ namespace chaiscript template typename detail::Cast_Helper::Result_Type boxed_cast(const Boxed_Value &bv) const { - return chaiscript::boxed_cast(bv, &m_conversions); + Type_Conversions_State state(m_conversions, m_conversions.conversion_saves()); + return chaiscript::boxed_cast(bv, &state); } /// Add a new conversion for upcasting to a base class @@ -482,24 +483,31 @@ 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 + void add_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); + } + + get_stack_data(t_holder).back().emplace_back(t_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 void add_object(const std::string &name, Boxed_Value obj) { - auto &stack_elem = get_stack_data().back(); - - if (std::any_of(stack_elem.begin(), stack_elem.end(), - [&](const std::pair &o) { - return o.first == name; - })) - { - throw chaiscript::exception::name_conflict_error(name); - } - - get_stack_data().back().emplace_back(name, std::move(obj)); - + add_object(name, std::move(obj), get_stack_holder()); } /// Adds a new global shared object, between all the threads @@ -511,7 +519,7 @@ namespace chaiscript throw chaiscript::exception::global_non_const(); } - chaiscript::detail::threading::unique_lock l(m_global_object_mutex); + chaiscript::detail::threading::unique_lock l(m_mutex); if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) { @@ -526,7 +534,7 @@ namespace chaiscript { validate_object_name(name); - chaiscript::detail::threading::unique_lock l(m_global_object_mutex); + chaiscript::detail::threading::unique_lock l(m_mutex); const auto itr = m_state.m_global_objects.find(name); if (itr == m_state.m_global_objects.end()) @@ -544,7 +552,7 @@ namespace chaiscript { validate_object_name(name); - chaiscript::detail::threading::unique_lock l(m_global_object_mutex); + chaiscript::detail::threading::unique_lock l(m_mutex); if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) { @@ -554,6 +562,21 @@ 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); + if (itr != m_state.m_global_objects.end()) + { + itr->second.assign(obj); + } else { + m_state.m_global_objects.insert(std::make_pair(name, obj)); + } + } /// Adds a new scope to the stack void new_scope() @@ -589,13 +612,13 @@ namespace chaiscript /// Pushes a new stack on to the list of stacks - void new_stack(Stack_Holder &t_holder) + static void new_stack(Stack_Holder &t_holder) { // add a new Stack with 1 element t_holder.stacks.emplace_back(1); } - void pop_stack(Stack_Holder &t_holder) + static void pop_stack(Stack_Holder &t_holder) { t_holder.stacks.pop_back(); } @@ -641,19 +664,21 @@ namespace chaiscript return stack[stack.size() - 1 - ((loc & static_cast(Loc::stack_mask)) >> 16)][loc & static_cast(Loc::loc_mask)].second; } - // Is the value we are looking for a global? - { - chaiscript::detail::threading::shared_lock l(m_global_object_mutex); + // Is the value we are looking for a global or function? + chaiscript::detail::threading::shared_lock l(m_mutex); - const auto itr = m_state.m_global_objects.find(name); - if (itr != m_state.m_global_objects.end()) - { - return itr->second; - } + const auto itr = m_state.m_global_objects.find(name); + if (itr != m_state.m_global_objects.end()) + { + return itr->second; } - // If all that failed, then check to see if it's a function - return get_function_object(name); + // no? is it a function object? + auto obj = get_function_object_int(name, loc); + if (obj.first != loc) t_loc.store(uint_fast32_t(obj.first), std::memory_order_relaxed); + return obj.second; + + } /// Registers a new named type @@ -711,20 +736,29 @@ namespace chaiscript return std::vector >(m_state.m_types.begin(), m_state.m_types.end()); } + std::shared_ptr> get_method_missing_functions() const + { + uint_fast32_t method_missing_loc = m_method_missing_loc.load(std::memory_order_relaxed); + auto method_missing_funs = get_function("method_missing", method_missing_loc); + if (method_missing_funs.first != method_missing_loc) m_method_missing_loc.store(uint_fast32_t(method_missing_funs.first), std::memory_order_relaxed); + return std::move(method_missing_funs.second); + } + + /// Return a function by name - std::vector< Proxy_Function > get_function(const std::string &t_name) const + std::pair>> get_function(const std::string &t_name, const size_t t_hint) const { chaiscript::detail::threading::shared_lock l(m_mutex); const auto &funs = get_functions_int(); - auto itr = funs.find(t_name); + auto itr = find_keyed_value(funs, t_name, t_hint); if (itr != funs.end()) { - return itr->second; + return std::make_pair(std::distance(funs.begin(), itr), itr->second); } else { - return std::vector(); + return std::make_pair(size_t(0), std::make_shared>()); } } @@ -732,28 +766,36 @@ namespace chaiscript /// \throws std::range_error if it does not Boxed_Value get_function_object(const std::string &t_name) const { -// std::cout << "Getting function object: " << t_name << '\n'; chaiscript::detail::threading::shared_lock l(m_mutex); + return get_function_object_int(t_name, 0).second; + } + + /// \returns a function object (Boxed_Value wrapper) if it exists + /// \throws std::range_error if it does not + /// \warn does not obtain a mutex lock. \sa get_function_object for public version + std::pair get_function_object_int(const std::string &t_name, const size_t t_hint) const + { const auto &funs = get_boxed_functions_int(); - auto itr = funs.find(t_name); + auto itr = find_keyed_value(funs, t_name, t_hint); if (itr != funs.end()) { - return itr->second; + return std::make_pair(std::distance(funs.begin(), itr), itr->second); } else { throw std::range_error("Object not found: " + t_name); } } + /// Return true if a function exists bool function_exists(const std::string &name) const { chaiscript::detail::threading::shared_lock l(m_mutex); const auto &functions = get_functions_int(); - return functions.find(name) != functions.end(); + return find_keyed_value(functions, name) != functions.end(); } /// \returns All values in the local thread state in the parent scope, or if it doesn't exist, @@ -810,11 +852,8 @@ namespace chaiscript } // add the global values - { - chaiscript::detail::threading::shared_lock l(m_global_object_mutex); - - retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end()); - } + chaiscript::detail::threading::shared_lock l(m_mutex); + retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end()); return retval; } @@ -851,7 +890,7 @@ namespace chaiscript for (const auto & function : functions) { - for (const auto & internal_func : function.second) + for (const auto & internal_func : *function.second) { rets.emplace_back(function.first, internal_func); } @@ -873,7 +912,7 @@ namespace chaiscript } bool is_attribute_call(const std::vector &t_funs, const std::vector &t_params, - bool t_has_params) const + bool t_has_params, const Type_Conversions_State &t_conversions) const { if (!t_has_params || t_params.empty()) { return false; @@ -881,7 +920,7 @@ namespace chaiscript for (const auto &fun : t_funs) { if (fun->is_attribute_function()) { - if (fun->compare_first_type(t_params[0], m_conversions)) { + if (fun->compare_first_type(t_params[0], t_conversions)) { return true; } } @@ -896,38 +935,55 @@ namespace chaiscript #pragma warning(push) #pragma warning(disable : 4715) #endif - Boxed_Value call_member(const std::string &t_name, const std::vector ¶ms, bool t_has_params) const + Boxed_Value call_member(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector ¶ms, bool t_has_params, + const Type_Conversions_State &t_conversions) { - const auto funs = get_function(t_name); + uint_fast32_t loc = t_loc.load(std::memory_order_relaxed); + const auto funs = get_function(t_name, loc); + if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed); const auto do_attribute_call = - [this](int l_num_params, const std::vector &l_params, const std::vector &l_funs, const Type_Conversions &l_conversions)->Boxed_Value + [this](int l_num_params, const std::vector &l_params, const std::vector &l_funs, const Type_Conversions_State &l_conversions)->Boxed_Value { std::vector attr_params{l_params.begin(), l_params.begin() + l_num_params}; - std::vector remaining_params{l_params.begin() + l_num_params, l_params.end()}; Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions); - if (!remaining_params.empty() || bv.get_type_info().bare_equal(user_type())) { + if (l_num_params < int(l_params.size()) || bv.get_type_info().bare_equal(user_type())) { + struct This_Foist { + This_Foist(Dispatch_Engine &e, const Boxed_Value &t_bv) : m_e(e) { + m_e.get().new_scope(); + m_e.get().add_object("__this", t_bv); + } + + ~This_Foist() { + m_e.get().pop_scope(); + } + + std::reference_wrapper m_e; + }; + + This_Foist fi(*this, l_params.front()); + auto func = boxed_cast>(bv); try { - return (*func)(remaining_params, l_conversions); + 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(remaining_params, std::vector{func}); + throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()}, std::vector{func}); } else { return bv; } }; - if (is_attribute_call(funs, params, t_has_params)) { - return do_attribute_call(1, params, funs, m_conversions); + if (is_attribute_call(*funs.second, params, t_has_params, t_conversions)) { + return do_attribute_call(1, params, *funs.second, t_conversions); } else { std::exception_ptr except; - if (!funs.empty()) { + if (!funs.second->empty()) { try { - return dispatch::dispatch(funs, params, m_conversions); + return dispatch::dispatch(*funs.second, params, t_conversions); } catch(chaiscript::exception::dispatch_error&) { except = std::current_exception(); } @@ -939,9 +995,11 @@ namespace chaiscript const auto functions = [&]()->std::vector { std::vector fs; - for (const auto &f : get_function("method_missing")) + const auto method_missing_funs = get_method_missing_functions(); + + for (const auto &f : *method_missing_funs) { - if(f->compare_first_type(params[0], m_conversions)) { + if(f->compare_first_type(params[0], t_conversions)) { fs.push_back(f); } } @@ -961,12 +1019,17 @@ namespace chaiscript }(); if (!functions.empty()) { - if (is_no_param) { - std::vector tmp_params(params); - tmp_params.insert(tmp_params.begin() + 1, var(t_name)); - return do_attribute_call(2, tmp_params, functions, m_conversions); - } else { - return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector(params.begin()+1, params.end()))}, m_conversions); + try { + if (is_no_param) { + std::vector tmp_params(params); + tmp_params.insert(tmp_params.begin() + 1, var(t_name)); + return do_attribute_call(2, tmp_params, functions, t_conversions); + } else { + return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector(params.begin()+1, params.end()))}, t_conversions); + } + } catch (const dispatch::option_explicit_set &e) { + throw chaiscript::exception::dispatch_error(params, std::vector(funs.second->begin(), funs.second->end()), + e.what()); } } @@ -975,7 +1038,7 @@ namespace chaiscript if (except) { std::rethrow_exception(except); } else { - throw chaiscript::exception::dispatch_error(params, std::vector(funs.begin(), funs.end())); + throw chaiscript::exception::dispatch_error(params, std::vector(funs.second->begin(), funs.second->end())); } } } @@ -985,30 +1048,15 @@ namespace chaiscript - Boxed_Value call_function(const std::string &t_name, const std::vector ¶ms) const + Boxed_Value call_function(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector ¶ms, + const Type_Conversions_State &t_conversions) const { - Boxed_Value bv = dispatch::dispatch(get_function(t_name), params, m_conversions); - // the result of a clone is never to be marked as a return_value - if (t_name == "clone") { - bv.reset_return_value(); - } - return bv; + uint_fast32_t loc = t_loc.load(std::memory_order_relaxed); + const auto funs = get_function(t_name, loc); + if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed); + return dispatch::dispatch(*funs.second, params, t_conversions); } - Boxed_Value call_function(const std::string &t_name) const - { - return call_function(t_name, std::vector()); - } - - Boxed_Value call_function(const std::string &t_name, Boxed_Value p1) const - { - return call_function(t_name, std::vector({std::move(p1)})); - } - - Boxed_Value call_function(const std::string &t_name, Boxed_Value p1, Boxed_Value p2) const - { - return call_function(t_name, std::vector({std::move(p1), std::move(p2)})); - } /// Dump object info to stdout void dump_object(const Boxed_Value &o) const @@ -1052,7 +1100,7 @@ namespace chaiscript /// Returns true if a call can be made that consists of the first parameter /// (the function) with the remaining parameters as its arguments. - Boxed_Value call_exists(const std::vector ¶ms) + Boxed_Value call_exists(const std::vector ¶ms) const { if (params.empty()) { @@ -1060,8 +1108,9 @@ namespace chaiscript } const Const_Proxy_Function &f = this->boxed_cast(params[0]); + const Type_Conversions_State convs(m_conversions, m_conversions.conversion_saves()); - return Boxed_Value(f->call_match(std::vector(params.begin() + 1, params.end()), m_conversions)); + return Boxed_Value(f->call_match(std::vector(params.begin() + 1, params.end()), convs)); } /// Dump all system info to stdout @@ -1119,7 +1168,6 @@ namespace chaiscript State get_state() const { chaiscript::detail::threading::shared_lock l(m_mutex); - chaiscript::detail::threading::shared_lock l2(m_global_object_mutex); return m_state; } @@ -1127,17 +1175,16 @@ namespace chaiscript void set_state(const State &t_state) { chaiscript::detail::threading::unique_lock l(m_mutex); - chaiscript::detail::threading::unique_lock l2(m_global_object_mutex); m_state = t_state; } - void save_function_params(Stack_Holder &t_s, std::initializer_list t_params) + static void save_function_params(Stack_Holder &t_s, std::initializer_list t_params) { t_s.call_params.back().insert(t_s.call_params.back().begin(), std::move(t_params)); } - void save_function_params(Stack_Holder &t_s, std::vector &&t_params) + static void save_function_params(Stack_Holder &t_s, std::vector &&t_params) { for (auto &¶m : t_params) { @@ -1145,7 +1192,7 @@ namespace chaiscript } } - void save_function_params(Stack_Holder &t_s, const std::vector &t_params) + static void save_function_params(Stack_Holder &t_s, const std::vector &t_params) { t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params.begin(), t_params.end()); } @@ -1165,19 +1212,19 @@ namespace chaiscript save_function_params(*m_stack_holder, t_params); } - void new_function_call(Stack_Holder &t_s) + void new_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves) { if (t_s.call_depth == 0) { - m_conversions.enable_conversion_saves(true); + m_conversions.enable_conversion_saves(t_saves, true); } ++t_s.call_depth; - save_function_params(m_conversions.take_saves()); + save_function_params(m_conversions.take_saves(t_saves)); } - void pop_function_call(Stack_Holder &t_s) + void pop_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves) { --t_s.call_depth; @@ -1186,18 +1233,18 @@ namespace chaiscript if (t_s.call_depth == 0) { t_s.call_params.back().clear(); - m_conversions.enable_conversion_saves(false); + m_conversions.enable_conversion_saves(t_saves, false); } } void new_function_call() { - new_function_call(*m_stack_holder); + new_function_call(*m_stack_holder, m_conversions.conversion_saves()); } void pop_function_call() { - pop_function_call(*m_stack_holder); + pop_function_call(*m_stack_holder, m_conversions.conversion_saves()); } Stack_Holder &get_stack_holder() @@ -1205,7 +1252,6 @@ namespace chaiscript return *m_stack_holder; } - private: /// Returns the current stack /// make const/non const versions const StackData &get_stack_data() const @@ -1213,7 +1259,7 @@ namespace chaiscript return m_stack_holder->stacks.back(); } - StackData &get_stack_data(Stack_Holder &t_holder) + static StackData &get_stack_data(Stack_Holder &t_holder) { return t_holder.stacks.back(); } @@ -1223,32 +1269,34 @@ namespace chaiscript return m_stack_holder->stacks.back(); } - const std::map &get_boxed_functions_int() const + private: + + const std::vector> &get_boxed_functions_int() const { return m_state.m_boxed_functions; } - std::map &get_boxed_functions_int() + std::vector> &get_boxed_functions_int() { return m_state.m_boxed_functions; } - const std::map &get_function_objects_int() const + const std::vector> &get_function_objects_int() const { return m_state.m_function_objects; } - std::map &get_function_objects_int() + std::vector> &get_function_objects_int() { return m_state.m_function_objects; } - const std::map > &get_functions_int() const + const std::vector>>> &get_functions_int() const { return m_state.m_functions; } - std::map > &get_functions_int() + std::vector>>> &get_functions_int() { return m_state.m_functions; } @@ -1362,6 +1410,49 @@ namespace chaiscript } } + template + static void add_keyed_value(Container &t_c, const Key &t_key, Value &&t_value) + { + auto itr = find_keyed_value(t_c, t_key); + + if (itr == t_c.end()) { + t_c.reserve(t_c.size() + 1); // tightly control growth of memory usage here + t_c.emplace_back(t_key, std::forward(t_value)); + } else { + typedef typename Container::value_type value_type; + *itr = value_type(t_key, std::forward(t_value)); + } + } + + template + static typename Container::iterator find_keyed_value(Container &t_c, const Key &t_key) + { + return std::find_if(t_c.begin(), t_c.end(), + [&t_key](const typename Container::value_type &o) { + return o.first == t_key; + }); + } + + template + static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key) + { + return std::find_if(t_c.begin(), t_c.end(), + [&t_key](const typename Container::value_type &o) { + return o.first == t_key; + }); + } + + template + static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key, const size_t t_hint) + { + if (t_c.size() > t_hint && t_c[t_hint].first == t_key) { + return t_c.begin() + t_hint; + } else { + return find_keyed_value(t_c, t_key); + } + } + + /// Implementation detail for adding a function. /// \throws exception::name_conflict_error if there's a function matching the given one being added void add_function(const Proxy_Function &t_f, const std::string &t_name) @@ -1370,16 +1461,13 @@ namespace chaiscript auto &funcs = get_functions_int(); - auto itr = funcs.find(t_name); - - auto &func_objs = get_function_objects_int(); - auto &boxed_funcs = get_boxed_functions_int(); + auto itr = find_keyed_value(funcs, t_name); Proxy_Function new_func = [&]() -> Proxy_Function { if (itr != funcs.end()) { - auto &vec = itr->second; + auto vec = *itr->second; for (const auto &func : vec) { if ((*t_f) == *(func)) @@ -1388,33 +1476,35 @@ namespace chaiscript } } + vec.reserve(vec.size() + 1); // tightly control vec growth vec.push_back(t_f); std::stable_sort(vec.begin(), vec.end(), &function_less_than); - return std::make_shared(vec); + itr->second = std::make_shared>(vec); + return std::make_shared(std::move(vec)); } else if (t_f->has_arithmetic_param()) { // if the function is the only function but it also contains // arithmetic operators, we must wrap it in a dispatch function // to allow for automatic arithmetic type conversions std::vector vec({t_f}); - funcs.insert(std::make_pair(t_name, vec)); + funcs.emplace_back(t_name, std::make_shared>(vec)); return std::make_shared(std::move(vec)); } else { - funcs.insert(std::make_pair(t_name, std::vector{t_f})); + funcs.emplace_back(t_name, std::make_shared>(std::initializer_list({t_f}))); return t_f; } }(); - boxed_funcs[t_name] = const_var(new_func); - func_objs[t_name] = std::move(new_func); + add_keyed_value(get_boxed_functions_int(), t_name, const_var(new_func)); + add_keyed_value(get_function_objects_int(), t_name, std::move(new_func)); } mutable chaiscript::detail::threading::shared_mutex m_mutex; - mutable chaiscript::detail::threading::shared_mutex m_global_object_mutex; Type_Conversions m_conversions; chaiscript::detail::threading::Thread_Storage m_stack_holder; + mutable std::atomic_uint_fast32_t m_method_missing_loc; State m_state; }; @@ -1424,7 +1514,8 @@ namespace chaiscript public: Dispatch_State(Dispatch_Engine &t_engine) : m_engine(t_engine), - m_stack_holder(t_engine.get_stack_holder()) + m_stack_holder(t_engine.get_stack_holder()), + m_conversions(t_engine.conversions(), t_engine.conversions().conversion_saves()) { } @@ -1440,9 +1531,22 @@ namespace chaiscript return m_stack_holder.get(); } + const Type_Conversions_State &conversions() const { + return m_conversions; + } + + Type_Conversions::Conversion_Saves &conversion_saves() const { + return m_conversions.saves(); + } + + 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()); + } + private: std::reference_wrapper m_engine; std::reference_wrapper m_stack_holder; + Type_Conversions_State m_conversions; }; } } diff --git a/include/chaiscript/dispatchkit/dynamic_object.hpp b/include/chaiscript/dispatchkit/dynamic_object.hpp index 5a9ccaa..05fc069 100644 --- a/include/chaiscript/dispatchkit/dynamic_object.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_DYNAMIC_OBJECT_HPP_ @@ -24,23 +24,55 @@ namespace chaiscript { namespace dispatch { + struct option_explicit_set : std::runtime_error { + option_explicit_set(const std::string &t_param_name) + : std::runtime_error("option explicit set and parameter '" + t_param_name + "' does not exist") + { + + } + + option_explicit_set(const option_explicit_set &) = default; + + virtual ~option_explicit_set() CHAISCRIPT_NOEXCEPT {} + }; + class Dynamic_Object { public: Dynamic_Object(std::string t_type_name) - : m_type_name(std::move(t_type_name)) + : m_type_name(std::move(t_type_name)), m_option_explicit(false) { } - Dynamic_Object() : m_type_name("") + Dynamic_Object() : m_type_name(""), m_option_explicit(false) { } + bool is_explicit() const + { + return m_option_explicit; + } + + void set_explicit(const bool t_explicit) + { + m_option_explicit = t_explicit; + } + std::string get_type_name() const { return m_type_name; } + const Boxed_Value &operator[](const std::string &t_attr_name) const + { + return get_attr(t_attr_name); + } + + Boxed_Value &operator[](const std::string &t_attr_name) + { + return get_attr(t_attr_name); + } + const Boxed_Value &get_attr(const std::string &t_attr_name) const { auto a = m_attrs.find(t_attr_name); @@ -59,11 +91,19 @@ namespace chaiscript Boxed_Value &method_missing(const std::string &t_method_name) { + if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { + throw option_explicit_set(t_method_name); + } + return get_attr(t_method_name); } const Boxed_Value &method_missing(const std::string &t_method_name) const { + if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { + throw option_explicit_set(t_method_name); + } + return get_attr(t_method_name); } @@ -75,6 +115,7 @@ namespace chaiscript private: std::string m_type_name; + bool m_option_explicit; std::map m_attrs; }; diff --git a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp index 9d501ae..8c9b8f6 100644 --- a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_DYNAMIC_OBJECT_DETAIL_HPP_ @@ -84,7 +84,7 @@ namespace chaiscript virtual bool is_attribute_function() const CHAISCRIPT_OVERRIDE { return m_is_attribute; } - virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions)) { @@ -106,7 +106,7 @@ namespace chaiscript protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { if (dynamic_object_typename_match(params, m_type_name, m_ti, t_conversions)) { @@ -116,7 +116,7 @@ namespace chaiscript } } - virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { return dynamic_object_typename_match(bv, m_type_name, m_ti, t_conversions); } @@ -134,7 +134,7 @@ namespace chaiscript } bool dynamic_object_typename_match(const Boxed_Value &bv, const std::string &name, - const std::unique_ptr &ti, const Type_Conversions &t_conversions) const + const std::unique_ptr &ti, const Type_Conversions_State &t_conversions) const { if (bv.get_type_info().bare_equal(m_doti)) { @@ -156,7 +156,7 @@ namespace chaiscript } bool dynamic_object_typename_match(const std::vector &bvs, const std::string &name, - const std::unique_ptr &ti, const Type_Conversions &t_conversions) const + const std::unique_ptr &ti, const Type_Conversions_State &t_conversions) const { if (bvs.size() > 0) { @@ -216,7 +216,7 @@ namespace chaiscript return dc && dc->m_type_name == m_type_name && (*dc->m_func) == (*m_func); } - virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { std::vector new_vals{Boxed_Value(Dynamic_Object(m_type_name))}; new_vals.insert(new_vals.end(), vals.begin(), vals.end()); @@ -230,9 +230,9 @@ namespace chaiscript } protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { - auto bv = var(Dynamic_Object(m_type_name)); + auto bv = Boxed_Value(Dynamic_Object(m_type_name), true); std::vector new_params{bv}; new_params.insert(new_params.end(), params.begin(), params.end()); diff --git a/include/chaiscript/dispatchkit/exception_specification.hpp b/include/chaiscript/dispatchkit/exception_specification.hpp index 54f19c4..55398f7 100644 --- a/include/chaiscript/dispatchkit/exception_specification.hpp +++ b/include/chaiscript/dispatchkit/exception_specification.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_ @@ -32,7 +32,7 @@ namespace chaiscript protected: template - void throw_type(const Boxed_Value &bv, const Dispatch_Engine &t_engine) + static void throw_type(const Boxed_Value &bv, const Dispatch_Engine &t_engine) { try { T t = t_engine.boxed_cast(bv); throw t; } catch (const chaiscript::exception::bad_boxed_cast &) {} } diff --git a/include/chaiscript/dispatchkit/function_call.hpp b/include/chaiscript/dispatchkit/function_call.hpp index 3c8e3c5..ea0a61e 100644 --- a/include/chaiscript/dispatchkit/function_call.hpp +++ b/include/chaiscript/dispatchkit/function_call.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_FUNCTION_CALL_HPP_ @@ -14,10 +14,11 @@ #include "boxed_cast.hpp" #include "function_call_detail.hpp" #include "proxy_functions.hpp" +#include "callable_traits.hpp" namespace chaiscript { class Boxed_Value; -class Type_Conversions; +class Type_Conversions_State; namespace detail { template struct Cast_Helper; } // namespace detail @@ -35,8 +36,17 @@ namespace chaiscript /// \param[in] funcs the set of functions to dispatch on. template std::function - functor(const std::vector &funcs, const Type_Conversions *t_conversions) + functor(const std::vector &funcs, const Type_Conversions_State *t_conversions) { + const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(), + [](const Const_Proxy_Function &f) { + return f->get_arity() == -1 || size_t(f->get_arity()) == chaiscript::dispatch::detail::Arity::arity; + }); + + if (!has_arity_match) { + throw exception::bad_boxed_cast(user_type(), typeid(std::function)); + } + FunctionType *p=nullptr; return detail::build_function_caller_helper(p, funcs, t_conversions); } @@ -54,7 +64,7 @@ namespace chaiscript /// \param[in] func A function to execute. template std::function - functor(Const_Proxy_Function func, const Type_Conversions *t_conversions) + functor(Const_Proxy_Function func, const Type_Conversions_State *t_conversions) { return functor(std::vector({std::move(func)}), t_conversions); } @@ -63,7 +73,7 @@ namespace chaiscript /// and creating a typesafe C++ function caller from it. template std::function - functor(const Boxed_Value &bv, const Type_Conversions *t_conversions) + functor(const Boxed_Value &bv, const Type_Conversions_State *t_conversions) { return functor(boxed_cast(bv, t_conversions), t_conversions); } @@ -76,7 +86,7 @@ namespace chaiscript { typedef std::function Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *t_conversions) + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { if (ob.get_type_info().bare_equal(user_type())) { @@ -93,7 +103,7 @@ namespace chaiscript { typedef std::function Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *t_conversions) + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { if (ob.get_type_info().bare_equal(user_type())) { @@ -110,7 +120,7 @@ namespace chaiscript { typedef std::function Result_Type; - static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *t_conversions) + static Result_Type 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/function_call_detail.hpp b/include/chaiscript/dispatchkit/function_call_detail.hpp index 4054b59..8ed6115 100644 --- a/include/chaiscript/dispatchkit/function_call_detail.hpp +++ b/include/chaiscript/dispatchkit/function_call_detail.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_ @@ -31,9 +31,15 @@ namespace chaiscript struct Function_Caller_Ret { static Ret call(const std::vector &t_funcs, - const std::vector ¶ms, const Type_Conversions *t_conversions) + const std::vector ¶ms, const Type_Conversions_State *t_conversions) { - return boxed_cast(dispatch::dispatch(t_funcs, params, t_conversions?*t_conversions:Type_Conversions()), t_conversions); + if (t_conversions) { + return boxed_cast(dispatch::dispatch(t_funcs, params, *t_conversions), t_conversions); + } else { + Type_Conversions conv; + Type_Conversions_State state(conv, conv.conversion_saves()); + return boxed_cast(dispatch::dispatch(t_funcs, params, state), t_conversions); + } } }; @@ -44,9 +50,15 @@ namespace chaiscript struct Function_Caller_Ret { static Ret call(const std::vector &t_funcs, - const std::vector ¶ms, const Type_Conversions *t_conversions) + const std::vector ¶ms, const Type_Conversions_State *t_conversions) { - return Boxed_Number(dispatch::dispatch(t_funcs, params, t_conversions?*t_conversions:Type_Conversions())).get_as(); + if (t_conversions) { + return Boxed_Number(dispatch::dispatch(t_funcs, params, *t_conversions)).get_as(); + } else { + Type_Conversions conv; + Type_Conversions_State state(conv, conv.conversion_saves()); + return Boxed_Number(dispatch::dispatch(t_funcs, params, state)).get_as(); + } } }; @@ -58,9 +70,15 @@ namespace chaiscript struct Function_Caller_Ret { static void call(const std::vector &t_funcs, - const std::vector ¶ms, const Type_Conversions *t_conversions) + const std::vector ¶ms, const Type_Conversions_State *t_conversions) { - dispatch::dispatch(t_funcs, params, t_conversions?*t_conversions:Type_Conversions()); + if (t_conversions) { + dispatch::dispatch(t_funcs, params, *t_conversions); + } else { + Type_Conversions conv; + Type_Conversions_State state(conv, conv.conversion_saves()); + dispatch::dispatch(t_funcs, params, state); + } } }; @@ -79,11 +97,18 @@ namespace chaiscript template Ret operator()(P&& ... param) { - return Function_Caller_Ret::value && !std::is_same::value>::call(m_funcs, { - box

(std::forward

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

(std::forward

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

(std::forward

(param))... + }, nullptr + ); + } } @@ -114,7 +139,7 @@ namespace chaiscript /// \todo what happens if t_conversions is deleted out from under us?! template - std::function build_function_caller_helper(Ret (Params...), const std::vector &funcs, const Type_Conversions *t_conversions) + std::function build_function_caller_helper(Ret (Params...), const std::vector &funcs, const Type_Conversions_State *t_conversions) { /* if (funcs.size() == 1) @@ -132,7 +157,7 @@ namespace chaiscript } */ - return std::function(Build_Function_Caller_Helper(funcs, t_conversions)); + return std::function(Build_Function_Caller_Helper(funcs, t_conversions?t_conversions->get():nullptr)); } } } diff --git a/include/chaiscript/dispatchkit/handle_return.hpp b/include/chaiscript/dispatchkit/handle_return.hpp index e3a8254..2650e3b 100644 --- a/include/chaiscript/dispatchkit/handle_return.hpp +++ b/include/chaiscript/dispatchkit/handle_return.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_HANDLE_RETURN_HPP_ diff --git a/include/chaiscript/dispatchkit/operators.hpp b/include/chaiscript/dispatchkit/operators.hpp index a04cba4..2d4401e 100644 --- a/include/chaiscript/dispatchkit/operators.hpp +++ b/include/chaiscript/dispatchkit/operators.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_OPERATORS_HPP_ diff --git a/include/chaiscript/dispatchkit/proxy_constructors.hpp b/include/chaiscript/dispatchkit/proxy_constructors.hpp index 063c557..a5ad41e 100644 --- a/include/chaiscript/dispatchkit/proxy_constructors.hpp +++ b/include/chaiscript/dispatchkit/proxy_constructors.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index 1eee060..6128b33 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com @@ -43,7 +43,7 @@ namespace chaiscript namespace dispatch { template - std::function functor(std::shared_ptr func, const Type_Conversions *t_conversions); + std::function functor(std::shared_ptr func, const Type_Conversions_State *t_conversions); class Param_Types { @@ -72,7 +72,7 @@ namespace chaiscript return m_types == t_rhs.m_types; } - bool match(const std::vector &vals, const Type_Conversions &t_conversions) const + bool match(const std::vector &vals, const Type_Conversions_State &t_conversions) const { if (!m_has_types) return true; if (vals.size() != m_types.size()) return false; @@ -147,7 +147,7 @@ namespace chaiscript public: virtual ~Proxy_Function_Base() {} - Boxed_Value operator()(const std::vector ¶ms, const chaiscript::Type_Conversions &t_conversions) const + Boxed_Value operator()(const std::vector ¶ms, const chaiscript::Type_Conversions_State &t_conversions) const { if (m_arity < 0 || size_t(m_arity) == params.size()) { return do_call(params, t_conversions); @@ -163,7 +163,7 @@ namespace chaiscript const std::vector &get_param_types() const { return m_types; } virtual bool operator==(const Proxy_Function_Base &) const = 0; - virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const = 0; + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const = 0; virtual bool is_attribute_function() const { return false; } @@ -179,7 +179,7 @@ namespace chaiscript //! Return true if the function is a possible match //! to the passed in values - bool filter(const std::vector &vals, const Type_Conversions &t_conversions) const + bool filter(const std::vector &vals, const Type_Conversions_State &t_conversions) const { if (m_arity < 0) { @@ -206,15 +206,15 @@ namespace chaiscript virtual std::string annotation() const = 0; - static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv, const Type_Conversions &t_conversions) + static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv, const Type_Conversions_State &t_conversions) { if (ti.is_undef() || ti.bare_equal(user_type()) || (!bv.get_type_info().is_undef() - && (ti.bare_equal(user_type()) + && ( (ti.bare_equal(user_type()) && bv.get_type_info().is_arithmetic()) || ti.bare_equal(bv.get_type_info()) || bv.get_type_info().bare_equal(user_type >()) - || t_conversions.converts(ti, bv.get_type_info()) + || t_conversions->converts(ti, bv.get_type_info()) ) ) ) @@ -225,13 +225,13 @@ namespace chaiscript } } - virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions &t_conversions) const + virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const { return compare_type_to_param(m_types[1], bv, t_conversions); } protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const = 0; + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const = 0; Proxy_Function_Base(std::vector t_types, int t_arity) : m_types(std::move(t_types)), m_arity(t_arity), m_has_arithmetic_param(false) @@ -248,7 +248,7 @@ namespace chaiscript } - static bool compare_types(const std::vector &tis, const std::vector &bvs) + static bool compare_types(const std::vector &tis, const std::vector &bvs, const Type_Conversions_State &t_conversions) { if (tis.size() - 1 != bvs.size()) { @@ -257,10 +257,7 @@ namespace chaiscript const size_t size = bvs.size(); for (size_t i = 0; i < size; ++i) { - if (!(tis[i+1].bare_equal(bvs[i].get_type_info()) && tis[i+1].is_const() >= bvs[i].get_type_info().is_const() )) - { - return false; - } + if (!compare_type_to_param(tis[i + 1], bvs[i], t_conversions)) { return false; } } } return true; @@ -330,7 +327,7 @@ namespace chaiscript && this->m_param_types == prhs->m_param_types); } - virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { return (m_arity < 0 || (vals.size() == size_t(m_arity) && m_param_types.match(vals, t_conversions))) && test_guard(vals, t_conversions); @@ -354,7 +351,7 @@ namespace chaiscript protected: - bool test_guard(const std::vector ¶ms, const Type_Conversions &t_conversions) const + bool test_guard(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const { if (m_guard) { @@ -422,7 +419,7 @@ namespace chaiscript protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { if (call_match(params, t_conversions) && test_guard(params, t_conversions)) { @@ -472,7 +469,7 @@ namespace chaiscript virtual ~Bound_Function() {} - virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { return m_f->call_match(build_param_list(vals), t_conversions); } @@ -551,7 +548,7 @@ namespace chaiscript return retval; } - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { return (*m_f)(build_param_list(params), t_conversions); } @@ -576,12 +573,12 @@ namespace chaiscript return ""; } - virtual bool call_match(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { - return static_cast(vals.size()) == get_arity() && (compare_types(m_types, vals) || compare_types_with_cast(vals, t_conversions)); + return static_cast(vals.size()) == get_arity() && (compare_types(m_types, vals, t_conversions) && compare_types_with_cast(vals, t_conversions)); } - virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions &t_conversions) const = 0; + virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const = 0; }; @@ -599,7 +596,7 @@ namespace chaiscript virtual ~Proxy_Function_Callable_Impl() {} - virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { return detail::compare_types_cast(static_cast(nullptr), vals, t_conversions); } @@ -611,7 +608,7 @@ namespace chaiscript protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { typedef typename detail::Function_Signature::Return_Type Return_Type; return detail::Do_Call::template go(m_f, params, t_conversions); @@ -651,7 +648,7 @@ namespace chaiscript virtual ~Assignable_Proxy_Function_Impl() {} - virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { return detail::compare_types_cast(static_cast(nullptr), vals, t_conversions); } @@ -671,7 +668,7 @@ namespace chaiscript } protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { return detail::Do_Call::result_type>::template go(m_f.get(), params, t_conversions); } @@ -708,7 +705,7 @@ namespace chaiscript } } - virtual bool call_match(const std::vector &vals, const Type_Conversions &) const CHAISCRIPT_OVERRIDE + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &) const CHAISCRIPT_OVERRIDE { if (vals.size() != 1) { @@ -724,7 +721,7 @@ namespace chaiscript } protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE { const Boxed_Value &bv = params[0]; if (bv.is_const()) @@ -763,6 +760,14 @@ namespace chaiscript { } + dispatch_error(std::vector t_parameters, + std::vector t_functions, + const std::string &t_desc) + : std::runtime_error(t_desc), parameters(std::move(t_parameters)), functions(std::move(t_functions)) + { + } + + dispatch_error(const dispatch_error &) = default; virtual ~dispatch_error() CHAISCRIPT_NOEXCEPT {} @@ -777,7 +782,7 @@ namespace chaiscript { template bool types_match_except_for_arithmetic(const FuncType &t_func, const std::vector &plist, - const Type_Conversions &t_conversions) + const Type_Conversions_State &t_conversions) { const std::vector &types = t_func->get_param_types(); @@ -796,7 +801,7 @@ namespace chaiscript template Boxed_Value dispatch_with_conversions(InItr begin, const InItr &end, const std::vector &plist, - const Type_Conversions &t_conversions, const Funcs &t_funcs) + const Type_Conversions_State &t_conversions, const Funcs &t_funcs) { InItr matching_func(end); @@ -873,7 +878,7 @@ namespace chaiscript */ template Boxed_Value dispatch(const Funcs &funcs, - const std::vector &plist, const Type_Conversions &t_conversions) + const std::vector &plist, const Type_Conversions_State &t_conversions) { std::vector> ordered_funcs; ordered_funcs.reserve(funcs.size()); diff --git a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp index 130d3c1..452a999 100644 --- a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_ @@ -19,7 +19,7 @@ #include "callable_traits.hpp" namespace chaiscript { -class Type_Conversions; +class Type_Conversions_State; namespace exception { class bad_boxed_cast; } // namespace exception @@ -77,7 +77,7 @@ namespace chaiscript template struct Try_Cast { - static void do_try(const std::vector ¶ms, size_t generation, const Type_Conversions &t_conversions) + static void do_try(const std::vector ¶ms, size_t generation, const Type_Conversions_State &t_conversions) { boxed_cast(params[generation], &t_conversions); Try_Cast::do_try(params, generation+1, t_conversions); @@ -88,7 +88,7 @@ namespace chaiscript template<> struct Try_Cast<> { - static void do_try(const std::vector &, size_t, const Type_Conversions &) + static void do_try(const std::vector &, size_t, const Type_Conversions_State &) { } }; @@ -101,7 +101,7 @@ namespace chaiscript */ template bool compare_types_cast(Ret (*)(Params...), - const std::vector ¶ms, const Type_Conversions &t_conversions) + const std::vector ¶ms, const Type_Conversions_State &t_conversions) { try { Try_Cast::do_try(params, 0, t_conversions); @@ -118,7 +118,7 @@ namespace chaiscript template static Ret do_call(const Callable &f, - const std::vector ¶ms, const Type_Conversions &t_conversions, InnerParams &&... innerparams) + const std::vector ¶ms, const Type_Conversions_State &t_conversions, InnerParams &&... innerparams) { return Call_Func::do_call(f, params, t_conversions, std::forward(innerparams)..., params[sizeof...(Params) - count]); } @@ -133,7 +133,7 @@ namespace chaiscript #endif template static Ret do_call(const Callable &f, - const std::vector &, const Type_Conversions &t_conversions, InnerParams &&... innerparams) + const std::vector &, const Type_Conversions_State &t_conversions, InnerParams &&... innerparams) { return f(boxed_cast(std::forward(innerparams), &t_conversions)...); } @@ -150,7 +150,7 @@ namespace chaiscript */ template Ret call_func(const chaiscript::dispatch::detail::Function_Signature &, const Callable &f, - const std::vector ¶ms, const Type_Conversions &t_conversions) + const std::vector ¶ms, const Type_Conversions_State &t_conversions) { if (params.size() == sizeof...(Params)) { @@ -190,7 +190,7 @@ namespace chaiscript */ template bool compare_types_cast(Indexes, Ret (*)(Params...), - const std::vector ¶ms, const Type_Conversions &t_conversions) + const std::vector ¶ms, const Type_Conversions_State &t_conversions) { try { (void)params; (void)t_conversions; @@ -204,7 +204,7 @@ namespace chaiscript template bool compare_types_cast(Ret (*f)(Params...), - const std::vector ¶ms, const Type_Conversions &t_conversions) + const std::vector ¶ms, const Type_Conversions_State &t_conversions) { typedef typename Make_Indexes::indexes indexes; return compare_types_cast(indexes(), f, params, t_conversions); @@ -213,7 +213,7 @@ namespace chaiscript template Ret call_func(const chaiscript::dispatch::detail::Function_Signature &, Indexes, const Callable &f, - const std::vector ¶ms, const Type_Conversions &t_conversions) + const std::vector ¶ms, const Type_Conversions_State &t_conversions) { (void)params; (void)t_conversions; return f(boxed_cast(params[I], &t_conversions)...); @@ -228,7 +228,7 @@ namespace chaiscript */ template Ret call_func(const chaiscript::dispatch::detail::Function_Signature &sig, const Callable &f, - const std::vector ¶ms, const Type_Conversions &t_conversions) + const std::vector ¶ms, const Type_Conversions_State &t_conversions) { typedef typename Make_Indexes::indexes indexes; return call_func(sig, indexes(), f, params, t_conversions); @@ -252,7 +252,7 @@ namespace chaiscript struct Do_Call { template - static Boxed_Value go(const Callable &fun, const std::vector ¶ms, const Type_Conversions &t_conversions) + 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)); } @@ -262,7 +262,7 @@ namespace chaiscript struct Do_Call { template - static Boxed_Value go(const Callable &fun, const std::vector ¶ms, const Type_Conversions &t_conversions) + 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(); diff --git a/include/chaiscript/dispatchkit/register_function.hpp b/include/chaiscript/dispatchkit/register_function.hpp index 598dd8b..8334b7b 100644 --- a/include/chaiscript/dispatchkit/register_function.hpp +++ b/include/chaiscript/dispatchkit/register_function.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_REGISTER_FUNCTION_HPP_ diff --git a/include/chaiscript/dispatchkit/type_conversions.hpp b/include/chaiscript/dispatchkit/type_conversions.hpp index 602d4d9..3a4d239 100644 --- a/include/chaiscript/dispatchkit/type_conversions.hpp +++ b/include/chaiscript/dispatchkit/type_conversions.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_ @@ -310,12 +310,21 @@ namespace chaiscript private: Callable m_func; }; - } class Type_Conversions { public: + struct Conversion_Saves + { + Conversion_Saves() + : enabled(false) + {} + + bool enabled; + std::vector saves; + }; + struct Less_Than { bool operator()(const std::type_info *t_lhs, const std::type_info *t_rhs) const @@ -387,15 +396,14 @@ namespace chaiscript } else { return false; } - } template - Boxed_Value boxed_type_conversion(const Boxed_Value &from) const + Boxed_Value boxed_type_conversion(Conversion_Saves &t_saves, const Boxed_Value &from) const { try { Boxed_Value ret = get_conversion(user_type(), from.get_type_info())->convert(from); - if (m_conversion_saves->enabled) m_conversion_saves->saves.push_back(ret); + if (t_saves.enabled) t_saves.saves.push_back(ret); return ret; } catch (const std::out_of_range &) { throw exception::bad_boxed_dynamic_cast(from.get_type_info(), typeid(To), "No known conversion"); @@ -405,11 +413,11 @@ namespace chaiscript } template - Boxed_Value boxed_type_down_conversion(const Boxed_Value &to) const + Boxed_Value boxed_type_down_conversion(Conversion_Saves &t_saves, const Boxed_Value &to) const { try { Boxed_Value ret = get_conversion(to.get_type_info(), user_type())->convert_down(to); - if (m_conversion_saves->enabled) m_conversion_saves->saves.push_back(ret); + if (t_saves.enabled) t_saves.saves.push_back(ret); return ret; } catch (const std::out_of_range &) { throw exception::bad_boxed_dynamic_cast(to.get_type_info(), typeid(From), "No known conversion"); @@ -418,15 +426,15 @@ namespace chaiscript } } - void enable_conversion_saves(bool t_val) + static void enable_conversion_saves(Conversion_Saves &t_saves, bool t_val) { - m_conversion_saves->enabled = t_val; + t_saves.enabled = t_val; } - std::vector take_saves() + std::vector take_saves(Conversion_Saves &t_saves) { std::vector ret; - std::swap(ret, m_conversion_saves->saves); + std::swap(ret, t_saves.saves); return ret; } @@ -450,6 +458,10 @@ namespace chaiscript } } + Conversion_Saves &conversion_saves() const { + return *m_conversion_saves; + } + private: std::set >::const_iterator find_bidir( const Type_Info &to, const Type_Info &from) const @@ -459,7 +471,6 @@ namespace chaiscript { return (conversion->to().bare_equal(to) && conversion->from().bare_equal(from)) || (conversion->bidir() && conversion->from().bare_equal(to) && conversion->to().bare_equal(from)); -; } ); } @@ -483,15 +494,6 @@ namespace chaiscript } - struct Conversion_Saves - { - Conversion_Saves() - : enabled(false) - {} - - bool enabled; - std::vector saves; - }; mutable chaiscript::detail::threading::shared_mutex m_mutex; std::set> m_conversions; @@ -501,6 +503,33 @@ namespace chaiscript mutable chaiscript::detail::threading::Thread_Storage m_conversion_saves; }; + class Type_Conversions_State + { + public: + Type_Conversions_State(const Type_Conversions &t_conversions, + Type_Conversions::Conversion_Saves &t_saves) + : m_conversions(t_conversions), + m_saves(t_saves) + { + } + + const Type_Conversions *operator->() const { + return &m_conversions.get(); + } + + const Type_Conversions *get() const { + return &m_conversions.get(); + } + + Type_Conversions::Conversion_Saves &saves() const { + return m_saves; + } + + private: + std::reference_wrapper m_conversions; + std::reference_wrapper m_saves; + }; + typedef std::shared_ptr Type_Conversion; /// \brief Used to register a to / parent class relationship with ChaiScript. Necessary if you @@ -582,7 +611,7 @@ namespace chaiscript const std::vector &from_vec = detail::Cast_Helper &>::cast(t_bv, nullptr); To vec; - + vec.reserve(from_vec.size()); for (const Boxed_Value &bv : from_vec) { vec.push_back(detail::Cast_Helper::cast(bv, nullptr)); } @@ -593,6 +622,22 @@ namespace chaiscript return chaiscript::make_shared>(user_type>(), user_type(), func); } + template + Type_Conversion map_conversion() + { + auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { + const std::map &from_map = detail::Cast_Helper &>::cast(t_bv, nullptr); + + To map; + for (const std::pair &p : from_map) { + map.insert(std::make_pair(p.first, detail::Cast_Helper::cast(p.second, nullptr))); + } + + return Boxed_Value(std::move(map)); + }; + + return chaiscript::make_shared>(user_type>(), user_type(), func); + } } diff --git a/include/chaiscript/dispatchkit/type_info.hpp b/include/chaiscript/dispatchkit/type_info.hpp index 0d5c5f3..1426085 100644 --- a/include/chaiscript/dispatchkit/type_info.hpp +++ b/include/chaiscript/dispatchkit/type_info.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_TYPE_INFO_HPP_ @@ -32,17 +32,17 @@ namespace chaiscript CHAISCRIPT_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) : m_type_info(t_ti), m_bare_type_info(t_bare_ti), - m_is_const(t_is_const), m_is_reference(t_is_reference), m_is_pointer(t_is_pointer), - m_is_void(t_is_void), m_is_arithmetic(t_is_arithmetic), - m_is_undef(false) + m_flags((t_is_const << is_const_flag) + + (t_is_reference << is_reference_flag) + + (t_is_pointer << is_pointer_flag) + + (t_is_void << is_void_flag) + + (t_is_arithmetic << is_arithmetic_flag)) { } CHAISCRIPT_CONSTEXPR Type_Info() : m_type_info(nullptr), m_bare_type_info(nullptr), - m_is_const(false), m_is_reference(false), m_is_pointer(false), - m_is_void(false), m_is_arithmetic(false), - m_is_undef(true) + m_flags(1 << is_undef_flag) { } @@ -83,12 +83,12 @@ namespace chaiscript && (*m_bare_type_info) == ti; } - CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return m_is_const; } - CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return m_is_reference; } - CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return m_is_void; } - CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return m_is_arithmetic; } - CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return m_is_undef; } - CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return m_is_pointer; } + CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_const_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_reference_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_void_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_arithmetic_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_undef_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_pointer_flag)) != 0; } std::string name() const { @@ -118,12 +118,13 @@ namespace chaiscript private: const std::type_info *m_type_info; const std::type_info *m_bare_type_info; - bool m_is_const; - bool m_is_reference; - bool m_is_pointer; - bool m_is_void; - bool m_is_arithmetic; - bool m_is_undef; + unsigned int m_flags; + static const int is_const_flag = 0; + static const int is_reference_flag = 1; + static const int is_pointer_flag = 2; + static const int is_void_flag = 3; + static const int is_arithmetic_flag = 4; + static const int is_undef_flag = 5; }; namespace detail diff --git a/include/chaiscript/language/chaiscript_algebraic.hpp b/include/chaiscript/language/chaiscript_algebraic.hpp index 151a94b..52441e8 100644 --- a/include/chaiscript/language/chaiscript_algebraic.hpp +++ b/include/chaiscript/language/chaiscript_algebraic.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_ALGEBRAIC_HPP_ diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 3a46c96..b44a5a3 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_COMMON_HPP_ @@ -34,7 +34,7 @@ namespace chaiscript /// 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, Inplace_Fun_Call, Arg_List, Variable, Equation, Var_Decl, + 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, @@ -47,7 +47,7 @@ namespace chaiscript /// 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", "Inplace_Fun_Call", "Arg_List", "Variable", "Equation", "Var_Decl", + 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", @@ -582,12 +582,12 @@ namespace chaiscript 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()->new_function_call(m_ds.get().stack_holder(), m_ds.get().conversion_saves()); } ~Function_Push_Pop() { - m_ds.get()->pop_function_call(m_ds.get().stack_holder()); + m_ds.get()->pop_function_call(m_ds.get().stack_holder(), m_ds.get().conversion_saves()); } void save_params(const std::vector &t_params) diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index a6a604a..f3aa069 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_ENGINE_HPP_ @@ -36,7 +36,9 @@ #else #ifdef CHAISCRIPT_WINDOWS #define VC_EXTRA_LEAN +#if !defined(WIN32_LEAN_AND_MEAN) #define WIN32_LEAN_AND_MEAN +#endif #include #endif #endif @@ -175,11 +177,11 @@ namespace chaiscript FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, + nullptr, t_err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&lpMsgBuf), - 0, NULL ) != 0 && lpMsgBuf) + 0, nullptr ) != 0 && lpMsgBuf) { retval = lpMsgBuf; LocalFree(lpMsgBuf); @@ -284,14 +286,7 @@ namespace chaiscript - const Boxed_Value internal_eval_ast(const AST_NodePtr &t_ast) - { - try { - return t_ast->eval(m_engine); - } catch (const exception::eval_error &t_ee) { - throw Boxed_Value(t_ee); - } - } + /// Evaluates the given file and looks in the 'use' paths const Boxed_Value internal_eval_file(const std::string &t_filename) { @@ -347,6 +342,7 @@ namespace chaiscript 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("_"); @@ -372,11 +368,15 @@ namespace chaiscript // m_engine.add(fun &)>(std::bind(&chaiscript::dispatch::Proxy_Function_Base::operator(), std::placeholders::_1, std::placeholders::_2, std::ref(m_engine.conversions()))), "call"); // +// + m_engine.add(fun( - [=](const dispatch::Proxy_Function_Base &t_fun, const std::vector &t_params) { - return t_fun(t_params, this->m_engine.conversions()); + [=](const dispatch::Proxy_Function_Base &t_fun, const std::vector &t_params) -> Boxed_Value { + Type_Conversions_State s(this->m_engine.conversions(), this->m_engine.conversions().conversion_saves()); + return t_fun(t_params, s); }), "call"); + m_engine.add(fun([this](const Type_Info &t_ti){ return m_engine.get_type_name(t_ti); }), "name"); m_engine.add(fun([this](const std::string &t_type_name, bool t_throw){ return m_engine.get_type(t_type_name, t_throw); }), "type"); @@ -396,16 +396,21 @@ namespace chaiscript 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 internal_eval_ast(t_ast); }), "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 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"); - + m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global"); } @@ -517,6 +522,28 @@ namespace chaiscript build_eval_system(ModulePtr()); } + + const Boxed_Value eval(const AST_NodePtr &t_ast) + { + try { + return t_ast->eval(m_engine); + } catch (const exception::eval_error &t_ee) { + throw Boxed_Value(t_ee); + } + } + + static AST_NodePtr parse(const std::string &t_input) + { + 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"); + } + } + + static int version_major() { return chaiscript::version_major; @@ -534,11 +561,36 @@ namespace chaiscript static std::string version() { - std::stringstream ss; - ss << version_major() << "." << version_minor() << "." << version_patch(); - return ss.str(); + 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); @@ -608,6 +660,12 @@ namespace chaiscript return *this; } + ChaiScript &set_global(const Boxed_Value &t_bv, const std::string &t_name) + { + m_engine.set_global(t_bv, t_name); + return *this; + } + /// \brief Represents the current state of the ChaiScript system. State and be saved and restored /// \warning State object does not contain the user defined type conversions of the engine. They /// are left out due to performance considerations involved in tracking the state diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index c14a156..60474b4 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_EVAL_HPP_ @@ -44,16 +44,33 @@ namespace chaiscript 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=std::map()) { + 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) { chaiscript::detail::Dispatch_State state(t_ss); - chaiscript::eval::detail::Scope_Push_Pop spp(state); - for (const auto &local : t_locals) { - t_ss.add_object(local.first, local.second); + const Boxed_Value *thisobj = [&]() -> const Boxed_Value *{ + auto &stack = t_ss.get_stack_data(state.stack_holder()).back(); + if (!stack.empty() && stack.back().first == "__this") { + return &stack.back().second; + } else if (!t_vals.empty()) { + return &t_vals[0]; + } else { + return nullptr; + } + }(); + + chaiscript::eval::detail::Stack_Push_Pop tpp(state); + if (thisobj) state.add_object("this", *thisobj); + + if (t_locals) { + for (const auto &local : *t_locals) { + state.add_object(local.first, local.second); + } } for (size_t i = 0; i < t_param_names.size(); ++i) { - t_ss.add_object(t_param_names[i], t_vals[i]); + if (t_param_names[i] != "this") { + state.add_object(t_param_names[i], t_vals[i]); + } } try { @@ -73,9 +90,9 @@ namespace chaiscript virtual ~Binary_Operator_AST_Node() {} virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { - return do_oper(t_ss, m_oper, text, - this->children[0]->eval(t_ss), - this->children[1]->eval(t_ss)); + 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); } virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE @@ -101,8 +118,7 @@ namespace chaiscript } else { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); fpp.save_params({t_lhs, t_rhs}); - chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); - return t_ss->call_function(t_oper_string, t_lhs, t_rhs); + return t_ss->call_function(t_oper_string, m_loc, {t_lhs, t_rhs}, t_ss.conversions()); } } catch(const exception::dispatch_error &e){ @@ -112,13 +128,14 @@ namespace chaiscript private: Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc; }; struct Int_AST_Node : public AST_Node { public: 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)) { } + m_value(std::move(t_bv)) { assert(text != ""); } virtual ~Int_AST_Node() {} virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const CHAISCRIPT_OVERRIDE{ return m_value; @@ -236,8 +253,7 @@ namespace chaiscript Boxed_Value fn(this->children[0]->eval(t_ss)); try { - chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); - 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); @@ -286,73 +302,6 @@ namespace chaiscript - /// Used in the context of in-string ${} evals, so that no new scope is created - struct Inplace_Fun_Call_AST_Node : public AST_Node { - public: - Inplace_Fun_Call_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::Inplace_Fun_Call, std::move(t_loc), std::move(t_children)) - { assert(children.size() == 2); } - - virtual ~Inplace_Fun_Call_AST_Node() {} - virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ - 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)); - } - - - Const_Proxy_Function fn; - try { - Boxed_Value bv = this->children[0]->eval(t_ss); - try { - fn = t_ss->boxed_cast(bv); - } catch (const exception::bad_boxed_cast &) { - throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function."); - } - return (*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 &){ - // 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, {fn}, false, *t_ss); - } - 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; - } - } - - virtual std::string pretty_print() const CHAISCRIPT_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 : public AST_Node { public: Arg_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : @@ -448,6 +397,8 @@ namespace chaiscript { assert(children.size() == 3); } Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_clone_loc; virtual ~Equation_AST_Node() {} virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { @@ -483,14 +434,14 @@ namespace chaiscript } else { if (!rhs.is_return_value()) { - rhs = t_ss->call_function("clone", rhs); + rhs = t_ss->call_function("clone", m_clone_loc, {rhs}, t_ss.conversions()); } rhs.reset_return_value(); } } try { - return t_ss->call_function(this->children[1]->text, std::move(lhs), rhs); + return t_ss->call_function(this->children[1]->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); @@ -503,13 +454,14 @@ namespace chaiscript else if (this->children[1]->text == ":=") { if (lhs.is_undef() || Boxed_Value::type_match(lhs, rhs)) { lhs.assign(rhs); + lhs.reset_return_value(); } else { throw exception::eval_error("Mismatched types in equation"); } } else { try { - return t_ss->call_function(this->children[1]->text, std::move(lhs), rhs); + return t_ss->call_function(this->children[1]->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); } @@ -560,7 +512,7 @@ namespace chaiscript try { Boxed_Value bv; - t_ss->add_object(idname, bv); + t_ss.add_object(idname, bv); return bv; } catch (const exception::reserved_word_error &) { @@ -591,9 +543,8 @@ namespace chaiscript std::vector params{children[0]->eval(t_ss), children[1]->eval(t_ss)}; try { - chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); fpp.save_params(params); - return t_ss->call_function("[]", params); + return t_ss->call_function("[]", m_loc, params, t_ss.conversions()); } catch(const exception::dispatch_error &e){ throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss ); @@ -615,6 +566,8 @@ namespace chaiscript return oss.str(); } + + mutable std::atomic_uint_fast32_t m_loc; }; struct Dot_Access_AST_Node : public AST_Node { @@ -629,6 +582,7 @@ namespace chaiscript virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + Boxed_Value retval = children[0]->eval(t_ss); std::vector params{retval}; @@ -643,9 +597,7 @@ namespace chaiscript fpp.save_params(params); try { - chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); - t_ss->add_object("this", retval); - retval = t_ss->call_member(m_fun_name, std::move(params), has_function_params); + retval = t_ss->call_member(m_fun_name, m_loc, std::move(params), has_function_params, t_ss.conversions()); } catch(const exception::dispatch_error &e){ if (e.functions.empty()) @@ -661,7 +613,7 @@ namespace chaiscript if (this->children[2]->identifier == AST_Node_Type::Array_Call) { try { - retval = t_ss->call_function("[]", retval, this->children[2]->children[1]->eval(t_ss)); + retval = t_ss->call_function("[]", m_array_loc, {retval, this->children[2]->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); @@ -672,6 +624,8 @@ namespace chaiscript } private: + mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_array_loc; std::string m_fun_name; }; @@ -745,7 +699,7 @@ namespace chaiscript dispatch::make_dynamic_proxy_function( [engine, lambda_node, param_names, captures](const std::vector &t_params) { - return detail::eval_function(engine, lambda_node, param_names, t_params, captures); + return detail::eval_function(engine, lambda_node, param_names, t_params, &captures); }, static_cast(numparams), lambda_node, param_types ) @@ -876,7 +830,7 @@ namespace chaiscript /// \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(children[0]->text)); children[1]->eval(t_ss); @@ -985,7 +939,7 @@ namespace chaiscript if (this->children[currentCase]->identifier == AST_Node_Type::Case) { //This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here. try { - if (hasMatched || boxed_cast(t_ss->call_function("==", match_value, this->children[currentCase]->children[0]->eval(t_ss)))) { + if (hasMatched || boxed_cast(t_ss->call_function("==", m_loc, {match_value, this->children[currentCase]->children[0]->eval(t_ss)}, t_ss.conversions()))) { this->children[currentCase]->eval(t_ss); hasMatched = true; } @@ -996,7 +950,7 @@ namespace chaiscript } else if (this->children[currentCase]->identifier == AST_Node_Type::Default) { this->children[currentCase]->eval(t_ss); - breaking = true; + hasMatched = true; } } catch (detail::Break_Loop &) { @@ -1006,6 +960,8 @@ namespace chaiscript } return Boxed_Value(); } + + mutable std::atomic_uint_fast32_t m_loc; }; struct Case_AST_Node : public AST_Node { @@ -1049,10 +1005,11 @@ namespace chaiscript try { std::vector vec; if (!children.empty()) { + vec.reserve(children[0]->children.size()); for (const auto &child : children[0]->children) { auto obj = child->eval(t_ss); if (!obj.is_return_value()) { - vec.push_back(t_ss->call_function("clone", obj)); + vec.push_back(t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions())); } else { vec.push_back(std::move(obj)); } @@ -1069,6 +1026,8 @@ namespace chaiscript { return "[" + AST_Node::pretty_print() + "]"; } + + mutable std::atomic_uint_fast32_t m_loc; }; struct Inline_Map_AST_Node : public AST_Node { @@ -1083,7 +1042,7 @@ namespace chaiscript for (const auto &child : children[0]->children) { auto obj = child->children[1]->eval(t_ss); if (!obj.is_return_value()) { - obj = t_ss->call_function("clone", obj); + obj = t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions()); } retval[t_ss->boxed_cast(child->children[0]->eval(t_ss))] = std::move(obj); @@ -1096,6 +1055,7 @@ namespace chaiscript } } + mutable std::atomic_uint_fast32_t m_loc; }; struct Return_AST_Node : public AST_Node { @@ -1148,7 +1108,7 @@ namespace chaiscript virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ try { Boxed_Value bv; - t_ss->add_object(this->children[0]->text, bv); + t_ss.add_object(this->children[0]->text, bv); return bv; } catch (const exception::reserved_word_error &) { @@ -1177,9 +1137,8 @@ namespace chaiscript return Boxed_Number::do_oper(m_oper, bv); } else { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); fpp.save_params({bv}); - return t_ss->call_function(children[0]->text, std::move(bv)); + return t_ss->call_function(children[0]->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); @@ -1188,6 +1147,7 @@ namespace chaiscript private: Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc; }; struct Break_AST_Node : public AST_Node { @@ -1248,15 +1208,16 @@ namespace chaiscript virtual ~Inline_Range_AST_Node() {} virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ try { - return t_ss->call_function("generate_range", - children[0]->children[0]->children[0]->eval(t_ss), - children[0]->children[0]->children[1]->eval(t_ss)); + auto oper1 = children[0]->children[0]->children[0]->eval(t_ss); + auto oper2 = 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) { throw exception::eval_error("Unable to generate range vector, while calling 'generate_range'", e.parameters, e.functions, false, *t_ss); } } + mutable std::atomic_uint_fast32_t m_loc; }; struct Annotation_AST_Node : public AST_Node { @@ -1294,9 +1255,9 @@ namespace chaiscript if (dispatch::Param_Types( std::vector>{Arg_List_AST_Node::get_arg_type(catch_block->children[0], t_ss)} - ).match(std::vector{t_except}, t_ss->conversions())) + ).match(std::vector{t_except}, t_ss.conversions())) { - t_ss->add_object(name, t_except); + t_ss.add_object(name, t_except); if (catch_block->children.size() == 2) { //Variable capture, no guards @@ -1433,7 +1394,7 @@ namespace chaiscript if (guardnode) { 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, std::map()); + return chaiscript::eval::detail::eval_function(engine, guardnode, t_param_names, t_params); }, static_cast(numparams), guardnode); } @@ -1450,7 +1411,7 @@ namespace chaiscript 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, std::map()); + return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params); }, static_cast(numparams), node, param_types, l_annotation, guard ) @@ -1466,7 +1427,7 @@ 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, std::map()); + return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params); }, static_cast(numparams), node, param_types, l_annotation, guard), type), function_name); diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index ddc484f..0bfabe5 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_PARSER_HPP_ @@ -17,9 +17,19 @@ #include + #include "../dispatchkit/boxed_value.hpp" #include "chaiscript_common.hpp" + +#if defined(CHAISCRIPT_MSVC) && defined(max) && defined(min) +#pragma push_macro("max") // Why Microsoft? why? This is worse than bad +#undef max +#pragma push_macro("min") +#undef min +#endif + + namespace chaiscript { /// \brief Classes and functions used during the parsing process. @@ -48,8 +58,6 @@ namespace chaiscript class ChaiScript_Parser { - std::string::const_iterator m_input_pos, m_input_end; - int m_line, m_col; std::string m_multiline_comment_begin; std::string m_multiline_comment_end; std::string m_singleline_comment; @@ -60,10 +68,110 @@ namespace chaiscript std::vector> m_operator_matches; std::vector m_operators; + struct Position + { + Position() + : line(-1), col(-1), m_last_col(-1) {} + + 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) + { + } + + static std::string str(const Position &t_begin, const Position &t_end) { + return std::string(t_begin.m_pos, t_end.m_pos); + } + + Position &operator++() { + if (m_pos != m_end) { + if (*m_pos == '\n') { + ++line; + m_last_col = col; + col = 1; + } else { + ++col; + } + + ++m_pos; + } + return *this; + } + + Position &operator--() { + --m_pos; + if (*m_pos == '\n') { + --line; + col = m_last_col; + } else { + --col; + } + return *this; + } + + Position &operator+=(size_t t_distance) { + *this = (*this) + t_distance; + return *this; + } + + Position operator+(size_t t_distance) const { + Position ret(*this); + for (size_t i = 0; i < t_distance; ++i) { + ++ret; + } + return ret; + } + + Position &operator-=(size_t t_distance) { + *this = (*this) - t_distance; + return *this; + } + + Position operator-(size_t t_distance) const { + Position ret(*this); + for (size_t i = 0; i < t_distance; ++i) { + --ret; + } + return ret; + } + + bool operator==(const Position &t_rhs) const { + return m_pos == t_rhs.m_pos; + } + + bool operator!=(const Position &t_rhs) const { + return m_pos != t_rhs.m_pos; + } + + bool has_more() const { + return m_pos != m_end; + } + + size_t remaining() const { + return std::distance(m_pos, m_end); + } + + char operator*() const { + if (m_pos == m_end) { + return '\0'; + } else { + return *m_pos; + } + } + + int line; + int col; + + private: + std::string::const_iterator m_pos; + std::string::const_iterator m_end; + int m_last_col; + }; + + Position m_position; + public: ChaiScript_Parser() - : m_line(-1), m_col(-1), - m_multiline_comment_begin("/*"), + : m_multiline_comment_begin("/*"), m_multiline_comment_end("*/"), m_singleline_comment("//") { @@ -167,17 +275,16 @@ namespace chaiscript } /// test a char in an m_alphabet - bool char_in_alphabet(char c, detail::Alphabet a) const { return m_alphabet[a][static_cast(c)]; } + 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 = "") { - 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 + " "); - } - } - */ + void debug_print(AST_NodePtr t, std::string prepend = "") const { + 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 + " "); + } + } + /// Shows the current stack of matched ast_nodes void show_match_stack() const { @@ -283,16 +390,16 @@ namespace chaiscript m_filename, m_match_stack[t_match_start]->location.start.line, m_match_stack[t_match_start]->location.start.column, - m_line, - m_col + m_position.line, + m_position.col ); } else { return Parse_Location( m_filename, - m_line, - m_col, - m_line, - m_col + m_position.line, + m_position.col, + m_position.line, + m_position.col ); } }(); @@ -313,34 +420,28 @@ namespace chaiscript std::move(new_children))); } - /// Check to see if there is more text parse - inline bool has_more_input() const { - return (m_input_pos != m_input_end); - } /// Skips any multi-line or single-line comment bool SkipComment() { if (Symbol_(m_multiline_comment_begin.c_str())) { - while (m_input_pos != m_input_end) { + while (m_position.has_more()) { if (Symbol_(m_multiline_comment_end.c_str())) { break; } else if (!Eol_()) { - ++m_col; - ++m_input_pos; + ++m_position; } } return true; } else if (Symbol_(m_singleline_comment.c_str())) { - while (m_input_pos != m_input_end) { + while (m_position.has_more()) { if (Symbol_("\r\n")) { - m_input_pos -= 2; + m_position -= 2; break; } else if (Char_('\n')) { - --m_input_pos; + --m_position; break; } else { - ++m_col; - ++m_input_pos; + ++m_position; } } return true; @@ -354,24 +455,19 @@ namespace chaiscript bool SkipWS(bool skip_cr=false) { bool retval = false; - while (has_more_input()) { - auto end_line = (*m_input_pos != 0) && ((*m_input_pos == '\n') || (*m_input_pos == '\r' && *(m_input_pos+1) == '\n')); + while (m_position.has_more()) { + auto end_line = (*m_position != 0) && ((*m_position == '\n') || (*m_position == '\r' && *(m_position+1) == '\n')); - if ( char_in_alphabet(*m_input_pos,detail::white_alphabet) || (skip_cr && end_line)) { + if ( char_in_alphabet(*m_position,detail::white_alphabet) || (skip_cr && end_line)) { if(end_line) { - m_col = 1; - ++m_line; - - if(*(m_input_pos) == '\r') { + if(*m_position == '\r') { // discards lf - ++m_input_pos; + ++m_position; } } - else { - ++m_col; - } - ++m_input_pos; + + ++m_position; retval = true; } @@ -387,29 +483,25 @@ namespace chaiscript /// Reads the optional exponent (scientific notation) and suffix for a Float bool read_exponent_and_suffix() { // Support a form of scientific notation: 1e-5, 35.5E+8, 0.01e19 - if (has_more_input() && (std::tolower(*m_input_pos) == 'e')) { - ++m_input_pos; - ++m_col; - if (has_more_input() && ((*m_input_pos == '-') || (*m_input_pos == '+'))) { - ++m_input_pos; - ++m_col; + if (m_position.has_more() && (std::tolower(*m_position) == 'e')) { + ++m_position; + if (m_position.has_more() && ((*m_position == '-') || (*m_position == '+'))) { + ++m_position; } - auto exponent_pos = m_input_pos; - while (has_more_input() && char_in_alphabet(*m_input_pos,detail::int_alphabet) ) { - ++m_input_pos; - ++m_col; + auto exponent_pos = m_position; + while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) { + ++m_position; } - if (m_input_pos == exponent_pos) { + if (m_position == exponent_pos) { // Require at least one digit after the exponent return false; } } // Parse optional float suffix - while (has_more_input() && char_in_alphabet(*m_input_pos, detail::float_suffix_alphabet)) + while (m_position.has_more() && char_in_alphabet(*m_position, detail::float_suffix_alphabet)) { - ++m_input_pos; - ++m_col; + ++m_position; } return true; @@ -418,30 +510,26 @@ namespace chaiscript /// Reads a floating point value from input, without skipping initial whitespace bool Float_() { - if (has_more_input() && char_in_alphabet(*m_input_pos,detail::float_alphabet) ) { - while (has_more_input() && char_in_alphabet(*m_input_pos,detail::int_alphabet) ) { - ++m_input_pos; - ++m_col; + if (m_position.has_more() && char_in_alphabet(*m_position,detail::float_alphabet) ) { + while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) { + ++m_position; } - if (has_more_input() && (std::tolower(*m_input_pos) == 'e')) { + if (m_position.has_more() && (std::tolower(*m_position) == 'e')) { // The exponent is valid even without any decimal in the Float (1e8, 3e-15) return read_exponent_and_suffix(); } - else if (has_more_input() && (*m_input_pos == '.')) { - ++m_input_pos; - ++m_col; - if (has_more_input() && char_in_alphabet(*m_input_pos,detail::int_alphabet)) { - while (has_more_input() && char_in_alphabet(*m_input_pos,detail::int_alphabet) ) { - ++m_input_pos; - ++m_col; + else if (m_position.has_more() && (*m_position == '.')) { + ++m_position; + if (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet)) { + while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) { + ++m_position; } // After any decimal digits, support an optional exponent (3.7e3) return read_exponent_and_suffix(); } else { - --m_input_pos; - --m_col; + --m_position; } } } @@ -450,34 +538,28 @@ namespace chaiscript /// Reads a hex value from input, without skipping initial whitespace bool Hex_() { - if (has_more_input() && (*m_input_pos == '0')) { - ++m_input_pos; - ++m_col; + if (m_position.has_more() && (*m_position == '0')) { + ++m_position; - if (has_more_input() && char_in_alphabet(*m_input_pos, detail::x_alphabet) ) { - ++m_input_pos; - ++m_col; - if (has_more_input() && char_in_alphabet(*m_input_pos, detail::hex_alphabet)) { - while (has_more_input() && char_in_alphabet(*m_input_pos, detail::hex_alphabet) ) { - ++m_input_pos; - ++m_col; + if (m_position.has_more() && char_in_alphabet(*m_position, detail::x_alphabet) ) { + ++m_position; + if (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet)) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet) ) { + ++m_position; } - while (has_more_input() && char_in_alphabet(*m_input_pos, detail::int_suffix_alphabet)) + while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet)) { - ++m_input_pos; - ++m_col; + ++m_position; } return true; } else { - --m_input_pos; - --m_col; + --m_position; } } else { - --m_input_pos; - --m_col; + --m_position; } } @@ -486,35 +568,29 @@ namespace chaiscript /// Reads an integer suffix void IntSuffix_() { - while (has_more_input() && char_in_alphabet(*m_input_pos, detail::int_suffix_alphabet)) + while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet)) { - ++m_input_pos; - ++m_col; + ++m_position; } } /// Reads a binary value from input, without skipping initial whitespace bool Binary_() { - if (has_more_input() && (*m_input_pos == '0')) { - ++m_input_pos; - ++m_col; + if (m_position.has_more() && (*m_position == '0')) { + ++m_position; - if (has_more_input() && char_in_alphabet(*m_input_pos, detail::b_alphabet) ) { - ++m_input_pos; - ++m_col; - if (has_more_input() && char_in_alphabet(*m_input_pos, detail::bin_alphabet) ) { - while (has_more_input() && char_in_alphabet(*m_input_pos, detail::bin_alphabet) ) { - ++m_input_pos; - ++m_col; + if (m_position.has_more() && char_in_alphabet(*m_position, detail::b_alphabet) ) { + ++m_position; + if (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet) ) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet) ) { + ++m_position; } return true; } else { - --m_input_pos; - --m_col; + --m_position; } } else { - --m_input_pos; - --m_col; + --m_position; } } @@ -555,8 +631,7 @@ namespace chaiscript - template - static Boxed_Value buildInt(const IntType &t_type, const std::string &t_val) + static Boxed_Value buildInt(const int base, const std::string &t_val, const bool prefixed) { bool unsigned_ = false; bool long_ = false; @@ -583,102 +658,62 @@ namespace chaiscript } } - std::stringstream ss(t_val.substr(0, i)); - ss >> t_type; + const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val; - std::stringstream testu(t_val.substr(0, i)); - uint64_t u; - testu >> t_type >> u; +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-compare" - bool unsignedrequired = false; +#ifdef CHAISCRIPT_CLANG +#pragma GCC diagnostic ignored "-Wtautological-compare" +#endif - if ((u >> (sizeof(int) * 8)) > 0) - { - //requires something bigger than int - long_ = true; - } +#endif - static_assert(sizeof(long) == sizeof(uint64_t) || sizeof(long) * 2 == sizeof(uint64_t), "Unexpected sizing of integer types"); - - if ((sizeof(long) < sizeof(uint64_t)) - && (u >> ((sizeof(uint64_t) - sizeof(long)) * 8)) > 0) - { - //requires something bigger than long - longlong_ = true; - } + try { + auto u = std::stoll(val,nullptr,base); - const size_t size = [&]()->size_t{ - if (longlong_) - { - return sizeof(int64_t) * 8; - } else if (long_) { - return sizeof(long) * 8; + if (!unsigned_ && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if ((unsigned_ || base != 10) && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if (!unsigned_ && !longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if ((unsigned_ || base != 10) && !longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if (!unsigned_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); } else { - return sizeof(int) * 8; + return const_var(static_cast(u)); } - }(); - if ( (u >> (size - 1)) > 0) - { - unsignedrequired = true; - } + } catch (const std::out_of_range &) { + // too big to be signed + try { + auto u = std::stoull(val,nullptr,base); - if (unsignedrequired && !unsigned_) - { - if (t_type == &std::hex || t_type == &std::oct) - { - // with hex and octal we are happy to just make it unsigned - unsigned_ = true; - } else { - // with decimal we must bump it up to the next size - if (long_) - { - longlong_ = true; - } else if (!long_ && !longlong_) { - long_ = true; + if (u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else { + return const_var(static_cast(u)); } + } catch (const std::out_of_range &) { + // it's just simply too big + return const_var(std::numeric_limits::max()); } } - if (unsigned_) - { - if (longlong_) - { - uint64_t val; - ss >> val; - return const_var(val); - } else if (long_) { - unsigned long val; - ss >> val; - return const_var(val); - } else { - unsigned int val; - ss >> val; - return const_var(val); - } - } else { - if (longlong_) - { - int64_t val; - ss >> val; - return const_var(val); - } else if (long_) { - long val; - ss >> val; - return const_var(val); - } else { - int val; - ss >> val; - return const_var(val); - } - } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } template 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_line, m_col), std::forward(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)...); } /// Reads a number from the input, detecting if it's an integer or floating point @@ -688,61 +723,46 @@ namespace chaiscript if (!t_capture) { return Hex_() || Float_(); } else { - const auto start = m_input_pos; - const auto prev_col = m_col; - const auto prev_line = m_line; - if (has_more_input() && char_in_alphabet(*m_input_pos, detail::float_alphabet) ) { - if (Hex_()) { - std::string match(start, m_input_pos); - auto bv = buildInt(std::hex, match); - m_match_stack.emplace_back(make_node(std::move(match), prev_line, prev_col, std::move(bv))); - return true; - } - - if (Binary_()) { - std::string match(start, m_input_pos); - int64_t temp_int = 0; - size_t pos = 0; - const auto end = match.length(); - - while ((pos < end) && (pos < (2 + sizeof(int) * 8))) { - temp_int <<= 1; - if (match[pos] == '1') { - temp_int += 1; - } - ++pos; + const auto start = m_position; + if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet) ) { + try { + 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))); + return true; } - Boxed_Value i = [&]()->Boxed_Value{ - if (match.length() <= sizeof(int) * 8) - { - return const_var(static_cast(temp_int)); - } else { - return const_var(temp_int); - } - }(); - - m_match_stack.push_back(make_node(std::move(match), prev_line, prev_col, std::move(i))); - return true; - } - if (Float_()) { - std::string match(start, m_input_pos); - auto bv = buildFloat(match); - m_match_stack.push_back(make_node(std::move(match), prev_line, prev_col, std::move(bv))); - return true; - } - else { - IntSuffix_(); - std::string match(start, m_input_pos); - if (!match.empty() && (match[0] == '0')) { - auto bv = buildInt(std::oct, match); - m_match_stack.push_back(make_node(std::move(match), prev_line, prev_col, std::move(bv))); + 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))); + 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))); + return true; } else { - auto bv = buildInt(std::dec, match); - m_match_stack.push_back(make_node(std::move(match), prev_line, prev_col, std::move(bv))); + IntSuffix_(); + 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))); + } + 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))); + } else { + return false; + } + return true; } - return true; + } catch (const std::invalid_argument &) { + // error parsing number passed in to buildFloat/buildInt + return false; } } else { @@ -753,37 +773,33 @@ namespace chaiscript /// Reads an identifier from input which conforms to C's identifier naming conventions, without skipping initial whitespace bool Id_() { - if (has_more_input() && char_in_alphabet(*m_input_pos, detail::id_alphabet)) { - while (has_more_input() && char_in_alphabet(*m_input_pos, detail::keyword_alphabet) ) { - ++m_input_pos; - ++m_col; + if (m_position.has_more() && char_in_alphabet(*m_position, detail::id_alphabet)) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet) ) { + ++m_position; } return true; - } else if (has_more_input() && (*m_input_pos == '`')) { - ++m_col; - ++m_input_pos; - const auto start = m_input_pos; + } else if (m_position.has_more() && (*m_position == '`')) { + ++m_position; + const auto start = m_position; - while (has_more_input() && (*m_input_pos != '`')) { + while (m_position.has_more() && (*m_position != '`')) { if (Eol()) { - throw exception::eval_error("Carriage return in identifier literal", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Carriage return in identifier literal", File_Position(m_position.line, m_position.col), *m_filename); } else { - ++m_input_pos; - ++m_col; + ++m_position; } } - if (start == m_input_pos) { - throw exception::eval_error("Missing contents of identifier literal", File_Position(m_line, m_col), *m_filename); + if (start == m_position) { + throw exception::eval_error("Missing contents of identifier literal", File_Position(m_position.line, m_position.col), *m_filename); } - else if (m_input_pos == m_input_end) { - throw exception::eval_error("Incomplete identifier literal", File_Position(m_line, m_col), *m_filename); + else if (!m_position.has_more()) { + throw exception::eval_error("Incomplete identifier literal", File_Position(m_position.line, m_position.col), *m_filename); } - ++m_col; - ++m_input_pos; + ++m_position; return true; } @@ -794,20 +810,18 @@ namespace chaiscript bool Id() { SkipWS(); - const auto start = m_input_pos; - const auto prev_col = m_col; - const auto prev_line = m_line; + const auto start = m_position; if (Id_()) { m_match_stack.push_back(make_node( [&]()->std::string{ if (*start == '`') { //Id Literal - return std::string(start+1, m_input_pos-1); + return Position::str(start+1, m_position-1); } else { - return std::string(start, m_input_pos); + return Position::str(start, m_position); } }(), - prev_line, prev_col)); + start.line, start.col)); return true; } else { return false; @@ -839,24 +853,21 @@ namespace chaiscript /// Checks for a node annotation of the form "#" bool Annotation() { SkipWS(); - const auto start = m_input_pos; - const auto prev_col = m_col; - const auto prev_line = m_line; + const auto start = m_position; if (Symbol_("#")) { do { - while (m_input_pos != m_input_end) { + while (m_position.has_more()) { if (Eol_()) { break; } else { - ++m_col; - ++m_input_pos; + ++m_position; } } } while (Symbol("#")); - std::string match(start, m_input_pos); - m_match_stack.push_back(make_node(std::move(match), prev_line, prev_col)); + auto match = Position::str(start, m_position); + m_match_stack.push_back(make_node(std::move(match), start.line, start.col)); return true; } else { @@ -866,28 +877,37 @@ namespace chaiscript /// Reads a quoted string from input, without skipping initial whitespace bool Quoted_String_() { - if (has_more_input() && (*m_input_pos == '\"')) { - char prev_char = *m_input_pos; - ++m_input_pos; - ++m_col; + if (m_position.has_more() && (*m_position == '\"')) { + char prev_char = *m_position; + ++m_position; + + int in_interpolation = 0; + bool in_quote = false; + + while (m_position.has_more() && ((*m_position != '\"') || ((*m_position == '\"') && (in_interpolation > 0)) || ((*m_position == '\"') && (prev_char == '\\')))) { - while (has_more_input() && ((*m_input_pos != '\"') || ((*m_input_pos == '\"') && (prev_char == '\\')))) { if (!Eol_()) { + if (prev_char == '$' && *m_position == '{') { + ++in_interpolation; + } else if (prev_char != '\\' && *m_position == '"') { + in_quote = !in_quote; + } else if (*m_position == '}' && !in_quote) { + --in_interpolation; + } + if (prev_char == '\\') { prev_char = 0; } else { - prev_char = *m_input_pos; + prev_char = *m_position; } - ++m_input_pos; - ++m_col; + ++m_position; } } - if (has_more_input()) { - ++m_input_pos; - ++m_col; + if (m_position.has_more()) { + ++m_position; } else { - throw exception::eval_error("Unclosed quoted string", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Unclosed quoted string", File_Position(m_position.line, m_position.col), *m_filename); } return true; @@ -895,6 +915,140 @@ namespace chaiscript return false; } + template + struct Char_Parser + { + 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; + const bool interpolation_allowed; + + string_type octal_matches; + string_type hex_matches; + + 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) + { + } + + Char_Parser &operator=(const Char_Parser &) = delete; + + ~Char_Parser(){ + if (is_octal) { + process_octal(); + } + + if (is_hex) { + process_hex(); + } + } + + void process_hex() + { + auto val = stoll(hex_matches, 0, 16); + match.push_back(char_type(val)); + hex_matches.clear(); + is_escaped = false; + is_hex = false; + } + + + void process_octal() + { + auto val = stoll(octal_matches, 0, 8); + match.push_back(char_type(val)); + octal_matches.clear(); + is_escaped = false; + is_octal = 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'; + + if (is_octal) { + if (is_octal_char) { + octal_matches.push_back(t_char); + + if (octal_matches.size() == 3) { + process_octal(); + } + return; + } else { + 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); + + if (hex_matches.size() == 2*sizeof(char_type)) { + // This rule differs from the C/C++ standard, but ChaiScript + // does not offer the same workaround options, and having + // hexadecimal sequences longer than can fit into the char + // type is undefined behavior anyway. + process_hex(); + } + return; + } else { + process_hex(); + } + } + + if (t_char == '\\') { + if (is_escaped) { + match.push_back('\\'); + is_escaped = false; + } else { + is_escaped = true; + } + } else { + if (is_escaped) { + if (is_octal_char) { + is_octal = true; + octal_matches.push_back(t_char); + } else if (t_char == 'x') { + is_hex = true; + } else { + switch (t_char) { + case ('\'') : match.push_back('\''); break; + case ('\"') : match.push_back('\"'); break; + case ('?') : match.push_back('?'); break; + case ('a') : match.push_back('\a'); break; + case ('b') : match.push_back('\b'); break; + case ('f') : match.push_back('\f'); break; + case ('n') : match.push_back('\n'); break; + case ('r') : match.push_back('\r'); break; + case ('t') : match.push_back('\t'); break; + case ('v') : match.push_back('\v'); break; + case ('$') : match.push_back('$'); break; + default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(line, col), filename); + } + is_escaped = false; + } + } else if (interpolation_allowed && t_char == '$') { + saw_interpolation_marker = true; + } else { + match.push_back(t_char); + } + } + } + + }; + + /// Reads (and potentially captures) a quoted string from input. Translates escaped sequences. bool Quoted_String(const bool t_capture = false) { SkipWS(); @@ -902,111 +1056,86 @@ namespace chaiscript if (!t_capture) { return Quoted_String_(); } else { - const auto start = m_input_pos; - const auto prev_col = m_col; - const auto prev_line = m_line; + const auto start = m_position; if (Quoted_String_()) { std::string match; - bool is_escaped = false; - bool is_interpolated = false; - bool saw_interpolation_marker = false; const auto prev_stack_top = m_match_stack.size(); - std::string::const_iterator s = start + 1, end = m_input_pos - 1; + bool is_interpolated = [&]()->bool { + Char_Parser cparser(match, true); - while (s != end) { - if (saw_interpolation_marker) { - if (*s == '{') { - //We've found an interpolation point - if (is_interpolated) { - //If we've seen previous interpolation, add on instead of making a new one - m_match_stack.push_back(make_node(match, prev_line, prev_col)); + auto s = start + 1, end = m_position - 1; - build_match(prev_stack_top, "+"); - } else { - m_match_stack.push_back(make_node(match, prev_line, prev_col)); - } + while (s != end) { + if (cparser.saw_interpolation_marker) { + if (*s == '{') { + //We've found an interpolation point - //We've finished with the part of the string up to this point, so clear it - match.clear(); + 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)); - std::string eval_match; - - ++s; - while ((s != end) && (*s != '}')) { - eval_match.push_back(*s); - ++s; - } - - if (*s == '}') { - is_interpolated = true; - ++s; - - const auto tostr_stack_top = m_match_stack.size(); - - m_match_stack.push_back(make_node("to_string", prev_line, prev_col)); - - const auto ev_stack_top = m_match_stack.size(); - - /// \todo can we evaluate this in place and save the runtime cost of evaluating with each execution of the node? - m_match_stack.push_back(make_node("eval", prev_line, prev_col)); - - const auto arg_stack_top = m_match_stack.size(); - - m_match_stack.push_back(make_node(eval_match, prev_line, prev_col)); - - build_match(arg_stack_top); - build_match(ev_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(prev_line, prev_col), *m_filename); - } - } else { - match.push_back('$'); - } - saw_interpolation_marker = false; - } else { - if (*s == '\\') { - if (is_escaped) { - match.push_back('\\'); - is_escaped = false; - } else { - is_escaped = true; - } - } else { - if (is_escaped) { - switch (*s) { - case ('b') : match.push_back('\b'); break; - case ('f') : match.push_back('\f'); break; - case ('n') : match.push_back('\n'); break; - case ('r') : match.push_back('\r'); break; - case ('t') : match.push_back('\t'); break; - case ('\'') : match.push_back('\''); break; - case ('\"') : match.push_back('\"'); break; - case ('$') : match.push_back('$'); break; - default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(prev_line, prev_col), *m_filename); + build_match(prev_stack_top, "+"); + } else { + m_match_stack.push_back(make_node(match, start.line, start.col)); + } + + //We've finished with the part of the string up to this point, so clear it + match.clear(); + + std::string eval_match; + + ++s; + while ((s != end) && (*s != '}')) { + eval_match.push_back(*s); + ++s; + } + + if (*s == '}') { + cparser.is_interpolated = true; + ++s; + + const auto tostr_stack_top = m_match_stack.size(); + + 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()); + } 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, "+"); + } else { + throw exception::eval_error("Unclosed in-string eval", File_Position(start.line, start.col), *m_filename); } - } else if (*s == '$') { - saw_interpolation_marker = true; } else { - match.push_back(*s); + match.push_back('$'); } - is_escaped = false; + cparser.saw_interpolation_marker = false; + } else { + cparser.parse(*s, start.line, start.col, *m_filename); + ++s; } - ++s; } - } + + return cparser.is_interpolated; + }(); if (is_interpolated) { - m_match_stack.push_back(make_node(match, prev_line, prev_col)); + 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, prev_line, prev_col)); + m_match_stack.push_back(make_node(match, start.line, start.col)); } return true; } else { @@ -1018,29 +1147,26 @@ namespace chaiscript /// Reads a character group from input, without skipping initial whitespace bool Single_Quoted_String_() { bool retval = false; - if (has_more_input() && (*m_input_pos == '\'')) { + if (m_position.has_more() && (*m_position == '\'')) { retval = true; - char prev_char = *m_input_pos; - ++m_input_pos; - ++m_col; + char prev_char = *m_position; + ++m_position; - while (has_more_input() && ((*m_input_pos != '\'') || ((*m_input_pos == '\'') && (prev_char == '\\')))) { + while (m_position.has_more() && ((*m_position != '\'') || ((*m_position == '\'') && (prev_char == '\\')))) { if (!Eol_()) { if (prev_char == '\\') { prev_char = 0; } else { - prev_char = *m_input_pos; + prev_char = *m_position; } - ++m_input_pos; - ++m_col; + ++m_position; } } - if (m_input_pos != m_input_end) { - ++m_input_pos; - ++m_col; + if (m_position.has_more()) { + ++m_position; } else { - throw exception::eval_error("Unclosed single-quoted string", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Unclosed single-quoted string", File_Position(m_position.line, m_position.col), *m_filename); } } return retval; @@ -1053,39 +1179,20 @@ namespace chaiscript if (!t_capture) { return Single_Quoted_String_(); } else { - const auto start = m_input_pos; - const auto prev_col = m_col; - const auto prev_line = m_line; + const auto start = m_position; if (Single_Quoted_String_()) { std::string match; - bool is_escaped = false; - for (auto s = start + 1, end = m_input_pos - 1; s != end; ++s) { - if (*s == '\\') { - if (is_escaped) { - match.push_back('\\'); - is_escaped = false; - } else { - is_escaped = true; - } - } else { - if (is_escaped) { - switch (*s) { - case ('b') : match.push_back('\b'); break; - case ('f') : match.push_back('\f'); break; - case ('n') : match.push_back('\n'); break; - case ('r') : match.push_back('\r'); break; - case ('t') : match.push_back('\t'); break; - case ('\'') : match.push_back('\''); break; - case ('\"') : match.push_back('\"'); break; - default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(prev_line, prev_col), *m_filename); - } - } else { - match.push_back(*s); - } - is_escaped = false; + + { + // scope for cparser destrutor + Char_Parser cparser(match, false); + + for (auto s = start + 1, end = m_position - 1; s != end; ++s) { + cparser.parse(*s, start.line, start.col, *m_filename); } } - m_match_stack.push_back(make_node(match, prev_line, prev_col)); + + m_match_stack.push_back(make_node(match, start.line, start.col)); return true; } else { @@ -1096,9 +1203,8 @@ namespace chaiscript /// Reads a char from input if it matches the parameter, without skipping initial whitespace bool Char_(const char c) { - if (has_more_input() && (*m_input_pos == c)) { - ++m_input_pos; - ++m_col; + if (m_position.has_more() && (*m_position == c)) { + ++m_position; return true; } else { return false; @@ -1112,11 +1218,9 @@ namespace chaiscript if (!t_capture) { return Char_(t_c); } else { - const auto start = m_input_pos; - const auto prev_col = m_col; - const auto prev_line = m_line; + const auto start = m_position; if (Char_(t_c)) { - m_match_stack.push_back(make_node(std::string(start, m_input_pos), prev_line, prev_col)); + m_match_stack.push_back(make_node(Position::str(start, m_position), start.line, start.col)); return true; } else { return false; @@ -1128,16 +1232,15 @@ namespace chaiscript bool Keyword_(const char *t_s) { const auto len = strlen(t_s); - if ((m_input_end - m_input_pos) >= static_cast::type>(len)) { - auto tmp = m_input_pos; - for (size_t i = 0; i < len; ++i) { + if (m_position.remaining() >= len) { + auto tmp = m_position; + for (size_t i = 0; tmp.has_more() && i < len; ++i) { if (*tmp != t_s[i]) { return false; } ++tmp; } - m_input_pos = tmp; - m_col += static_cast(len); + m_position = tmp; return true; } @@ -1147,20 +1250,16 @@ 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) { SkipWS(); - const auto start = m_input_pos; - const auto prev_col = m_col; - const auto prev_line = m_line; + const auto start = m_position; bool retval = Keyword_(t_s); // ignore substring matches - if ( retval && has_more_input() && char_in_alphabet(*m_input_pos, detail::keyword_alphabet) ) { - m_input_pos = start; - m_col = prev_col; - m_line = prev_line; + if ( retval && m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet) ) { + m_position = start; retval = false; } if ( t_capture && retval ) { - m_match_stack.push_back(make_node(std::string(start, m_input_pos), prev_line, prev_col)); + m_match_stack.push_back(make_node(Position::str(start, m_position), start.line, start.col)); } return retval; } @@ -1169,40 +1268,49 @@ namespace chaiscript bool Symbol_(const char *t_s) { const auto len = strlen(t_s); - if ((m_input_end - m_input_pos) >= static_cast::type>(len)) { - auto tmp = m_input_pos; - for (size_t i = 0; i < len; ++i) { + if (m_position.remaining() >= len) { + auto tmp = m_position; + for (size_t i = 0; m_position.has_more() && i < len; ++i) { if (*tmp != t_s[i]) { return false; } ++tmp; } - m_input_pos = tmp; - m_col += static_cast(len); + m_position = tmp; return true; } return false; } + bool is_operator(const std::string &t_s) const { + return std::any_of(m_operator_matches.begin(), m_operator_matches.end(), + [t_s](const std::vector &opers) { + return std::any_of(opers.begin(), opers.end(), + [t_s](const std::string &s) { + return s == t_s; + }); + }); + } + /// 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) { SkipWS(); - const auto start = m_input_pos; - const auto prev_col = m_col; - const auto prev_line = m_line; + const auto start = m_position; bool retval = Symbol_(t_s); // ignore substring matches - if (retval && has_more_input() && (t_disallow_prevention == false) && char_in_alphabet(*m_input_pos,detail::symbol_alphabet)) { - m_input_pos = start; - m_col = prev_col; - m_line = prev_line; - retval = false; + if (retval && m_position.has_more() && (t_disallow_prevention == false) && char_in_alphabet(*m_position,detail::symbol_alphabet)) { + if (*m_position != '=' && is_operator(Position::str(start, m_position)) && !is_operator(Position::str(start, m_position+1))) { + // don't throw this away, it's a good match and the next is not + } else { + m_position = start; + retval = false; + } } if ( t_capture && retval ) { - m_match_stack.push_back(make_node(std::string(start, m_input_pos), prev_line, prev_col)); + m_match_stack.push_back(make_node(Position::str(start, m_position), start.line, start.col)); } return retval; @@ -1212,11 +1320,11 @@ namespace chaiscript bool Eol_(const bool t_eos = false) { bool retval = false; - if (has_more_input() && (Symbol_("\r\n") || Char_('\n'))) { + if (m_position.has_more() && (Symbol_("\r\n") || Char_('\n'))) { retval = true; - ++m_line; - m_col = 1; - } else if (has_more_input() && !t_eos && Char_(';')) { + //++m_position.line; + m_position.col = 1; + } else if (m_position.has_more() && !t_eos && Char_(';')) { retval = true; } @@ -1251,7 +1359,7 @@ namespace chaiscript do { while (Eol()) {} if (!Arg(false)) { - throw exception::eval_error("Unexpected value in parameter list", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); } } while (Char(',')); } @@ -1277,7 +1385,7 @@ namespace chaiscript do { while (Eol()) {} if (!Arg()) { - throw exception::eval_error("Unexpected value in parameter list", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); } } while (Char(',')); } @@ -1304,7 +1412,7 @@ namespace chaiscript do { while (Eol()) {} if (!Equation()) { - throw exception::eval_error("Unexpected value in parameter list", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); } } while (Char(',')); } @@ -1334,7 +1442,7 @@ namespace chaiscript do { while (Eol()) {} if (!Map_Pair()) { - throw exception::eval_error("Unexpected value in container", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename); } } while (Char(',')); } @@ -1346,7 +1454,7 @@ namespace chaiscript do { while (Eol()) {} if (!Operator()) { - throw exception::eval_error("Unexpected value in container", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename); } } while (Char(',')); } @@ -1370,7 +1478,7 @@ namespace chaiscript if (Char('[')) { Id_Arg_List(); if (!Char(']')) { - throw exception::eval_error("Incomplete anonymous function bind", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete anonymous function bind", File_Position(m_position.line, m_position.col), *m_filename); } } else { // make sure we always have the same number of nodes @@ -1380,17 +1488,17 @@ namespace chaiscript if (Char('(')) { Decl_Arg_List(); if (!Char(')')) { - throw exception::eval_error("Incomplete anonymous function", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename); } } else { - throw exception::eval_error("Incomplete anonymous function", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename); } while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete anonymous function", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -1416,7 +1524,7 @@ namespace chaiscript retval = true; if (!Id()) { - throw exception::eval_error("Missing function name in definition", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Missing function name in definition", File_Position(m_position.line, m_position.col), *m_filename); } bool is_method = false; @@ -1426,14 +1534,14 @@ namespace chaiscript is_method = true; if (!Id()) { - throw exception::eval_error("Missing method name in definition", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Missing method name in definition", File_Position(m_position.line, m_position.col), *m_filename); } } if (Char('(')) { Decl_Arg_List(); if (!Char(')')) { - throw exception::eval_error("Incomplete function definition", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete function definition", File_Position(m_position.line, m_position.col), *m_filename); } } @@ -1441,13 +1549,13 @@ namespace chaiscript if (Char(':')) { if (!Operator()) { - throw exception::eval_error("Missing guard expression for function", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Missing guard expression for function", File_Position(m_position.line, m_position.col), *m_filename); } } while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete function definition", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete function definition", File_Position(m_position.line, m_position.col), *m_filename); } if (is_method || t_class_context) { @@ -1476,7 +1584,7 @@ namespace chaiscript while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete 'try' block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'try' block", File_Position(m_position.line, m_position.col), *m_filename); } bool has_matches = true; @@ -1487,11 +1595,11 @@ namespace chaiscript const auto catch_stack_top = m_match_stack.size(); if (Char('(')) { if (!(Arg() && Char(')'))) { - throw exception::eval_error("Incomplete 'catch' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'catch' expression", File_Position(m_position.line, m_position.col), *m_filename); } if (Char(':')) { if (!Operator()) { - throw exception::eval_error("Missing guard expression for catch", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Missing guard expression for catch", File_Position(m_position.line, m_position.col), *m_filename); } } } @@ -1499,7 +1607,7 @@ namespace chaiscript while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete 'catch' block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'catch' block", File_Position(m_position.line, m_position.col), *m_filename); } build_match(catch_stack_top); has_matches = true; @@ -1512,7 +1620,7 @@ namespace chaiscript while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete 'finally' block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'finally' block", File_Position(m_position.line, m_position.col), *m_filename); } build_match(finally_stack_top); } @@ -1533,17 +1641,17 @@ namespace chaiscript retval = true; if (!Char('(')) { - throw exception::eval_error("Incomplete 'if' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename); } if (!(Operator() && Char(')'))) { - throw exception::eval_error("Incomplete 'if' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename); } while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete 'if' block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'if' block", File_Position(m_position.line, m_position.col), *m_filename); } bool has_matches = true; @@ -1557,24 +1665,24 @@ namespace chaiscript 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_line, m_col), *m_filename); + 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_line, m_col), *m_filename); + 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_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'else if' block", File_Position(m_position.line, m_position.col), *m_filename); } has_matches = true; } else { while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete 'else' block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'else' block", File_Position(m_position.line, m_position.col), *m_filename); } has_matches = true; } @@ -1597,14 +1705,14 @@ namespace chaiscript retval = true; if (!Id()) { - throw exception::eval_error("Missing class name in definition", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Missing class name in definition", File_Position(m_position.line, m_position.col), *m_filename); } while (Eol()) {} if (!Class_Block()) { - throw exception::eval_error("Incomplete 'class' block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'class' block", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -1624,17 +1732,17 @@ namespace chaiscript retval = true; if (!Char('(')) { - throw exception::eval_error("Incomplete 'while' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'while' expression", File_Position(m_position.line, m_position.col), *m_filename); } if (!(Operator() && Char(')'))) { - throw exception::eval_error("Incomplete 'while' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'while' expression", File_Position(m_position.line, m_position.col), *m_filename); } while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete 'while' block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'while' block", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -1650,7 +1758,7 @@ namespace chaiscript { if (!Eol()) { - throw exception::eval_error("'for' loop initial statment missing", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("'for' loop initial statment missing", File_Position(m_position.line, m_position.col), *m_filename); } else { m_match_stack.push_back(chaiscript::make_shared()); } @@ -1660,7 +1768,7 @@ namespace chaiscript { if (!Eol()) { - throw exception::eval_error("'for' loop condition missing", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("'for' loop condition missing", File_Position(m_position.line, m_position.col), *m_filename); } else { m_match_stack.push_back(chaiscript::make_shared()); } @@ -1684,17 +1792,17 @@ namespace chaiscript retval = true; if (!Char('(')) { - throw exception::eval_error("Incomplete 'for' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename); } if (!(For_Guards() && Char(')'))) { - throw exception::eval_error("Incomplete 'for' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename); } while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete 'for' block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -1713,25 +1821,27 @@ namespace chaiscript retval = true; if (!Char('(')) { - throw exception::eval_error("Incomplete 'case' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'case' expression", File_Position(m_position.line, m_position.col), *m_filename); } if (!(Operator() && Char(')'))) { - throw exception::eval_error("Incomplete 'case' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'case' expression", File_Position(m_position.line, m_position.col), *m_filename); } while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete 'case' block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'case' block", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); } else if (Keyword("default")) { + retval = true; + while (Eol()) {} if (!Block()) { - throw exception::eval_error("Incomplete 'default' block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'default' block", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -1748,11 +1858,11 @@ namespace chaiscript if (Keyword("switch")) { if (!Char('(')) { - throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_position.line, m_position.col), *m_filename); } if (!(Operator() && Char(')'))) { - throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_position.line, m_position.col), *m_filename); } while (Eol()) {} @@ -1767,11 +1877,11 @@ namespace chaiscript while (Eol()) { } // eat if (!Char('}')) { - throw exception::eval_error("Incomplete block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename); } } else { - throw exception::eval_error("Incomplete block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -1795,7 +1905,7 @@ namespace chaiscript Class_Statements(); if (!Char('}')) { - throw exception::eval_error("Incomplete class block", File_Position(m_line, m_col), *m_filename); + 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) { @@ -1819,7 +1929,7 @@ namespace chaiscript Statements(); if (!Char('}')) { - throw exception::eval_error("Incomplete block", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename); } if (m_match_stack.size() == prev_stack_top) { @@ -1888,20 +1998,24 @@ namespace chaiscript Arg_List(); if (!Char(')')) { - throw exception::eval_error("Incomplete function call", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete function call", File_Position(m_position.line, m_position.col), *m_filename); } 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(); 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); m_match_stack.push_back(std::move(dot_access)); } } @@ -1909,7 +2023,7 @@ namespace chaiscript has_more = true; if (!(Operator() && Char(']'))) { - throw exception::eval_error("Incomplete array access", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete array access", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -1917,9 +2031,12 @@ namespace chaiscript else if (Symbol(".", true)) { has_more = true; if (!(Id())) { - throw exception::eval_error("Incomplete array access", File_Position(m_line, m_col), *m_filename); + 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) { + throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); + } build_match(prev_stack_top); } } @@ -1938,7 +2055,7 @@ namespace chaiscript retval = true; if (!Id()) { - throw exception::eval_error("Incomplete attribute declaration", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -1946,15 +2063,15 @@ namespace chaiscript retval = true; if (!(Reference() || Id())) { - throw exception::eval_error("Incomplete variable declaration", File_Position(m_line, m_col), *m_filename); + 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")) { + } else if (Keyword("GLOBAL") || Keyword("global")) { retval = true; if (!(Reference() || Id())) { - throw exception::eval_error("Incomplete global declaration", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete global declaration", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -1962,13 +2079,13 @@ namespace chaiscript retval = true; if (!Id()) { - throw exception::eval_error("Incomplete attribute declaration", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); } if (!Symbol("::", false)) { - throw exception::eval_error("Incomplete attribute declaration", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); } if (!Id()) { - throw exception::eval_error("Missing attribute name in definition", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Missing attribute name in definition", File_Position(m_position.line, m_position.col), *m_filename); } @@ -1982,10 +2099,10 @@ namespace chaiscript bool Paren_Expression() { if (Char('(')) { if (!Operator()) { - throw exception::eval_error("Incomplete expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete expression", File_Position(m_position.line, m_position.col), *m_filename); } if (!Char(')')) { - throw exception::eval_error("Missing closing parenthesis ')'", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Missing closing parenthesis ')'", File_Position(m_position.line, m_position.col), *m_filename); } return true; } else { @@ -2001,7 +2118,7 @@ namespace chaiscript Container_Arg_List(); if (!Char(']')) { - throw exception::eval_error("Missing closing square bracket ']' in container initializer", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Missing closing square bracket ']' in container initializer", File_Position(m_position.line, m_position.col), *m_filename); } 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) { @@ -2030,7 +2147,7 @@ namespace chaiscript if (Symbol("&", false)) { if (!Id()) { - throw exception::eval_error("Incomplete '&' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete '&' expression", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -2051,7 +2168,7 @@ namespace chaiscript if ((is_char && Char(oper[0], true)) || (!is_char && Symbol(oper.c_str(), true))) { if (!Operator(m_operators.size()-1)) { - throw exception::eval_error("Incomplete prefix '" + oper + "' expression", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete prefix '" + oper + "' expression", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -2089,7 +2206,7 @@ namespace chaiscript 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_line, m_col), *m_filename); + File_Position(m_position.line, m_position.col), *m_filename); } AST_NodePtr oper = m_match_stack.at(m_match_stack.size()-2); @@ -2102,14 +2219,14 @@ namespace chaiscript 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_line, m_col), *m_filename); + 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", - File_Position(m_line, m_col), *m_filename); + File_Position(m_position.line, m_position.col), *m_filename); } break; @@ -2134,7 +2251,7 @@ namespace chaiscript break; default: - throw exception::eval_error("Internal error: unhandled ast_node", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Internal error: unhandled ast_node", File_Position(m_position.line, m_position.col), *m_filename); } } while (Operator_Helper(t_precedence)); } @@ -2152,21 +2269,19 @@ namespace chaiscript bool retval = false; const auto prev_stack_top = m_match_stack.size(); - const auto prev_pos = m_input_pos; - const auto prev_col = m_col; + const auto prev_pos = m_position; if (Operator()) { if (Symbol(":")) { retval = true; if (!Operator()) { - throw exception::eval_error("Incomplete map pair", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete map pair", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); } else { - m_input_pos = prev_pos; - m_col = prev_col; + m_position = prev_pos; while (prev_stack_top != m_match_stack.size()) { m_match_stack.pop_back(); } @@ -2181,21 +2296,19 @@ namespace chaiscript bool retval = false; const auto prev_stack_top = m_match_stack.size(); - const auto prev_pos = m_input_pos; - const auto prev_col = m_col; + const auto prev_pos = m_position; if (Operator()) { if (Symbol("..")) { retval = true; if (!Operator()) { - throw exception::eval_error("Incomplete value range", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete value range", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); } else { - m_input_pos = prev_pos; - m_col = prev_col; + m_position = prev_pos; while (prev_stack_top != m_match_stack.size()) { m_match_stack.pop_back(); } @@ -2219,7 +2332,7 @@ namespace chaiscript Symbol("&=", true, true) || Symbol("^=", true, true) || Symbol("|=", true, true)) { SkipWS(true); if (!Equation()) { - throw exception::eval_error("Incomplete equation", File_Position(m_line, m_col), *m_filename); + throw exception::eval_error("Incomplete equation", File_Position(m_position.line, m_position.col), *m_filename); } build_match(prev_stack_top); @@ -2237,11 +2350,10 @@ namespace chaiscript bool saw_eol = true; while (has_more) { - const auto prev_line = m_line; - const auto prev_col = m_col; + const auto start = m_position; if (Def(true) || Var_Decl(true)) { if (!saw_eol) { - throw exception::eval_error("Two function definitions missing line separator", File_Position(prev_line, prev_col), *m_filename); + throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename); } has_more = true; retval = true; @@ -2266,11 +2378,10 @@ namespace chaiscript bool saw_eol = true; while (has_more) { - int prev_line = m_line; - int prev_col = m_col; + const auto start = m_position; if (Def() || Try() || If() || While() || Class() || For() || Switch()) { if (!saw_eol) { - throw exception::eval_error("Two function definitions missing line separator", File_Position(prev_line, prev_col), *m_filename); + throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename); } has_more = true; retval = true; @@ -2278,7 +2389,7 @@ namespace chaiscript } else if (Return() || Break() || Continue() || Equation()) { if (!saw_eol) { - throw exception::eval_error("Two expressions missing line separator", File_Position(prev_line, prev_col), *m_filename); + throw exception::eval_error("Two expressions missing line separator", File_Position(start.line, start.col), *m_filename); } has_more = true; retval = true; @@ -2299,24 +2410,22 @@ namespace chaiscript /// 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) { - m_input_pos = t_input.begin(); - m_input_end = t_input.end(); - m_line = 1; - m_col = 1; + m_position = Position(t_input.begin(), t_input.end()); m_filename = std::make_shared(std::move(t_fname)); if ((t_input.size() > 1) && (t_input[0] == '#') && (t_input[1] == '!')) { - while ((m_input_pos != m_input_end) && (!Eol())) { - ++m_input_pos; + 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()) { - if (m_input_pos != m_input_end) { - throw exception::eval_error("Unparsed input", File_Position(m_line, m_col), t_fname); + 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; } } else { @@ -2327,5 +2436,12 @@ namespace chaiscript } } + +#ifdef CHAISCRIPT_MSVC +#pragma pop_macro("min") +#pragma pop_macro("max") +#endif + + #endif /* CHAISCRIPT_PARSER_HPP_ */ diff --git a/include/chaiscript/language/chaiscript_prelude.chai b/include/chaiscript/language/chaiscript_prelude.chai index 0fe2a5a..55514bb 100644 --- a/include/chaiscript/language/chaiscript_prelude.chai +++ b/include/chaiscript/language/chaiscript_prelude.chai @@ -41,25 +41,25 @@ def new(x) { } def clone(double x) { - double(x).copy_var_attrs(x) + double(x).clone_var_attrs(x) } def clone(string x) { - string(x).copy_var_attrs(x) + string(x).clone_var_attrs(x) } def clone(vector x) { - vector(x).copy_var_attrs(x) + vector(x).clone_var_attrs(x) } def clone(int x) { - int(x).copy_var_attrs(x) + int(x).clone_var_attrs(x) } def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x) { - eval(type_name(x))(x).copy_var_attrs(x); + eval(type_name(x))(x).clone_var_attrs(x); } @@ -145,11 +145,6 @@ def reverse(container) { retval; } -# Return a range from a range -def range(r) : call_exists(empty, r) && call_exists(pop_front, r) && call_exists(pop_back, r) && call_exists(back, r) && call_exists(front, r) -{ - clone(r); -} def range(r) : call_exists(range_internal, r) { @@ -158,6 +153,13 @@ def range(r) : call_exists(range_internal, r) ri; } +# Return a range from a range +def range(r) : call_exists(empty, r) && call_exists(pop_front, r) && call_exists(pop_back, r) && call_exists(back, r) && call_exists(front, r) +{ + clone(r); +} + + # The retro attribute that contains the underlying range attr retro::m_range; diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp new file mode 100644 index 0000000..56f28e7 --- /dev/null +++ b/include/chaiscript/utility/json.hpp @@ -0,0 +1,663 @@ +// From github.com/nbsdx/SimpleJSON. +// Released under the DWTFYW PL +// + + +#pragma once + +#ifndef SIMPLEJSON_HPP +#define SIMPLEJSON_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../chaiscript_defines.hpp" + +namespace json { + +using std::map; +using std::deque; +using std::string; +using std::enable_if; +using std::initializer_list; +using std::is_same; +using std::is_convertible; +using std::is_integral; +using std::is_floating_point; + +namespace { + string json_escape( const string &str ) { + string output; + for( unsigned i = 0; i < str.length(); ++i ) + switch( str[i] ) { + case '\"': output += "\\\""; break; + case '\\': output += "\\\\"; break; + case '\b': output += "\\b"; break; + case '\f': output += "\\f"; break; + case '\n': output += "\\n"; break; + case '\r': output += "\\r"; break; + case '\t': output += "\\t"; break; + default : output += str[i]; break; + } + return output; + } + + bool isspace(const char c) + { +#ifdef CHAISCRIPT_MSVC + // MSVC warns on these line in some circumstances +#pragma warning(push) +#pragma warning(disable : 6330) +#endif + return ::isspace(c) != 0; +#ifdef CHAISCRIPT_MSVC +#pragma warning(pop) +#endif + + + } +} + +class JSON +{ + union BackingData { + BackingData( double d ) : Float( d ){} + BackingData( long l ) : Int( l ){} + BackingData( bool b ) : Bool( b ){} + BackingData( string s ) : String( new string( s ) ){} + BackingData() : Int( 0 ){} + + deque *List; + map *Map; + string *String; + double Float; + long Int; + bool Bool; + } Internal; + + public: + enum class Class { + Null, + Object, + Array, + String, + Floating, + Integral, + Boolean + }; + + template + class JSONWrapper { + Container *object; + + public: + JSONWrapper( Container *val ) : object( val ) {} + JSONWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } + typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); } + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); } + }; + + template + class JSONConstWrapper { + const Container *object; + + public: + JSONConstWrapper( const Container *val ) : object( val ) {} + JSONConstWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); } + }; + + JSON() : Internal(), Type( Class::Null ){} + + explicit JSON(Class type) + : Internal(), Type(Class::Null) + { + SetType( type ); + } + + JSON( initializer_list list ) + : Internal(), Type(Class::Null) + { + SetType( Class::Object ); + for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i ) + operator[]( i->ToString() ) = *std::next( i ); + } + + JSON( JSON&& other ) + : Internal( other.Internal ) + , Type( other.Type ) + { other.Type = Class::Null; other.Internal.Map = nullptr; } + + JSON& operator=( JSON&& other ) { + Internal = other.Internal; + Type = other.Type; + other.Internal.Map = nullptr; + other.Type = Class::Null; + return *this; + } + + JSON( const JSON &other ) { + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + } + + JSON& operator=( const JSON &other ) { + if (&other == this) return *this; + + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + return *this; + } + + ~JSON() { + switch( Type ) { + case Class::Array: + delete Internal.List; + break; + case Class::Object: + delete Internal.Map; + break; + case Class::String: + delete Internal.String; + break; + default:; + } + } + + template + JSON( T b, typename enable_if::value>::type* = 0 ) : Internal( b ), Type( Class::Boolean ){} + + template + JSON( T i, typename enable_if::value && !is_same::value>::type* = 0 ) : Internal( long(i) ), Type( Class::Integral ){} + + template + JSON( T f, typename enable_if::value>::type* = 0 ) : Internal( double(f) ), Type( Class::Floating ){} + + template + JSON( T s, typename enable_if::value>::type* = 0 ) : Internal( string( s ) ), Type( Class::String ){} + + JSON( std::nullptr_t ) : Internal(), Type( Class::Null ){} + + static JSON Make( Class type ) { + return JSON(type); + } + + static JSON Load( const string & ); + + template + void append( T arg ) { + SetType( Class::Array ); Internal.List->emplace_back( arg ); + } + + template + void append( T arg, U... args ) { + append( arg ); append( args... ); + } + + template + typename enable_if::value, JSON&>::type operator=( T b ) { + SetType( Class::Boolean ); Internal.Bool = b; return *this; + } + + template + typename enable_if::value && !is_same::value, JSON&>::type operator=( T i ) { + SetType( Class::Integral ); Internal.Int = i; return *this; + } + + template + typename enable_if::value, JSON&>::type operator=( T f ) { + SetType( Class::Floating ); Internal.Float = f; return *this; + } + + template + typename enable_if::value, JSON&>::type operator=( T s ) { + SetType( Class::String ); *Internal.String = string( s ); return *this; + } + + JSON& operator[]( const string &key ) { + SetType( Class::Object ); return Internal.Map->operator[]( key ); + } + + JSON& operator[]( const size_t index ) { + SetType( Class::Array ); + if( index >= Internal.List->size() ) Internal.List->resize( index + 1 ); + return Internal.List->operator[]( index ); + } + + JSON &at( const string &key ) { + return operator[]( key ); + } + + const JSON &at( const string &key ) const { + return Internal.Map->at( key ); + } + + JSON &at( unsigned index ) { + return operator[]( index ); + } + + const JSON &at( unsigned index ) const { + return Internal.List->at( index ); + } + + int length() const { + if( Type == Class::Array ) + return static_cast(Internal.List->size()); + else + return -1; + } + + bool hasKey( const string &key ) const { + if( Type == Class::Object ) + return Internal.Map->find( key ) != Internal.Map->end(); + return false; + } + + int size() const { + if( Type == Class::Object ) + return static_cast(Internal.Map->size()); + else if( Type == Class::Array ) + return static_cast(Internal.List->size()); + else + return -1; + } + + Class JSONType() const { return Type; } + + /// Functions for getting primitives from the JSON object. + bool IsNull() const { return Type == Class::Null; } + + string ToString() const { bool b; return ToString( b ); } + string ToString( bool &ok ) const { + ok = (Type == Class::String); + return ok ? *Internal.String : string(""); + } + + double ToFloat() const { bool b; return ToFloat( b ); } + double ToFloat( bool &ok ) const { + ok = (Type == Class::Floating); + return ok ? Internal.Float : 0.0; + } + + long ToInt() const { bool b; return ToInt( b ); } + long ToInt( bool &ok ) const { + ok = (Type == Class::Integral); + return ok ? Internal.Int : 0; + } + + bool ToBool() const { bool b; return ToBool( b ); } + bool ToBool( bool &ok ) const { + ok = (Type == Class::Boolean); + return ok ? Internal.Bool : false; + } + + JSONWrapper> ObjectRange() { + if( Type == Class::Object ) + return JSONWrapper>( Internal.Map ); + return JSONWrapper>( nullptr ); + } + + JSONWrapper> ArrayRange() { + if( Type == Class::Array ) + return JSONWrapper>( Internal.List ); + return JSONWrapper>( nullptr ); + } + + JSONConstWrapper> ObjectRange() const { + if( Type == Class::Object ) + return JSONConstWrapper>( Internal.Map ); + return JSONConstWrapper>( nullptr ); + } + + + JSONConstWrapper> ArrayRange() const { + if( Type == Class::Array ) + return JSONConstWrapper>( Internal.List ); + return JSONConstWrapper>( nullptr ); + } + + string dump( int depth = 1, string tab = " ") const { + switch( Type ) { + case Class::Null: + return "null"; + case Class::Object: { + string pad = ""; + for( int i = 0; i < depth; ++i, pad += tab ); + + string s = "{\n"; + bool skip = true; + for( auto &p : *Internal.Map ) { + if( !skip ) s += ",\n"; + s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) ); + skip = false; + } + s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ; + return s; + } + case Class::Array: { + string s = "["; + bool skip = true; + for( auto &p : *Internal.List ) { + if( !skip ) s += ", "; + s += p.dump( depth + 1, tab ); + skip = false; + } + s += "]"; + return s; + } + case Class::String: + return "\"" + json_escape( *Internal.String ) + "\""; + case Class::Floating: + return std::to_string( Internal.Float ); + case Class::Integral: + return std::to_string( Internal.Int ); + case Class::Boolean: + return Internal.Bool ? "true" : "false"; + } + + throw std::runtime_error("Unhandled JSON type"); + } + + friend std::ostream& operator<<( std::ostream&, const JSON & ); + + private: + void SetType( Class type ) { + if( type == Type ) + return; + + switch( Type ) { + case Class::Object: delete Internal.Map; break; + case Class::Array: delete Internal.List; break; + case Class::String: delete Internal.String; break; + default:; + } + + switch( type ) { + case Class::Null: Internal.Map = nullptr; break; + case Class::Object: Internal.Map = new map(); break; + case Class::Array: Internal.List = new deque(); break; + case Class::String: Internal.String = new string(); break; + case Class::Floating: Internal.Float = 0.0; break; + case Class::Integral: Internal.Int = 0; break; + case Class::Boolean: Internal.Bool = false; break; + } + + Type = type; + } + + private: + + Class Type; +}; + +inline JSON Array() { + return JSON::Make( JSON::Class::Array ); +} + +template +inline JSON Array( T... args ) { + JSON arr = JSON::Make( JSON::Class::Array ); + arr.append( args... ); + return arr; +} + +inline JSON Object() { + return JSON::Make( JSON::Class::Object ); +} + +inline std::ostream& operator<<( std::ostream &os, const JSON &json ) { + os << json.dump(); + return os; +} + +namespace { + JSON parse_next( const string &, size_t & ); + + void consume_ws( const string &str, size_t &offset ) { + while( isspace( str[offset] ) ) ++offset; + } + + JSON parse_object( const string &str, size_t &offset ) { + JSON Object = JSON::Make( JSON::Class::Object ); + + ++offset; + consume_ws( str, offset ); + if( str[offset] == '}' ) { + ++offset; return Object; + } + + for (;offset= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) + val += c; + else { + throw std::runtime_error(std::string("JSON ERROR: String: Expected hex character in unicode escape, found '") + c + "'"); + } + } + offset += 4; + } break; + default : val += '\\'; break; + } + } + else + val += c; + } + ++offset; + return JSON(val); + } + + JSON parse_number( const string &str, size_t &offset ) { + JSON Number; + string val, exp_str; + char c = '\0'; + bool isDouble = false; + long exp = 0; + for (; offset < str.size() ;) { + c = str[offset++]; + if( (c == '-') || (c >= '0' && c <= '9') ) + val += c; + else if( c == '.' ) { + val += c; + isDouble = true; + } + else + break; + } + if( offset < str.size() && (c == 'E' || c == 'e' )) { + c = str[ offset++ ]; + if( c == '-' ) { exp_str += '-';} + else if( c == '+' ) { } + else --offset; + + for (; offset < str.size() ;) { + c = str[ offset++ ]; + if( c >= '0' && c <= '9' ) + exp_str += c; + else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + throw std::runtime_error(std::string("JSON ERROR: Number: Expected a number for exponent, found '") + c + "'"); + } + else + break; + } + exp = std::stol( exp_str ); + } + else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) { + throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'"); + } + --offset; + + if( isDouble ) + Number = std::stod( val ) * std::pow( 10, exp ); + else { + if( !exp_str.empty() ) + Number = std::stol( val ) * std::pow( 10, exp ); + else + Number = std::stol( val ); + } + return Number; + } + + JSON parse_bool( const string &str, size_t &offset ) { + JSON Bool; + if( str.substr( offset, 4 ) == "true" ) { + offset += 4; + Bool = true; + } else if( str.substr( offset, 5 ) == "false" ) { + offset += 5; + Bool = false; + } else { + throw std::runtime_error(std::string("JSON ERROR: Bool: Expected 'true' or 'false', found '") + str.substr( offset, 5 ) + "'"); + } + return Bool; + } + + JSON parse_null( const string &str, size_t &offset ) { + if( str.substr( offset, 4 ) != "null" ) { + throw std::runtime_error(std::string("JSON ERROR: Null: Expected 'null', found '") + str.substr( offset, 4 ) + "'"); + } + offset += 4; + return JSON(); + } + + JSON parse_next( const string &str, size_t &offset ) { + char value; + consume_ws( str, offset ); + value = str[offset]; + switch( value ) { + case '[' : return parse_array( str, offset ); + case '{' : return parse_object( str, offset ); + case '\"': return parse_string( str, offset ); + case 't' : + case 'f' : return parse_bool( str, offset ); + case 'n' : return parse_null( str, offset ); + default : if( ( value <= '9' && value >= '0' ) || value == '-' ) + return parse_number( str, offset ); + } + throw std::runtime_error(std::string("JSON ERROR: Parse: Unexpected starting character '") + value + "'"); + } +} + +inline JSON JSON::Load( const string &str ) { + size_t offset = 0; + return parse_next( str, offset ); +} + +} // End Namespace json + + +#endif diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp new file mode 100644 index 0000000..d15528f --- /dev/null +++ b/include/chaiscript/utility/json_wrap.hpp @@ -0,0 +1,158 @@ +#ifndef CHAISCRIPT_SIMPLEJSON_WRAP_HPP +#define CHAISCRIPT_SIMPLEJSON_WRAP_HPP + +#include "json.hpp" + +namespace chaiscript +{ + class json_wrap + { + public: + + static ModulePtr library(ModulePtr m = std::make_shared()) + { + + m->add(chaiscript::fun([](const std::string &t_str) { return from_json(t_str); }), "from_json"); + m->add(chaiscript::fun(&json_wrap::to_json), "to_json"); + + return m; + + } + + private: + + static Boxed_Value from_json(const json::JSON &t_json) + { + switch( t_json.JSONType() ) { + case json::JSON::Class::Null: + return Boxed_Value(); + case json::JSON::Class::Object: + { + std::map m; + + for (const auto &p : t_json.ObjectRange()) + { + m.insert(std::make_pair(p.first, from_json(p.second))); + } + + return Boxed_Value(m); + } + case json::JSON::Class::Array: + { + std::vector vec; + + for (const auto &p : t_json.ArrayRange()) + { + vec.emplace_back(from_json(p)); + } + + return Boxed_Value(vec); + } + case json::JSON::Class::String: + return Boxed_Value(t_json.ToString()); + case json::JSON::Class::Floating: + return Boxed_Value(t_json.ToFloat()); + case json::JSON::Class::Integral: + return Boxed_Value(t_json.ToInt()); + case json::JSON::Class::Boolean: + return Boxed_Value(t_json.ToBool()); + } + + throw std::runtime_error("Unknown JSON type"); + } + + static Boxed_Value from_json(const std::string &t_json) + { + return from_json( json::JSON::Load(t_json) ); + } + + static std::string to_json(const Boxed_Value &t_bv) + { + return to_json_object(t_bv).dump(); + } + + static json::JSON to_json_object(const Boxed_Value &t_bv) + { + try { + const std::map m = chaiscript::boxed_cast &>(t_bv); + + json::JSON obj; + for (const auto &o : m) + { + obj[o.first] = to_json_object(o.second); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a map + } + + try { + const std::vector v = chaiscript::boxed_cast &>(t_bv); + + json::JSON obj; + for (size_t i = 0; i < v.size(); ++i) + { + obj[i] = to_json_object(v[i]); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a vector + } + + + try { + Boxed_Number bn(t_bv); + json::JSON obj; + if (Boxed_Number::is_floating_point(t_bv)) + { + obj = bn.get_as(); + } else { + obj = bn.get_as(); + } + return obj; + } catch (const chaiscript::detail::exception::bad_any_cast &) { + // not a number + } + + try { + bool b = boxed_cast(t_bv); + json::JSON obj; + obj = b; + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a bool + } + + try { + std::string s = boxed_cast(t_bv); + json::JSON obj; + obj = s; + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a string + } + + + try { + const chaiscript::dispatch::Dynamic_Object &o = boxed_cast(t_bv); + + json::JSON obj; + for (const auto &attr : o.get_attrs()) + { + obj[attr.first] = to_json_object(attr.second); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a dynamic object + } + + throw std::runtime_error("Unknown object type to convert to JSON"); + } + + + }; + + +} + +#endif diff --git a/include/chaiscript/utility/utility.hpp b/include/chaiscript/utility/utility.hpp index ecd5a3f..22ab8e9 100644 --- a/include/chaiscript/utility/utility.hpp +++ b/include/chaiscript/utility/utility.hpp @@ -1,19 +1,21 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_UTILITY_UTILITY_HPP_ #define CHAISCRIPT_UTILITY_UTILITY_HPP_ #include +#include #include #include #include "../chaiscript.hpp" #include "../dispatchkit/proxy_functions.hpp" #include "../dispatchkit/type_info.hpp" +#include "../dispatchkit/operators.hpp" namespace chaiscript @@ -62,6 +64,42 @@ namespace chaiscript t_module.add(fun.first, fun.second); } } + + template + typename std::enable_if::value, void>::type + add_class(ModuleType &t_module, + const std::string &t_class_name, +#ifdef CHAISCRIPT_GCC_4_6 + const std::vector> &t_constants +#else + const std::vector::type, std::string>> &t_constants +#endif + ) + { + t_module.add(chaiscript::user_type(), t_class_name); + + t_module.add(chaiscript::constructor(), t_class_name); + t_module.add(chaiscript::constructor(), t_class_name); + + using namespace chaiscript::bootstrap::operators; + t_module.add([](){ + // add some comparison and assignment operators + return assign(not_equal(equal())); + }()); + +#ifdef CHAISCRIPT_GCC_4_6 + t_module.add(chaiscript::fun([](const Enum &e, const int &i) { return e == i; }), "=="); + t_module.add(chaiscript::fun([](const int &i, const Enum &e) { return i == e; }), "=="); +#else + t_module.add(chaiscript::fun([](const Enum &e, const typename std::underlying_type::type &i) { return e == i; }), "=="); + t_module.add(chaiscript::fun([](const typename std::underlying_type::type &i, const Enum &e) { return i == e; }), "=="); +#endif + + for (const auto &constant : t_constants) + { + t_module.add_global_const(chaiscript::const_var(Enum(constant.first)), constant.second); + } + } } } diff --git a/license.txt b/license.txt index 48f5292..105662d 100644 --- a/license.txt +++ b/license.txt @@ -1,4 +1,4 @@ -Copyright 2009-2015 Jason Turner +Copyright 2009-2016 Jason Turner Copyright 2009-2012 Jonathan Turner. All Rights Reserved. diff --git a/contrib/codeanalysis/heterogenous_array_loop.chai b/performance_tests/heterogenous_array_loop.chai similarity index 100% rename from contrib/codeanalysis/heterogenous_array_loop.chai rename to performance_tests/heterogenous_array_loop.chai diff --git a/contrib/codeanalysis/is_prime.chai b/performance_tests/is_prime.chai similarity index 100% rename from contrib/codeanalysis/is_prime.chai rename to performance_tests/is_prime.chai diff --git a/contrib/codeanalysis/profile.chai b/performance_tests/profile.chai similarity index 100% rename from contrib/codeanalysis/profile.chai rename to performance_tests/profile.chai diff --git a/performance_tests/profile_cpp_calls.chai b/performance_tests/profile_cpp_calls.chai new file mode 100644 index 0000000..011f9a1 --- /dev/null +++ b/performance_tests/profile_cpp_calls.chai @@ -0,0 +1,26 @@ + +var test_str = "bob was a string"; + +for( var i = 0; i < 200000; ++i) +{ + test_str.size(); +// test_str.find("a", i); + test_str.c_str(); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + test_str.erase_at(1); + + size(test_str); +// test_str.find("a", i); + c_str(test_str); + erase_at(test_str, 1); + erase_at(test_str, 1); + erase_at(test_str, 1); + erase_at(test_str, 1); + erase_at(test_str, 1); + erase_at(test_str, 1); + test_str = "bob was a string"; +} diff --git a/performance_tests/profile_cpp_calls_2.cpp b/performance_tests/profile_cpp_calls_2.cpp new file mode 100644 index 0000000..0424654 --- /dev/null +++ b/performance_tests/profile_cpp_calls_2.cpp @@ -0,0 +1,20 @@ +#include +#include + +double f(const std::string &, double, bool) noexcept { + return .0; +} + +int main() +{ + chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); + + chai.add(chaiscript::fun(&f), "f"); + + chai.eval(R"( + for (var i = 0; i < 100000; ++i) { + f("str", 1.2, false); + } + )"); + +} diff --git a/performance_tests/profile_fun_wrappers.cpp b/performance_tests/profile_fun_wrappers.cpp new file mode 100644 index 0000000..fb96f48 --- /dev/null +++ b/performance_tests/profile_fun_wrappers.cpp @@ -0,0 +1,20 @@ +#include +#include + +double f(const std::string &, double, bool) noexcept { + return .0; +} + +int main() +{ + chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); + + chai.add(chaiscript::fun(&f), "f"); + + const auto f = chai.eval>(R"(fun(){ f("str", 1.2, false); })"); + + for (int i = 0; i < 100000; ++i) { + f(); + } + +} diff --git a/contrib/codeanalysis/profile_math.chai b/performance_tests/profile_math.chai similarity index 100% rename from contrib/codeanalysis/profile_math.chai rename to performance_tests/profile_math.chai diff --git a/contrib/codeanalysis/type_conversions.chai b/performance_tests/type_conversions.chai similarity index 100% rename from contrib/codeanalysis/type_conversions.chai rename to performance_tests/type_conversions.chai diff --git a/readme.md b/readme.md index 75e470a..160662a 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ ChaiScript http://www.chaiscript.com (c) 2009-2012 Jonathan Turner -(c) 2009-2015 Jason Turner +(c) 2009-2016 Jason Turner Release under the BSD license, see "license.txt" for details. diff --git a/releasenotes.md b/releasenotes.md index c78376d..993f93d 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -1,6 +1,29 @@ Notes: ======= -Current Version: 5.7.1 +Current Version: 5.8.0 + +### Changes since 5.7.1 +* Make all parser iterator operations range checked +* Parse in-string eval statements once, not once for each execution +* Fix parsing of operators (ie 1<-1 now parses) +* Fix variable scoping for functors +* Exception reduction +* Various object lifetime fixes +* Add JSON support for load / save #207 +* Numeric overload resolution fixes #209 +* Fix long long #208 +* Add octal escapes in strings #211 +* Fixed sizing of binary literals #213 +* Added support for != with bool values #217 +* Various value assignment vector fixes +* Fixed broken hex escape sequences from @ChristianKaeser +* Multiply defined symbols fixes #232 @RaptorFactor +* Add add_class helper #233 @vrennert +* Cheatsheet fixes #235 @mlamby +* Fix parsing of strings inside of in-string eval statements +* Allow lower-case global keyword +* Enable thread-local on MSVC (should be significant performance boost) + ### Changes since 5.7.0 * Build time reduction diff --git a/samples/factory.cpp b/samples/factory.cpp new file mode 100644 index 0000000..ac8eb47 --- /dev/null +++ b/samples/factory.cpp @@ -0,0 +1,103 @@ +#include +#include + +class Entity +{ + public: + int width; + int height; + int x; + int y; + std::string name; + + std::function updater; + + Entity(const int t_width, const int t_height, const int t_x, const int t_y, std::string t_name) + : width(t_width), height(t_height), x(t_x), y(t_y), name(std::move(t_name)) + { + } +}; + +class Factory +{ + public: + // we may as well pass the parameters for the entity to the factory method, this does the initialization + // in one step. + Entity *make_entity(const int width, const int height, const int x, const int y, const std::string &name) + { + auto entity = entities.insert({name, Entity{width, height, x, y, name}}); + return &(entity.first->second); + } + + Entity *get_entity(const std::string &name) + { + return &entities.at(name); + } + + + // loop over all entities and all their updater function (if it exists) + void update_entities() + { + for (auto &entity : entities) + { + if (entity.second.updater) { + entity.second.updater(entity.second); + } + } + } + + + private: + // we cannot store the entities in a std::vector if we want to return a pointer to them, + // because a vector automatically resizing itself can invalidate the pointer that was returned. + // using a map guarantees that the memory assigned to the entity will never change, plus + // lets us easily look up an entity by name + std::map entities; +}; + +int main() +{ + chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); + + chai.add(chaiscript::fun(&Entity::width), "width"); + chai.add(chaiscript::fun(&Entity::height), "height"); + chai.add(chaiscript::fun(&Entity::x), "x"); + chai.add(chaiscript::fun(&Entity::y), "y"); + chai.add(chaiscript::fun(&Entity::name), "name"); + chai.add(chaiscript::fun(&Entity::updater), "updater"); + chai.add(chaiscript::user_type(), "Entity"); // this isn't strictly necessary but makes error messages nicer + + chai.add(chaiscript::fun(&Factory::make_entity), "make_entity"); + chai.add(chaiscript::fun(&Factory::get_entity), "get_entity"); + chai.add(chaiscript::fun(&Factory::update_entities), "update_entities"); + chai.add(chaiscript::user_type(), "Factory"); // this isn't strictly necessary but makes error messages nicer + + + Factory f; + chai.add(chaiscript::var(&f), "f"); + + std::string script = R""( + f.make_entity(10,10,1,1,"entity1").updater = fun(e){ e.x += 1; e.y += 1 }; + f.make_entity(10,10,10,10,"entity2").updater = fun(e){ e.x += 2; e.y += 2 }; + f.make_entity(10,10,20,20,"entity3"); + + print(f.get_entity("entity1").x == 1) + print(f.get_entity("entity2").x == 10) + print(f.get_entity("entity3").x == 20) + + f.update_entities(); // this runs the function objects we set in the previous lines + // we should now see the updated values + + print(f.get_entity("entity1").x == 2) + print(f.get_entity("entity2").x == 12) + print(f.get_entity("entity3").x == 20) // this one has no updater, so it stays the same + )""; + + + chai.eval(script); + + + +} + + diff --git a/samples/fun_call_performance.cpp b/samples/fun_call_performance.cpp index a416cc2..0ccc08d 100644 --- a/samples/fun_call_performance.cpp +++ b/samples/fun_call_performance.cpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #include @@ -316,7 +316,7 @@ int main(int argc, char *argv[]) ////for (int i = 0; i < 1000; i++) ////{ - //// chai.eval("puts(helloWorld(\"Bob12345\"));"); + //// chai.eval("puts(helloWorld(\"Bob12345\"));"); ////} //chai.eval_file("E:\\C++\\ChaiScript - 5.4.0\\samples\forx.chai"); diff --git a/samples/test_num_exceptions.cpp b/samples/test_num_exceptions.cpp new file mode 100644 index 0000000..3a35fb7 --- /dev/null +++ b/samples/test_num_exceptions.cpp @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +int main( int /*argc*/ , char * /*argv*/[] ) +{ + chaiscript::ChaiScript ch( chaiscript::Std_Lib::library( ) ); + + + try + { + static const char script[ ] = + R""( + + class Rectangle + { + def Rectangle() { } + } + + var rect = Rectangle( ); + + )""; + + + ch.eval( script ); + } + catch ( const std::exception &e ) + { + printf( " >>> Exception thrown: %s \n" , e.what( ) ); + } + + return 1; +} diff --git a/src/main.cpp b/src/main.cpp index 372adf7..7f203cd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #include @@ -66,7 +66,7 @@ std::vector default_search_paths() #ifdef CHAISCRIPT_WINDOWS // force no unicode CHAR path[4096]; - int size = GetModuleFileNameA(0, path, sizeof(path)-1); + int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1); std::string exepath(path, size); @@ -344,17 +344,16 @@ int main(int argc, char *argv[]) mode = eFile; } - chaiscript::Boxed_Value val ; try { switch ( mode ) { case eInteractive: interactive(chai); break; case eCommand: - val = chai.eval(arg); + chai.eval(arg); break; case eFile: - val = chai.eval_file(arg); + chai.eval_file(arg); } } catch (const chaiscript::exception::eval_error &ee) { diff --git a/src/stl_extra.cpp b/src/stl_extra.cpp index e391b18..ec38db7 100644 --- a/src/stl_extra.cpp +++ b/src/stl_extra.cpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2015, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #include diff --git a/unittests/array_access.chai b/unittests/array_access.chai new file mode 100644 index 0000000..7600492 --- /dev/null +++ b/unittests/array_access.chai @@ -0,0 +1,3 @@ +var v = [[[15]]] + +assert_true(v[0][0][0] == 15) diff --git a/unittests/bool_comparisons.chai b/unittests/bool_comparisons.chai new file mode 100644 index 0000000..6fdea7a --- /dev/null +++ b/unittests/bool_comparisons.chai @@ -0,0 +1,12 @@ + +assert_true(true == true) +assert_false(true == false) +assert_true(true != false) +assert_true(false != true) +assert_true(false || true) +assert_true(true || false) +assert_false(true && false) +assert_false(false && true) +assert_true(!false) +assert_false(!true) + diff --git a/unittests/boxed_cast_test.cpp b/unittests/boxed_cast_test.cpp index e575649..1e92874 100644 --- a/unittests/boxed_cast_test.cpp +++ b/unittests/boxed_cast_test.cpp @@ -13,9 +13,9 @@ bool run_test_type_conversion(const Boxed_Value &bv, bool expectedpass) try { To ret = chaiscript::boxed_cast(bv); use(ret); - } catch (const chaiscript::exception::bad_boxed_cast &/*e*/) { + } catch (const chaiscript::exception::bad_boxed_cast &e) { if (expectedpass) { -// std::cerr << "Failure in run_test_type_conversion: " << e.what() << '\n'; + std::cerr << "Failure in run_test_type_conversion: " << e.what() << '\n'; return false; } else { return true; diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index c856e9a..b27db07 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -2,6 +2,7 @@ // caught in other cpp files if chaiscript causes them #include +#include #ifdef CHAISCRIPT_MSVC #pragma warning(push) @@ -396,7 +397,7 @@ class Short_Comparison_Test { public: Short_Comparison_Test() : value_(5) {} - short get_value() { return value_; } + short get_value() const { return value_; } short value_; }; @@ -518,6 +519,65 @@ TEST_CASE("Utility_Test utility class wrapper") } +enum Utility_Test_Numbers +{ + ONE, + TWO, + THREE +}; + +void do_something_with_enum_vector(const std::vector &v) +{ + CHECK(v.size() == 3); + CHECK(v[0] == ONE); + CHECK(v[1] == THREE); + CHECK(v[2] == TWO); +} + +TEST_CASE("Utility_Test utility class wrapper for enum") +{ + + chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module()); + + using namespace chaiscript; + + chaiscript::utility::add_class(*m, + "Utility_Test_Numbers", + { { ONE, "ONE" }, + { TWO, "TWO" }, + { THREE, "THREE" } + + } + ); + + + chaiscript::ChaiScript chai; + chai.add(m); + + CHECK(chai.eval("ONE ") == 0); + CHECK(chai.eval("TWO ") == 1); + CHECK(chai.eval("THREE ") == 2); + + CHECK(chai.eval("ONE == 0")); + + chai.add(chaiscript::fun(&do_something_with_enum_vector), "do_something_with_enum_vector"); + chai.add(chaiscript::vector_conversion>()); + CHECK_NOTHROW(chai.eval("var a = [ONE, TWO, THREE]")); + CHECK_NOTHROW(chai.eval("do_something_with_enum_vector([ONE, THREE, TWO])")); + CHECK_NOTHROW(chai.eval("[ONE]")); + + const auto v = chai.eval>("a"); + CHECK(v.size() == 3); + CHECK(v.at(1) == TWO); + + CHECK(chai.eval("ONE == ONE")); + CHECK(chai.eval("ONE != TWO")); + CHECK_NOTHROW(chai.eval("var o = ONE; o = TWO")); + + +} + + ////// Object copy count test class Object_Copy_Count_Test @@ -743,3 +803,139 @@ TEST_CASE("Test Derived->Base with non-polymorphic classes") chai.add(chaiscript::fun(&myfunction), "myfunction"); CHECK(chai.eval("myfunction(d)") == 2); } + + +struct TestCppVariableScope +{ + void print() + { + std::cout << "Printed" << std::endl; + } +}; + +TEST_CASE("Variable Scope When Calling From C++") +{ + chaiscript::ChaiScript chai; + chai.add(chaiscript::user_type(), "Test"); + chai.add(chaiscript::constructor(), "Test"); + chai.add(chaiscript::fun(&TestCppVariableScope::print), "print"); + chai.eval(R"(var t := Test(); + + def func() + { + t.print(); + } + + )"); + + CHECK_THROWS(chai.eval("func()")); + + chai.eval("dump_object(t)"); + + auto func = chai.eval>("func"); + CHECK_THROWS(func()); +} + +TEST_CASE("Variable Scope When Calling From C++ 2") +{ + chaiscript::ChaiScript chai; + chai.eval("var obj = 2;"); + auto func = chai.eval>("fun(){ return obj; }"); + CHECK_THROWS(func()); +} + +void ulonglong(unsigned long long i) { + std::cout << i << '\n'; +} + + +void longlong(long long i) { + std::cout << i << '\n'; +} + +TEST_CASE("Test long long dispatch") +{ + chaiscript::ChaiScript chai; + chai.add(chaiscript::fun(&longlong), "longlong"); + chai.add(chaiscript::fun(&ulonglong), "ulonglong"); + chai.eval("longlong(15)"); + chai.eval("ulonglong(15)"); +} + + +struct Returned_Converted_Config +{ + int num_iterations; + int something_else; + std::string a_string; + std::function a_function; +}; + + + +TEST_CASE("Return of converted type from script") +{ + chaiscript::ChaiScript chai; + + chai.add(chaiscript::constructor(), "Returned_Converted_Config"); + chai.add(chaiscript::fun(&Returned_Converted_Config::num_iterations), "num_iterations"); + chai.add(chaiscript::fun(&Returned_Converted_Config::something_else), "something_else"); + chai.add(chaiscript::fun(&Returned_Converted_Config::a_string), "a_string"); + chai.add(chaiscript::fun(&Returned_Converted_Config::a_function), "a_function"); + chai.add(chaiscript::vector_conversion>()); + + auto c = chai.eval>(R"( + var c = Returned_Converted_Config(); + + c.num_iterations = 5; + c.something_else = c.num_iterations * 2; + c.a_string = "string"; + c.a_function = fun(s) { s.size(); } + + print("making vector"); + var v = []; + print("adding config item"); + v.push_back_ref(c); + print("returning vector"); + v; + + )"); + + + std::cout << typeid(decltype(c)).name() << std::endl; + + std::cout << "Info: " << c.size() << " " << &c[0] << std::endl; + + std::cout << "num_iterations " << c[0].num_iterations << '\n' + << "something_else " << c[0].something_else << '\n' + << "a_string " << c[0].a_string << '\n' + << "a_function " << c[0].a_function("bob") << '\n'; + + chai.add(chaiscript::user_type(), "Returned_Converted_Config"); +} + + +int get_value_a(const std::map &t_m) +{ + return t_m.at("a"); +} + + +TEST_CASE("Map conversions") +{ + chaiscript::ChaiScript chai; + chai.add(chaiscript::map_conversion>()); + chai.add(chaiscript::fun(&get_value_a), "get_value_a"); + + const auto c = chai.eval(R"( + var m = ["a": 42]; + get_value_a(m); + )"); + + CHECK(c == 42); + +} + + + + diff --git a/unittests/dynamic_object_dynamic_attrs_explicit.chai b/unittests/dynamic_object_dynamic_attrs_explicit.chai new file mode 100644 index 0000000..b37dc0f --- /dev/null +++ b/unittests/dynamic_object_dynamic_attrs_explicit.chai @@ -0,0 +1,14 @@ + +class MyClass { + def MyClass() + { + this.set_explicit(true); + } +}; + +var o = MyClass(); + +assert_true(o.is_explicit()); + +assert_throws("error", fun[o](){o.x = 2}) + diff --git a/unittests/fuzzy_tests-2015-07-16.tar.bz2 b/unittests/fuzzy_tests-2015-07-16.tar.bz2 index d150daa..9768b8e 100644 Binary files a/unittests/fuzzy_tests-2015-07-16.tar.bz2 and b/unittests/fuzzy_tests-2015-07-16.tar.bz2 differ diff --git a/unittests/global_in_script.chai b/unittests/global_in_script.chai index 951a910..3d94c7a 100644 --- a/unittests/global_in_script.chai +++ b/unittests/global_in_script.chai @@ -8,3 +8,17 @@ def myFun() } myFun(); + + +def myFun2() +{ + assert_equal(j, 7) +} + +set_global(7, "j") + +myFun2(); + +set_global(5, "j") + +myFun(); diff --git a/unittests/global_lcase.chai b/unittests/global_lcase.chai new file mode 100644 index 0000000..7f8959a --- /dev/null +++ b/unittests/global_lcase.chai @@ -0,0 +1,18 @@ +// Test global + +global g = 3; +assert_true(g == 3); + +var v := g; +assert_true(v == 3); + +global g = 2; +assert_true(g == 2); +assert_true(v == 2); + +def f() { + assert_true(g == 2); +} + +f(); + diff --git a/unittests/hex_escapes.chai b/unittests/hex_escapes.chai new file mode 100644 index 0000000..fdd0a8a --- /dev/null +++ b/unittests/hex_escapes.chai @@ -0,0 +1,7 @@ + +assert_equal("\x39", "9") +assert_equal("\x39ec", "9ec") +assert_equal("\x39g", "9g") +assert_equal("b\x39g", "b9g") +assert_equal("\x39\x38g", "98g") + diff --git a/unittests/instring_eval_with_string.chai b/unittests/instring_eval_with_string.chai new file mode 100644 index 0000000..9cb1091 --- /dev/null +++ b/unittests/instring_eval_with_string.chai @@ -0,0 +1 @@ +assert_equal("a string", "${"a string"}") diff --git a/unittests/integer_literal_test.cpp b/unittests/integer_literal_test.cpp index ecaff8a..58e954a 100644 --- a/unittests/integer_literal_test.cpp +++ b/unittests/integer_literal_test.cpp @@ -5,9 +5,10 @@ template bool test_literal(T val, const std::string &str) { + std::cout << "Comparing : " << val; chaiscript::ChaiScript chai; T val2 = chai.eval(str); - std::cout << "Comparing : " << val << " " << val2 << '\n'; + std::cout << " " << val2 << '\n'; return val == val2; } @@ -74,6 +75,39 @@ int main() && TEST_LITERAL(177777777777777777) && TEST_LITERAL(1777777777777777777) + && test_literal(0xF, "0b1111") + && test_literal(0xFF, "0b11111111") + && test_literal(0xFFF, "0b111111111111") + && test_literal(0xFFFF, "0b1111111111111111") + && test_literal(0xFFFFF, "0b11111111111111111111") + && test_literal(0xFFFFFF, "0b111111111111111111111111") + && test_literal(0xFFFFFFF, "0b1111111111111111111111111111") + && test_literal(0xFFFFFFFF, "0b11111111111111111111111111111111") + && test_literal(0xFFFFFFFFF, "0b111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFF, "0b1111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFFFF, "0b1111111111111111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111111111111") + && test_literal(0xFFFFFFFFFFFFFFFF, "0b1111111111111111111111111111111111111111111111111111111111111111") + + && test_literal(0x7, "0b111") + && test_literal(0x7F, "0b1111111") + && test_literal(0x7FF, "0b11111111111") + && test_literal(0x7FFF, "0b111111111111111") + && test_literal(0x7FFFF, "0b1111111111111111111") + && test_literal(0x7FFFFF, "0b11111111111111111111111") + && test_literal(0x7FFFFFF, "0b111111111111111111111111111") + && test_literal(0x7FFFFFFF, "0b1111111111111111111111111111111") + && test_literal(0x7FFFFFFFF, "0b11111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFF, "0b111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFF, "0b1111111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFFFFF, "0b1111111111111111111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111111111111111") + && test_literal(0x7FFFFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111111111111111") ) { return EXIT_SUCCESS; diff --git a/unittests/json_1.chai b/unittests/json_1.chai new file mode 100644 index 0000000..63277b3 --- /dev/null +++ b/unittests/json_1.chai @@ -0,0 +1,3 @@ + + +assert_true(from_json("null").is_var_null()) diff --git a/unittests/json_10.chai b/unittests/json_10.chai new file mode 100644 index 0000000..f19c7cf --- /dev/null +++ b/unittests/json_10.chai @@ -0,0 +1 @@ +assert_equal(from_json("\"This is a\\n\\nMultiline string\""), "This is a\n\nMultiline string") diff --git a/unittests/json_11.chai b/unittests/json_11.chai new file mode 100644 index 0000000..f11db38 --- /dev/null +++ b/unittests/json_11.chai @@ -0,0 +1,14 @@ +assert_equal(from_json( +"{\n" + +" \"T1\" : \"Value With a Quote : \\\"\",\n" + +" \"T2\" : \"Value With a Rev Solidus : \\/\",\n" + +" \"T3\" : \"Value with a Solidus : \\\\\",\n" + +" \"T4\" : \"Value with a Backspace : \\b\",\n" + +" \"T5\" : \"Value with a Formfeed : \\f\",\n" + +" \"T6\" : \"Value with a Newline : \\n\",\n" + +" \"T7\" : \"Value with a Carriage Return : \\r\",\n" + +" \"T8\" : \"Value with a Horizontal Tab : \\t\"\n" + +"}"), [ "T1" : "Value With a Quote : \"", "T2" : "Value With a Rev Solidus : /", "T3" : "Value with a Solidus : \\", "T4" : "Value with a Backspace : \b", "T5" : "Value with a Formfeed : \f", "T6" : "Value with a Newline : \n", "T7" : "Value with a Carriage Return : \r", "T8" : "Value with a Horizontal Tab : \t" ]); + + + diff --git a/unittests/json_12.chai b/unittests/json_12.chai new file mode 100644 index 0000000..a93211d --- /dev/null +++ b/unittests/json_12.chai @@ -0,0 +1 @@ +assert_equal(from_json("\"\""), "") diff --git a/unittests/json_13.chai b/unittests/json_13.chai new file mode 100644 index 0000000..3f9fa00 --- /dev/null +++ b/unittests/json_13.chai @@ -0,0 +1 @@ +assert_equal(from_json("1.20E+2"), 1.20e2) diff --git a/unittests/json_2.chai b/unittests/json_2.chai new file mode 100644 index 0000000..0f779bd --- /dev/null +++ b/unittests/json_2.chai @@ -0,0 +1,3 @@ + +assert_true(from_json("true")) + diff --git a/unittests/json_3.chai b/unittests/json_3.chai new file mode 100644 index 0000000..d3f222e --- /dev/null +++ b/unittests/json_3.chai @@ -0,0 +1 @@ +assert_equal(from_json("100"), 100) diff --git a/unittests/json_4.chai b/unittests/json_4.chai new file mode 100644 index 0000000..22d388c --- /dev/null +++ b/unittests/json_4.chai @@ -0,0 +1 @@ +assert_equal(from_json("1.234"), 1.234) diff --git a/unittests/json_5.chai b/unittests/json_5.chai new file mode 100644 index 0000000..9ad2ca2 --- /dev/null +++ b/unittests/json_5.chai @@ -0,0 +1 @@ +assert_equal(from_json("\"StringTest\""), "StringTest") diff --git a/unittests/json_6.chai b/unittests/json_6.chai new file mode 100644 index 0000000..8c33bff --- /dev/null +++ b/unittests/json_6.chai @@ -0,0 +1 @@ +assert_equal(from_json("{}"), Map()) diff --git a/unittests/json_7.chai b/unittests/json_7.chai new file mode 100644 index 0000000..b2c6efa --- /dev/null +++ b/unittests/json_7.chai @@ -0,0 +1,4 @@ +assert_equal(from_json("\n" + +"{\n" + +" \"Key\" : \"Value\"\n" + +"}\n"), ["Key":"Value"]) diff --git a/unittests/json_8.chai b/unittests/json_8.chai new file mode 100644 index 0000000..104c8bc --- /dev/null +++ b/unittests/json_8.chai @@ -0,0 +1 @@ +assert_equal(from_json("[]"), []) diff --git a/unittests/json_9.chai b/unittests/json_9.chai new file mode 100644 index 0000000..1512738 --- /dev/null +++ b/unittests/json_9.chai @@ -0,0 +1,2 @@ +assert_equal(from_json("[1,2,3]"), [1,2,3]) + diff --git a/unittests/json_roundtrip.chai b/unittests/json_roundtrip.chai new file mode 100644 index 0000000..94682e5 --- /dev/null +++ b/unittests/json_roundtrip.chai @@ -0,0 +1,6 @@ + +var m = ["a" : 1, "b" : [ 1, 2, 3 ], "c" : [1, "a string", ["d" : 15.4]]] + +assert_equal(from_json(to_json(m)), m) + + diff --git a/unittests/list_push_back.chai b/unittests/list_push_back.chai index 19d17e1..35b8fa6 100644 --- a/unittests/list_push_back.chai +++ b/unittests/list_push_back.chai @@ -6,3 +6,7 @@ x.push_back("A") assert_equal(3, x.front()); assert_equal("A", x.back()); + + + + diff --git a/unittests/list_push_front.chai b/unittests/list_push_front.chai index fe31821..b07c273 100644 --- a/unittests/list_push_front.chai +++ b/unittests/list_push_front.chai @@ -6,3 +6,8 @@ x.push_front("A") assert_equal("A", x.front()); assert_equal(3, x.back()); + +// push_back newly constructed return value that's non-copyable +x.push_front(async(fun(){})) + + diff --git a/unittests/map.chai b/unittests/map.chai index b2901ce..eb7a133 100644 --- a/unittests/map.chai +++ b/unittests/map.chai @@ -1,29 +1,41 @@ -assert_equal([true, false, true], map([1,2,3], odd)) +// Map function + +{ + assert_equal([true, false, true], map([1,2,3], odd)) + + var v = [1, 2, 3]; + var y = map(v, fun(s) { s*2; }); + y[0] = 1; + assert_equal(1, y[0]); +} +// Map objects -var m = ["a":1, "b":2]; +{ + var m = ["a":1, "b":2]; -assert_equal(1, m.count("a")) -assert_equal(0, m.count("c")) -assert_equal(1, m.erase("a")) -assert_equal(1, m.size()) -assert_equal(0, m.erase("a")) + assert_equal(1, m.count("a")) + assert_equal(0, m.count("c")) + assert_equal(1, m.erase("a")) + assert_equal(1, m.size()) + assert_equal(0, m.erase("a")) -assert_equal(1, m.size()); + assert_equal(1, m.size()); -var m2 = ["c":3, "b":4] -m.insert(m2); + var m2 = ["c":3, "b":4] + m.insert(m2); -assert_equal(3, m["c"]) -// The inserted values do not overwrite the existing ones -assert_equal(2, m["b"]) -assert_equal(2, m.size()) + assert_equal(3, m["c"]) + // The inserted values do not overwrite the existing ones + assert_equal(2, m["b"]) + assert_equal(2, m.size()) -var v = "bob"; + var v = "bob"; -m.insert_ref(Map_Pair("d", v)) + m.insert_ref(Map_Pair("d", v)) -assert_equal("bob", m["d"]) -v = "bob2" -assert_equal("bob2", m["d"]) + assert_equal("bob", m["d"]) + v = "bob2" + assert_equal("bob2", m["d"]) +} diff --git a/unittests/move_async.chai b/unittests/move_async.chai new file mode 100644 index 0000000..7975a7c --- /dev/null +++ b/unittests/move_async.chai @@ -0,0 +1,17 @@ +load_module("stl_extra") + +auto x = List() +// push_back newly constructed return value that's non-copyable +x.push_front(async(fun(){})) + +// push_front newly constructed return value that's non-copyable +x.push_front(async(fun(){})) + + + +// push_back newly constructed return value that's non-copyable + +var v = [] +v.push_back(async(fun(){})) + + diff --git a/unittests/number_suffixes.chai b/unittests/number_suffixes.chai index 99ac03f..b8fd1f5 100644 --- a/unittests/number_suffixes.chai +++ b/unittests/number_suffixes.chai @@ -2,8 +2,8 @@ assert_equal(true, int_type.bare_equal(1.get_type_info())) assert_equal(true, unsigned_int_type.bare_equal(1u.get_type_info())) assert_equal(true, unsigned_long_type.bare_equal(1lu.get_type_info())) assert_equal(true, long_type.bare_equal(1l.get_type_info())) -assert_equal(true, int64_t_type.bare_equal(1ll.get_type_info())) -assert_equal(true, uint64_t_type.bare_equal(1ull.get_type_info())) +assert_equal(true, long_long_type.bare_equal(1ll.get_type_info())) +assert_equal(true, unsigned_long_long_type.bare_equal(1ull.get_type_info())) assert_equal(true, double_type.bare_equal(1.6.get_type_info())) assert_equal(true, float_type.bare_equal(1.6f.get_type_info())) diff --git a/unittests/octal_escapes.chai b/unittests/octal_escapes.chai new file mode 100644 index 0000000..a220d57 --- /dev/null +++ b/unittests/octal_escapes.chai @@ -0,0 +1,7 @@ + +assert_equal("\71", "9") +assert_equal("\071", "9") +assert_equal("\71a", "9a") +assert_equal("b\71a", "b9a") +assert_equal("\71\70a", "98a") + diff --git a/unittests/operator_overload3.chai b/unittests/operator_overload3.chai new file mode 100644 index 0000000..90b81cd --- /dev/null +++ b/unittests/operator_overload3.chai @@ -0,0 +1,7 @@ + + +def string::`/=`(double d) { this = "${this}/=${d}"; return this; } + +var s = "Hello World" +s /= 2 + diff --git a/unittests/operator_overload4.chai b/unittests/operator_overload4.chai new file mode 100644 index 0000000..832d984 --- /dev/null +++ b/unittests/operator_overload4.chai @@ -0,0 +1,6 @@ + + +def string::`*`(double d) { return "${this} * ${d}"; } + +"Hello World" * 2 + diff --git a/unittests/operator_parsing.chai b/unittests/operator_parsing.chai new file mode 100644 index 0000000..59d2314 --- /dev/null +++ b/unittests/operator_parsing.chai @@ -0,0 +1,4 @@ +assert_true(2>-1); +assert_false(3<-2); +assert_true(-1==-1); + diff --git a/unittests/order_of_operations.chai b/unittests/order_of_operations.chai new file mode 100644 index 0000000..c07ce79 --- /dev/null +++ b/unittests/order_of_operations.chai @@ -0,0 +1,7 @@ + + +var s = "" + +s += to_string(fun[s](){ s += "3"; 3}() % fun[s](){ s += "2"; 2}()); + +assert_equal(s, "321"); diff --git a/unittests/parser_test.chai b/unittests/parser_test.chai new file mode 100644 index 0000000..05c2014 --- /dev/null +++ b/unittests/parser_test.chai @@ -0,0 +1,10 @@ +auto p = parse("5 + 4"); + +try { + assert_equal(eval(p), 9) +} catch (e) { + print(e.pretty_print()); + assert_true(false); +} + + diff --git a/unittests/return_value_assignment.chai b/unittests/return_value_assignment.chai new file mode 100644 index 0000000..e6906e6 --- /dev/null +++ b/unittests/return_value_assignment.chai @@ -0,0 +1,16 @@ + +try { + eval("to_string(5) = \"some string\"") + assert_true(false) +} catch (e) { + print("Caught Error: " + e.what()); +} + + + +try { + eval("var v = [1,2,3]; v.size() = 3") + assert_true(false) +} catch (e) { + print("Caught Error: " + e.what()); +} diff --git a/unittests/switch_default_2.chai b/unittests/switch_default_2.chai new file mode 100644 index 0000000..5082ab6 --- /dev/null +++ b/unittests/switch_default_2.chai @@ -0,0 +1,18 @@ +var total = 0; + +switch(2) { + case (1) { + total += 1; + } + default { + total += 16; + } + case (3) { + total += 4; + } + case (4) { + total += 8; + } +} + +assert_equal(total, 28) diff --git a/unittests/vector_assignment.chai b/unittests/vector_assignment.chai new file mode 100644 index 0000000..8646105 --- /dev/null +++ b/unittests/vector_assignment.chai @@ -0,0 +1,18 @@ +var v = [] +v.push_back(3.4); +v.push_back(1); +v.push_back("bob"); + +assert_true(v[0] == 3.4) +assert_true(v[1] == 1) +assert_true(v[2] == "bob") + +v[0] = 2.9 +v[1] = 3 +v[2] = "tom" + +assert_true(v[0] == 2.9) +assert_true(v[1] == 3) +assert_true(v[2] == "tom") + + diff --git a/unittests/vector_assignment_3.chai b/unittests/vector_assignment_3.chai new file mode 100644 index 0000000..61d4b58 --- /dev/null +++ b/unittests/vector_assignment_3.chai @@ -0,0 +1,5 @@ + +var v = [] +v.push_back(int(1)); +v[0] = 3; + diff --git a/unittests/vector_push_back.chai b/unittests/vector_push_back.chai index eea34b7..148e7ff 100644 --- a/unittests/vector_push_back.chai +++ b/unittests/vector_push_back.chai @@ -11,3 +11,7 @@ auto uint16v = u16vector(); uint16v.push_back(1u); assert_equal(1, uint16v.front()); + + + +