Enable warnings as errors (#694)

* Enable warnings as errors
* Fix warnings
* upgrade catch
This commit is contained in:
Jason Turner 2026-04-28 20:11:15 -06:00 committed by GitHub
parent cd8dacccc6
commit 0b75a8be7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 27965 additions and 18617 deletions

View File

@ -155,7 +155,7 @@ else()
endif()
if(MSVC)
add_definitions(/W4 /w14545 /w34242 /w34254 /w34287 /w44263 /w44265 /w44296 /w44311 /w44826 /we4289 /w14546 /w14547 /w14549 /w14555 /w14619 /w14905 /w14906 /w14928)
add_definitions(/WX /W4 /w14545 /w34242 /w34254 /w34287 /w44263 /w44265 /w44296 /w44311 /w44826 /we4289 /w14546 /w14547 /w14549 /w14555 /w14619 /w14905 /w14906 /w14928)
if(MSVC_VERSION STREQUAL "1800")
# VS2013 doesn't have magic statics
@ -175,10 +175,10 @@ if(MSVC)
# how to workaround or fix the error. So I'm disabling it globally.
add_definitions(/wd4503)
else()
add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -Wno-noexcept-type -Wpedantic -Werror=return-type)
add_definitions(-Werror -Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -Wno-noexcept-type -Wpedantic -Werror=return-type)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command -Wno-unused-template -Wno-undef -Wno-double-promotion)
add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command -Wno-unused-template -Wno-undef -Wno-double-promotion -Wno-switch-default -Wno-nrvo -Wno-shadow-uncaptured-local -Wno-unsafe-buffer-usage-in-libc-call -Wno-c++20-extensions -Wno-unknown-warning-option -Wno-poison-system-directories -Wno-c++20-compat -Wno-c++17-compat)
else()
add_definitions(-Wnoexcept)
endif()
@ -390,8 +390,16 @@ if(BUILD_TESTING)
)
if(NOT UNIT_TEST_LIGHT)
add_library(catch2 STATIC unittests/catch_amalgamated.cpp)
if(NOT MSVC)
target_compile_options(catch2 PRIVATE -Wno-conversion -Wno-noexcept -Wno-maybe-uninitialized)
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
target_compile_options(catch2 PUBLIC -Wno-unknown-warning-option -Wno-covered-switch-default -Wno-disabled-macro-expansion -Wno-unsafe-buffer-usage -Wno-unused-macros)
endif()
add_executable(compiled_tests unittests/compiled_tests.cpp)
target_link_libraries(compiled_tests ${LIBS} ${CHAISCRIPT_LIBS})
target_link_libraries(compiled_tests catch2 ${LIBS} ${CHAISCRIPT_LIBS})
catch_discover_tests(compiled_tests TEST_PREFIX "compiled.")
add_executable(static_chaiscript_test unittests/static_chaiscript.cpp)
@ -403,7 +411,7 @@ if(BUILD_TESTING)
add_test(NAME Boxed_Cast_Test COMMAND boxed_cast_test)
add_executable(type_info_test unittests/type_info_test.cpp)
target_link_libraries(type_info_test ${LIBS})
target_link_libraries(type_info_test catch2 ${LIBS})
add_test(NAME Type_Info_Test COMMAND type_info_test)
add_executable(c_linkage_test unittests/c_linkage_test.cpp)

View File

@ -33,6 +33,13 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
[TEST_SUFFIX suffix]
[PROPERTIES name1 value1...]
[TEST_LIST var]
[REPORTER reporter]
[OUTPUT_DIR dir]
[OUTPUT_PREFIX prefix]
[OUTPUT_SUFFIX suffix]
[DISCOVERY_MODE <POST_BUILD|PRE_TEST>]
[SKIP_IS_FAILURE]
[ADD_TAGS_AS_LABELS]
)
``catch_discover_tests`` sets up a post-build command on the test executable
@ -90,86 +97,222 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
executable is being used in multiple calls to ``catch_discover_tests()``.
Note that this variable is only available in CTest.
``REPORTER reporter``
Use the specified reporter when running the test case. The reporter will
be passed to the Catch executable as ``--reporter reporter``.
``OUTPUT_DIR dir``
If specified, the parameter is passed along as
``--out dir/<test_name>`` to Catch executable. The actual file name is the
same as the test name. This should be used instead of
``EXTRA_ARGS --out foo`` to avoid race conditions writing the result output
when using parallel test execution.
``OUTPUT_PREFIX prefix``
May be used in conjunction with ``OUTPUT_DIR``.
If specified, ``prefix`` is added to each output file name, like so
``--out dir/prefix<test_name>``.
``OUTPUT_SUFFIX suffix``
May be used in conjunction with ``OUTPUT_DIR``.
If specified, ``suffix`` is added to each output file name, like so
``--out dir/<test_name>suffix``. This can be used to add a file extension to
the output e.g. ".xml".
``DL_PATHS path...``
Specifies paths that need to be set for the dynamic linker to find shared
libraries/DLLs when running the test executable (PATH/LD_LIBRARY_PATH respectively).
These paths will both be set when retrieving the list of test cases from the
test executable and when the tests are executed themselves. This requires
cmake/ctest >= 3.22.
``DL_FRAMEWORK_PATHS path...``
Specifies paths that need to be set for the dynamic linker to find libraries
packaged as frameworks on Apple platforms when running the test executable
(DYLD_FRAMEWORK_PATH). These paths will both be set when retrieving the list
of test cases from the test executable and when the tests are executed themselves.
This requires cmake/ctest >= 3.22.
``DISCOVERY_MODE mode``
Provides control over when ``catch_discover_tests`` performs test discovery.
By default, ``POST_BUILD`` sets up a post-build command to perform test discovery
at build time. In certain scenarios, like cross-compiling, this ``POST_BUILD``
behavior is not desirable. By contrast, ``PRE_TEST`` delays test discovery until
just prior to test execution. This way test discovery occurs in the target environment
where the test has a better chance at finding appropriate runtime dependencies.
``DISCOVERY_MODE`` defaults to the value of the
``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when
calling ``catch_discover_tests``. This provides a mechanism for globally selecting
a preferred test discovery behavior without having to modify each call site.
``SKIP_IS_FAILURE``
Disables skipped test detection.
``ADD_TAGS_AS_LABELS``
Adds all test tags as CTest labels.
#]=======================================================================]
#------------------------------------------------------------------------------
function(catch_discover_tests TARGET)
cmake_parse_arguments(
""
""
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST"
"TEST_SPEC;EXTRA_ARGS;PROPERTIES"
"SKIP_IS_FAILURE;ADD_TAGS_AS_LABELS"
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX;DISCOVERY_MODE"
"TEST_SPEC;EXTRA_ARGS;PROPERTIES;DL_PATHS;DL_FRAMEWORK_PATHS"
${ARGN}
)
if(${CMAKE_VERSION} VERSION_LESS "3.19")
message(FATAL_ERROR "This script requires JSON support from CMake version 3.19 or greater.")
endif()
if(NOT _WORKING_DIRECTORY)
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
endif()
if(NOT _TEST_LIST)
set(_TEST_LIST ${TARGET}_TESTS)
endif()
if(_DL_PATHS AND ${CMAKE_VERSION} VERSION_LESS "3.22.0")
message(FATAL_ERROR "The DL_PATHS option requires at least cmake 3.22")
endif()
if(_DL_FRAMEWORK_PATHS AND ${CMAKE_VERSION} VERSION_LESS "3.22.0")
message(FATAL_ERROR "The DL_FRAMEWORK_PATHS option requires at least cmake 3.22")
endif()
if(NOT _DISCOVERY_MODE)
if(NOT CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE)
set(CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD")
endif()
set(_DISCOVERY_MODE ${CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE})
endif()
if(NOT _DISCOVERY_MODE MATCHES "^(POST_BUILD|PRE_TEST)$")
message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}")
endif()
## Generate a unique name based on the extra arguments
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX}")
string(SUBSTRING ${args_hash} 0 7 args_hash)
# Define rule to generate test list for aforementioned test executable
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-${args_hash}")
set(ctest_include_file "${ctest_file_base}_include.cmake")
set(ctest_tests_file "${ctest_file_base}_tests.cmake")
get_property(crosscompiling_emulator
TARGET ${TARGET}
PROPERTY CROSSCOMPILING_EMULATOR
)
add_custom_command(
TARGET ${TARGET} POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND "${CMAKE_COMMAND}"
-D "TEST_TARGET=${TARGET}"
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-D "TEST_SPEC=${_TEST_SPEC}"
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_PREFIX=${_TEST_PREFIX}"
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
-D "TEST_LIST=${_TEST_LIST}"
-D "CTEST_FILE=${ctest_tests_file}"
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
VERBATIM
)
if(NOT _SKIP_IS_FAILURE)
set(_PROPERTIES ${_PROPERTIES} SKIP_RETURN_CODE 4)
endif()
file(WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
"endif()\n"
)
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
# Add discovered tests to directory TEST_INCLUDE_FILES
set_property(DIRECTORY
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
if(_DISCOVERY_MODE STREQUAL "POST_BUILD")
add_custom_command(
TARGET ${TARGET} POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND "${CMAKE_COMMAND}"
-D "TEST_TARGET=${TARGET}"
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-D "TEST_SPEC=${_TEST_SPEC}"
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_PREFIX=${_TEST_PREFIX}"
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
-D "TEST_LIST=${_TEST_LIST}"
-D "TEST_REPORTER=${_REPORTER}"
-D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
-D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}"
-D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}"
-D "TEST_DL_PATHS=${_DL_PATHS}"
-D "TEST_DL_FRAMEWORK_PATHS=${_DL_FRAMEWORK_PATHS}"
-D "CTEST_FILE=${ctest_tests_file}"
-D "ADD_TAGS_AS_LABELS=${_ADD_TAGS_AS_LABELS}"
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
VERBATIM
)
else()
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
if (NOT ${test_include_file_set})
set_property(DIRECTORY
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
file(WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
"endif()\n"
)
elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST")
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL
PROPERTY GENERATOR_IS_MULTI_CONFIG
)
if(GENERATOR_IS_MULTI_CONFIG)
set(ctest_tests_file "${ctest_file_base}_tests-$<CONFIG>.cmake")
endif()
string(CONCAT ctest_include_content
"if(EXISTS \"$<TARGET_FILE:${TARGET}>\")" "\n"
" if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n"
" NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$<TARGET_FILE:${TARGET}>\" OR\n"
" NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n"
" include(\"${_CATCH_DISCOVER_TESTS_SCRIPT}\")" "\n"
" catch_discover_tests_impl(" "\n"
" TEST_EXECUTABLE" " [==[" "$<TARGET_FILE:${TARGET}>" "]==]" "\n"
" TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n"
" TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n"
" TEST_SPEC" " [==[" "${_TEST_SPEC}" "]==]" "\n"
" TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n"
" TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n"
" TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n"
" TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n"
" TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n"
" TEST_REPORTER" " [==[" "${_REPORTER}" "]==]" "\n"
" TEST_OUTPUT_DIR" " [==[" "${_OUTPUT_DIR}" "]==]" "\n"
" TEST_OUTPUT_PREFIX" " [==[" "${_OUTPUT_PREFIX}" "]==]" "\n"
" TEST_OUTPUT_SUFFIX" " [==[" "${_OUTPUT_SUFFIX}" "]==]" "\n"
" CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n"
" TEST_DL_PATHS" " [==[" "${_DL_PATHS}" "]==]" "\n"
" TEST_DL_FRAMEWORK_PATHS" " [==[" "${_DL_FRAMEWORK_PATHS}" "]==]" "\n"
" ADD_TAGS_AS_LABELS" " [==[" "${_ADD_TAGS_AS_LABELS}" "]==]" "\n"
" )" "\n"
" endif()" "\n"
" include(\"${ctest_tests_file}\")" "\n"
"else()" "\n"
" add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n"
"endif()" "\n"
)
if(GENERATOR_IS_MULTI_CONFIG)
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $<CONFIG:${_config}>)
endforeach()
string(CONCAT ctest_include_multi_content
"if(NOT CTEST_CONFIGURATION_TYPE)" "\n"
" message(\"No configuration for testing specified, use '-C <cfg>'.\")" "\n"
"else()" "\n"
" include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")" "\n"
"endif()" "\n"
)
file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_multi_content}")
else()
message(FATAL_ERROR
"Cannot set more than one TEST_INCLUDE_FILE"
)
file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}")
file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")")
endif()
endif()
# Add discovered tests to directory TEST_INCLUDE_FILES
set_property(DIRECTORY
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
)
endfunction()
###############################################################################
set(_CATCH_DISCOVER_TESTS_SCRIPT
${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
CACHE INTERNAL "Catch2 full path to CatchAddTests.cmake helper file"
)

View File

@ -1,18 +1,12 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
set(prefix "${TEST_PREFIX}")
set(suffix "${TEST_SUFFIX}")
set(spec ${TEST_SPEC})
set(extra_args ${TEST_EXTRA_ARGS})
set(properties ${TEST_PROPERTIES})
set(script)
set(suite)
set(tests)
function(add_command NAME)
set(_args "")
foreach(_arg ${ARGN})
# use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
math(EXPR _last_arg ${ARGC}-1)
foreach(_n RANGE 1 ${_last_arg})
set(_arg "${ARGV${_n}}")
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
else()
@ -22,55 +16,239 @@ function(add_command NAME)
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
endfunction()
# Run test executable to get list of available tests
if(NOT EXISTS "${TEST_EXECUTABLE}")
message(FATAL_ERROR
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
function(catch_discover_tests_impl)
cmake_parse_arguments(
""
""
"TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_OUTPUT_DIR;TEST_OUTPUT_PREFIX;TEST_OUTPUT_SUFFIX;TEST_PREFIX;TEST_REPORTER;TEST_SPEC;TEST_SUFFIX;TEST_LIST;CTEST_FILE"
"TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR;TEST_DL_PATHS;TEST_DL_FRAMEWORK_PATHS;ADD_TAGS_AS_LABELS"
${ARGN}
)
set(add_tags "${_ADD_TAGS_AS_LABELS}")
set(prefix "${_TEST_PREFIX}")
set(suffix "${_TEST_SUFFIX}")
set(spec ${_TEST_SPEC})
set(extra_args ${_TEST_EXTRA_ARGS})
set(properties ${_TEST_PROPERTIES})
set(reporter ${_TEST_REPORTER})
set(output_dir ${_TEST_OUTPUT_DIR})
set(output_prefix ${_TEST_OUTPUT_PREFIX})
set(output_suffix ${_TEST_OUTPUT_SUFFIX})
set(dl_paths ${_TEST_DL_PATHS})
set(dl_framework_paths ${_TEST_DL_FRAMEWORK_PATHS})
set(environment_modifications "")
set(script)
set(suite)
set(tests)
if(WIN32)
set(dl_paths_variable_name PATH)
elseif(APPLE)
set(dl_paths_variable_name DYLD_LIBRARY_PATH)
else()
set(dl_paths_variable_name LD_LIBRARY_PATH)
endif()
# Run test executable to get list of available tests
if(NOT EXISTS "${_TEST_EXECUTABLE}")
message(FATAL_ERROR
"Specified test executable '${_TEST_EXECUTABLE}' does not exist"
)
endif()
if(dl_paths)
cmake_path(CONVERT "$ENV{${dl_paths_variable_name}}" TO_NATIVE_PATH_LIST env_dl_paths)
list(PREPEND env_dl_paths "${dl_paths}")
cmake_path(CONVERT "${env_dl_paths}" TO_NATIVE_PATH_LIST paths)
set(ENV{${dl_paths_variable_name}} "${paths}")
endif()
if(APPLE AND dl_framework_paths)
cmake_path(CONVERT "$ENV{DYLD_FRAMEWORK_PATH}" TO_NATIVE_PATH_LIST env_dl_framework_paths)
list(PREPEND env_dl_framework_paths "${dl_framework_paths}")
cmake_path(CONVERT "${env_dl_framework_paths}" TO_NATIVE_PATH_LIST paths)
set(ENV{DYLD_FRAMEWORK_PATH} "${paths}")
endif()
execute_process(
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} --list-tests --reporter json
OUTPUT_VARIABLE listing_output
RESULT_VARIABLE result
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
)
if(NOT ${result} EQUAL 0)
message(FATAL_ERROR
"Error listing tests from executable '${_TEST_EXECUTABLE}':\n"
" Result: ${result}\n"
" Output: ${listing_output}\n"
)
endif()
# Prepare reporter
if(reporter)
set(reporter_arg "--reporter ${reporter}")
# Run test executable to check whether reporter is available
# note that the use of --list-reporters is not the important part,
# we only want to check whether the execution succeeds with ${reporter_arg}
execute_process(
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} ${reporter_arg} --list-reporters
OUTPUT_VARIABLE reporter_check_output
RESULT_VARIABLE reporter_check_result
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
)
if(${reporter_check_result} EQUAL 255)
message(FATAL_ERROR
"\"${reporter}\" is not a valid reporter!\n"
)
elseif(NOT ${reporter_check_result} EQUAL 0)
message(FATAL_ERROR
"Error checking for reporter in test executable '${_TEST_EXECUTABLE}':\n"
" Result: ${reporter_check_result}\n"
" Output: ${reporter_check_output}\n"
)
endif()
endif()
# Prepare output dir
if(output_dir AND NOT IS_ABSOLUTE ${output_dir})
set(output_dir "${_TEST_WORKING_DIR}/${output_dir}")
if(NOT EXISTS ${output_dir})
file(MAKE_DIRECTORY ${output_dir})
endif()
endif()
if(dl_paths)
foreach(path ${dl_paths})
cmake_path(NATIVE_PATH path native_path)
list(PREPEND environment_modifications "${dl_paths_variable_name}=path_list_prepend:${native_path}")
endforeach()
endif()
if(APPLE AND dl_framework_paths)
foreach(path ${dl_framework_paths})
cmake_path(NATIVE_PATH path native_path)
list(PREPEND environment_modifications "DYLD_FRAMEWORK_PATH=path_list_prepend:${native_path}")
endforeach()
endif()
# Parse JSON output for list of tests/class names/tags
string(JSON version GET "${listing_output}" "version")
if(NOT version STREQUAL "1")
message(FATAL_ERROR "Unsupported catch output version: '${version}'")
endif()
# Speed-up reparsing by cutting away unneeded parts of JSON.
string(JSON test_listing GET "${listing_output}" "listings" "tests")
string(JSON num_tests LENGTH "${test_listing}")
# Exit early if no tests are detected
if(num_tests STREQUAL "0")
file(WRITE "${_CTEST_FILE}" "")
return()
endif()
# CMake's foreach-RANGE is inclusive, so we have to subtract 1
math(EXPR num_tests "${num_tests} - 1")
foreach(idx RANGE ${num_tests})
string(JSON single_test GET ${test_listing} ${idx})
string(JSON test_tags GET "${single_test}" "tags")
string(JSON plain_name GET "${single_test}" "name")
# Escape characters in test case names that would be parsed by Catch2
# Note that the \ escaping must happen FIRST! Do not change the order.
set(escaped_name "${plain_name}")
foreach(char \\ , [ ] ;)
string(REPLACE ${char} "\\${char}" escaped_name "${escaped_name}")
endforeach(char)
# ...add output dir
if(output_dir)
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" escaped_name_clean "${escaped_name}")
set(output_dir_arg "--out ${output_dir}/${output_prefix}${escaped_name_clean}${output_suffix}")
endif()
# ...and add to script
add_command(add_test
"${prefix}${plain_name}${suffix}"
${_TEST_EXECUTOR}
"${_TEST_EXECUTABLE}"
"${escaped_name}"
${extra_args}
"${reporter_arg}"
"${output_dir_arg}"
)
add_command(set_tests_properties
"${prefix}${plain_name}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
${properties}
)
if(add_tags)
string(JSON num_tags LENGTH "${test_tags}")
math(EXPR num_tags "${num_tags} - 1")
set(tag_list "")
if(num_tags GREATER_EQUAL "0")
foreach(tag_idx RANGE ${num_tags})
string(JSON a_tag GET "${test_tags}" "${tag_idx}")
# Catch2's tags can contain semicolons, which are list element separators
# in CMake, so we have to escape them. Ideally we could use the [=[...]=]
# syntax for this, but CTest currently keeps the square quotes in the label
# name. So we add 2 backslashes to escape it instead.
# **IMPORTANT**: The number of backslashes depends on how many layers
# of CMake the tag goes. If this script is changed, the
# number of backslashes to escape may change as well.
string(REPLACE ";" "\\;" a_tag "${a_tag}")
list(APPEND tag_list "${a_tag}")
endforeach()
add_command(set_tests_properties
"${prefix}${plain_name}${suffix}"
PROPERTIES
LABELS "${tag_list}"
)
endif()
endif(add_tags)
if(environment_modifications)
add_command(set_tests_properties
"${prefix}${plain_name}${suffix}"
PROPERTIES
ENVIRONMENT_MODIFICATION "${environment_modifications}")
endif()
list(APPEND tests "${prefix}${plain_name}${suffix}")
endforeach()
# Create a list of all discovered tests, which users may use to e.g. set
# properties on the tests
add_command(set ${_TEST_LIST} ${tests})
# Write CTest script
file(WRITE "${_CTEST_FILE}" "${script}")
endfunction()
if(CMAKE_SCRIPT_MODE_FILE)
catch_discover_tests_impl(
TEST_EXECUTABLE ${TEST_EXECUTABLE}
TEST_EXECUTOR ${TEST_EXECUTOR}
TEST_WORKING_DIR ${TEST_WORKING_DIR}
TEST_SPEC ${TEST_SPEC}
TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS}
TEST_PROPERTIES ${TEST_PROPERTIES}
TEST_PREFIX ${TEST_PREFIX}
TEST_SUFFIX ${TEST_SUFFIX}
TEST_LIST ${TEST_LIST}
TEST_REPORTER ${TEST_REPORTER}
TEST_OUTPUT_DIR ${TEST_OUTPUT_DIR}
TEST_OUTPUT_PREFIX ${TEST_OUTPUT_PREFIX}
TEST_OUTPUT_SUFFIX ${TEST_OUTPUT_SUFFIX}
TEST_DL_PATHS ${TEST_DL_PATHS}
TEST_DL_FRAMEWORK_PATHS ${TEST_DL_FRAMEWORK_PATHS}
CTEST_FILE ${CTEST_FILE}
ADD_TAGS_AS_LABELS ${ADD_TAGS_AS_LABELS}
)
endif()
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
OUTPUT_VARIABLE output
RESULT_VARIABLE result
)
# Catch --list-test-names-only reports the number of tests, so 0 is... surprising
if(${result} EQUAL 0)
message(WARNING
"Test executable '${TEST_EXECUTABLE}' contains no tests!\n"
)
elseif(${result} LESS 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${result}\n"
" Output: ${output}\n"
)
endif()
string(REPLACE "\n" ";" output "${output}")
# Parse output
foreach(line ${output})
set(test ${line})
# ...and add to script
add_command(add_test
"${prefix}${test}${suffix}"
${TEST_EXECUTOR}
"${TEST_EXECUTABLE}"
${test}
${extra_args}
)
add_command(set_tests_properties
"${prefix}${test}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
${properties}
)
list(APPEND tests "${prefix}${test}${suffix}")
endforeach()
# Create a list of all discovered tests, which users may use to e.g. set
# properties on the tests
add_command(set ${TEST_LIST} ${tests})
# Write CTest script
file(WRITE "${CTEST_FILE}" "${script}")

View File

@ -0,0 +1,72 @@
# Copyright Catch2 Authors
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
# Supported optional args:
# * SHARD_COUNT - number of shards to split target's tests into
# * REPORTER - reporter spec to use for tests
# * TEST_SPEC - test spec used for filtering tests
function(catch_add_sharded_tests TARGET)
if(${CMAKE_VERSION} VERSION_LESS "3.10.0")
message(FATAL_ERROR "add_sharded_catch_tests only supports CMake versions 3.10.0 and up")
endif()
cmake_parse_arguments(
""
""
"SHARD_COUNT;REPORTER;TEST_SPEC"
""
${ARGN}
)
if(NOT DEFINED _SHARD_COUNT)
set(_SHARD_COUNT 2)
endif()
# Generate a unique name based on the extra arguments
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX} ${_SHARD_COUNT}")
string(SUBSTRING ${args_hash} 0 7 args_hash)
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-sharded-tests-include-${args_hash}.cmake")
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-sharded-tests-impl-${args_hash}.cmake")
file(WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
"endif()\n"
)
set_property(DIRECTORY
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
)
set(shard_impl_script_file "${_CATCH_DISCOVER_SHARD_TESTS_IMPL_SCRIPT}")
add_custom_command(
TARGET ${TARGET} POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND "${CMAKE_COMMAND}"
-D "TARGET_NAME=${TARGET}"
-D "TEST_BINARY=$<TARGET_FILE:${TARGET}>"
-D "CTEST_FILE=${ctest_tests_file}"
-D "SHARD_COUNT=${_SHARD_COUNT}"
-D "REPORTER_SPEC=${_REPORTER}"
-D "TEST_SPEC=${_TEST_SPEC}"
-P "${shard_impl_script_file}"
VERBATIM
)
endfunction()
###############################################################################
set(_CATCH_DISCOVER_SHARD_TESTS_IMPL_SCRIPT
${CMAKE_CURRENT_LIST_DIR}/CatchShardTestsImpl.cmake
CACHE INTERNAL "Catch2 full path to CatchShardTestsImpl.cmake helper file"
)

View File

@ -0,0 +1,52 @@
# Copyright Catch2 Authors
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
# Indirection for CatchShardTests that allows us to delay the script
# file generation until build time.
# Expected args:
# * TEST_BINARY - full path to the test binary to run sharded
# * CTEST_FILE - full path to ctest script file to write to
# * TARGET_NAME - name of the target to shard (used for test names)
# * SHARD_COUNT - number of shards to split the binary into
# Optional args:
# * REPORTER_SPEC - reporter specs to be passed down to the binary
# * TEST_SPEC - test spec to pass down to the test binary
if(NOT EXISTS "${TEST_BINARY}")
message(FATAL_ERROR
"Specified test binary '${TEST_BINARY}' does not exist"
)
endif()
set(other_args "")
if(TEST_SPEC)
set(other_args "${other_args} ${TEST_SPEC}")
endif()
if(REPORTER_SPEC)
set(other_args "${other_args} --reporter ${REPORTER_SPEC}")
endif()
# foreach RANGE in cmake is inclusive of the end, so we have to adjust it
math(EXPR adjusted_shard_count "${SHARD_COUNT} - 1")
file(WRITE "${CTEST_FILE}"
"string(RANDOM LENGTH 8 ALPHABET \"0123456789abcdef\" rng_seed)\n"
"\n"
"foreach(shard_idx RANGE ${adjusted_shard_count})\n"
" add_test(${TARGET_NAME}-shard-" [[${shard_idx}]] "/${adjusted_shard_count}\n"
" ${TEST_BINARY}"
" --shard-index " [[${shard_idx}]]
" --shard-count ${SHARD_COUNT}"
" --rng-seed " [[0x${rng_seed}]]
" --order rand"
"${other_args}"
"\n"
" )\n"
"endforeach()\n"
)

View File

@ -1,9 +1,11 @@
#==================================================================================================#
# supported macros #
# - TEST_CASE, #
# - TEMPLATE_TEST_CASE #
# - SCENARIO, #
# - TEST_CASE_METHOD, #
# - CATCH_TEST_CASE, #
# - CATCH_TEMPLATE_TEST_CASE #
# - CATCH_SCENARIO, #
# - CATCH_TEST_CASE_METHOD. #
# #
@ -39,9 +41,24 @@
# PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS (Default OFF) #
# -- causes CMake to rerun when file with tests changes so that new tests will be discovered #
# #
# One can also set (locally) the optional variable OptionalCatchTestLauncher to precise the way #
# a test should be run. For instance to use test MPI, one can write #
# set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${NUMPROC}) #
# just before calling this ParseAndAddCatchTests function #
# #
# The AdditionalCatchParameters optional variable can be used to pass extra argument to the test #
# command. For example, to include successful tests in the output, one can write #
# set(AdditionalCatchParameters --success) #
# #
# After the script, the ParseAndAddCatchTests_TESTS property for the target, and for each source #
# file in the target is set, and contains the list of the tests extracted from that target, or #
# from that file. This is useful, for example to add further labels or properties to the tests. #
# #
#==================================================================================================#
cmake_minimum_required(VERSION 2.8.8)
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.8)
message(FATAL_ERROR "ParseAndAddCatchTests requires CMake 2.8.8 or newer")
endif()
option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF)
option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF)
@ -49,10 +66,10 @@ option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the
option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON)
option(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF)
function(PrintDebugMessage)
if(PARSE_CATCH_TESTS_VERBOSE)
message(STATUS "ParseAndAddCatchTests: ${ARGV}")
endif()
function(ParseAndAddCatchTests_PrintDebugMessage)
if(PARSE_CATCH_TESTS_VERBOSE)
message(STATUS "ParseAndAddCatchTests: ${ARGV}")
endif()
endfunction()
# This removes the contents between
@ -60,7 +77,7 @@ endfunction()
# - full line comments (i.e. // ... )
# contents have been read into '${CppCode}'.
# !keep partial line comments
function(RemoveComments CppCode)
function(ParseAndAddCatchTests_RemoveComments CppCode)
string(ASCII 2 CMakeBeginBlockComment)
string(ASCII 3 CMakeEndBlockComment)
string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}")
@ -72,114 +89,162 @@ function(RemoveComments CppCode)
endfunction()
# Worker function
function(ParseFile SourceFile TestTarget)
# According to CMake docs EXISTS behavior is well-defined only for full paths.
get_filename_component(SourceFile ${SourceFile} ABSOLUTE)
if(NOT EXISTS ${SourceFile})
message(WARNING "Cannot find source file: ${SourceFile}")
return()
function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget)
# If SourceFile is an object library, do not scan it (as it is not a file). Exit without giving a warning about a missing file.
if(SourceFile MATCHES "\\\$<TARGET_OBJECTS:.+>")
ParseAndAddCatchTests_PrintDebugMessage("Detected OBJECT library: ${SourceFile} this will not be scanned for tests.")
return()
endif()
# According to CMake docs EXISTS behavior is well-defined only for full paths.
get_filename_component(SourceFile ${SourceFile} ABSOLUTE)
if(NOT EXISTS ${SourceFile})
message(WARNING "Cannot find source file: ${SourceFile}")
return()
endif()
ParseAndAddCatchTests_PrintDebugMessage("parsing ${SourceFile}")
file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME)
# Remove block and fullline comments
ParseAndAddCatchTests_RemoveComments(Contents)
# Find definition of test names
# https://regex101.com/r/JygOND/1
string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([ \t\n]*\"[^\"]*\"[ \t\n]*(,[ \t\n]*\"[^\"]*\")?(,[ \t\n]*[^\,\)]*)*\\)[ \t\n]*\{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}")
if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests)
ParseAndAddCatchTests_PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property")
set_property(
DIRECTORY
APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile}
)
endif()
# check CMP0110 policy for new add_test() behavior
if(POLICY CMP0110)
cmake_policy(GET CMP0110 _cmp0110_value) # new add_test() behavior
else()
# just to be thorough explicitly set the variable
set(_cmp0110_value)
endif()
foreach(TestName ${Tests})
# Strip newlines
string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}")
# Get test type and fixture if applicable
string(REGEX MATCH "(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}")
string(REGEX MATCH "(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}")
string(REGEX REPLACE "${TestType}\\([ \t]*" "" TestFixture "${TestTypeAndFixture}")
# Get string parts of test definition
string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}")
# Strip wrapping quotation marks
string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}")
string(REPLACE "\";\"" ";" TestStrings "${TestStrings}")
# Validate that a test name and tags have been provided
list(LENGTH TestStrings TestStringsLength)
if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1)
message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}")
endif()
PrintDebugMessage("parsing ${SourceFile}")
file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME)
# Remove block and fullline comments
RemoveComments(Contents)
# Assign name and tags
list(GET TestStrings 0 Name)
if("${TestType}" STREQUAL "SCENARIO")
set(Name "Scenario: ${Name}")
endif()
if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND "${TestType}" MATCHES "(CATCH_)?TEST_CASE_METHOD" AND TestFixture)
set(CTestName "${TestFixture}:${Name}")
else()
set(CTestName "${Name}")
endif()
if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME)
set(CTestName "${TestTarget}:${CTestName}")
endif()
# add target to labels to enable running all tests added from this target
set(Labels ${TestTarget})
if(TestStringsLength EQUAL 2)
list(GET TestStrings 1 Tags)
string(TOLOWER "${Tags}" Tags)
# remove target from labels if the test is hidden
if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*")
list(REMOVE_ITEM Labels ${TestTarget})
endif()
string(REPLACE "]" ";" Tags "${Tags}")
string(REPLACE "[" "" Tags "${Tags}")
else()
# unset tags variable from previous loop
unset(Tags)
endif()
# Find definition of test names
string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^\)]+\\)+[ \t\n]*{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}")
list(APPEND Labels ${Tags})
if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests)
PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property")
set(HiddenTagFound OFF)
foreach(label ${Labels})
string(REGEX MATCH "^!hide|^\\." result ${label})
if(result)
set(HiddenTagFound ON)
break()
endif()
endforeach(label)
if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_LESS "3.9")
ParseAndAddCatchTests_PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label")
else()
ParseAndAddCatchTests_PrintDebugMessage("Adding test \"${CTestName}\"")
if(Labels)
ParseAndAddCatchTests_PrintDebugMessage("Setting labels to ${Labels}")
endif()
# Escape commas in the test spec
string(REPLACE "," "\\," Name ${Name})
# Work around CMake 3.18.0 change in `add_test()`, before the escaped quotes were necessary,
# only with CMake 3.18.0 the escaped double quotes confuse the call. This change is reverted in 3.18.1
# And properly introduced in 3.19 with the CMP0110 policy
if(_cmp0110_value STREQUAL "NEW" OR ${CMAKE_VERSION} VERSION_EQUAL "3.18")
ParseAndAddCatchTests_PrintDebugMessage("CMP0110 set to NEW, no need for add_test(\"\") workaround")
else()
ParseAndAddCatchTests_PrintDebugMessage("CMP0110 set to OLD adding \"\" for add_test() workaround")
set(CTestName "\"${CTestName}\"")
endif()
# Handle template test cases
if("${TestTypeAndFixture}" MATCHES ".*TEMPLATE_.*")
set(Name "${Name} - *")
endif()
# Add the test and set its properties
add_test(NAME "${CTestName}" COMMAND ${OptionalCatchTestLauncher} $<TARGET_FILE:${TestTarget}> ${Name} ${AdditionalCatchParameters})
# Old CMake versions do not document VERSION_GREATER_EQUAL, so we use VERSION_GREATER with 3.8 instead
if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_GREATER "3.8")
ParseAndAddCatchTests_PrintDebugMessage("Setting DISABLED test property")
set_tests_properties("${CTestName}" PROPERTIES DISABLED ON)
else()
set_tests_properties("${CTestName}" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran"
LABELS "${Labels}")
endif()
set_property(
DIRECTORY
TARGET ${TestTarget}
APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile}
)
PROPERTY ParseAndAddCatchTests_TESTS "${CTestName}")
set_property(
SOURCE ${SourceFile}
APPEND
PROPERTY ParseAndAddCatchTests_TESTS "${CTestName}")
endif()
foreach(TestName ${Tests})
# Strip newlines
string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}")
# Get test type and fixture if applicable
string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}")
string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}")
string(REPLACE "${TestType}(" "" TestFixture "${TestTypeAndFixture}")
# Get string parts of test definition
string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}")
# Strip wrapping quotation marks
string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}")
string(REPLACE "\";\"" ";" TestStrings "${TestStrings}")
# Validate that a test name and tags have been provided
list(LENGTH TestStrings TestStringsLength)
if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1)
message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}")
endif()
# Assign name and tags
list(GET TestStrings 0 Name)
if("${TestType}" STREQUAL "SCENARIO")
set(Name "Scenario: ${Name}")
endif()
if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND TestFixture)
set(CTestName "${TestFixture}:${Name}")
else()
set(CTestName "${Name}")
endif()
if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME)
set(CTestName "${TestTarget}:${CTestName}")
endif()
# add target to labels to enable running all tests added from this target
set(Labels ${TestTarget})
if(TestStringsLength EQUAL 2)
list(GET TestStrings 1 Tags)
string(TOLOWER "${Tags}" Tags)
# remove target from labels if the test is hidden
if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*")
list(REMOVE_ITEM Labels ${TestTarget})
endif()
string(REPLACE "]" ";" Tags "${Tags}")
string(REPLACE "[" "" Tags "${Tags}")
endif()
list(APPEND Labels ${Tags})
list(FIND Labels "!hide" IndexOfHideLabel)
set(HiddenTagFound OFF)
foreach(label ${Labels})
string(REGEX MATCH "^!hide|^\\." result ${label})
if(result)
set(HiddenTagFound ON)
break()
endif(result)
endforeach(label)
if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound})
PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label")
else()
PrintDebugMessage("Adding test \"${CTestName}\"")
if(Labels)
PrintDebugMessage("Setting labels to ${Labels}")
endif()
# Add the test and set its properties
add_test(NAME "\"${CTestName}\"" COMMAND ${TestTarget} ${Name} ${AdditionalCatchParameters})
set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran"
LABELS "${Labels}")
endif()
endforeach()
endforeach()
endfunction()
# entry point
function(ParseAndAddCatchTests TestTarget)
PrintDebugMessage("Started parsing ${TestTarget}")
get_target_property(SourceFiles ${TestTarget} SOURCES)
PrintDebugMessage("Found the following sources: ${SourceFiles}")
foreach(SourceFile ${SourceFiles})
ParseFile(${SourceFile} ${TestTarget})
endforeach()
PrintDebugMessage("Finished parsing ${TestTarget}")
message(DEPRECATION "ParseAndAddCatchTest: function deprecated because of possibility of missed test cases. Consider using 'catch_discover_tests' from 'Catch.cmake'")
ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}")
get_target_property(SourceFiles ${TestTarget} SOURCES)
ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}")
foreach(SourceFile ${SourceFiles})
ParseAndAddCatchTests_ParseFile(${SourceFile} ${TestTarget})
endforeach()
ParseAndAddCatchTests_PrintDebugMessage("Finished parsing ${TestTarget}")
endfunction()

View File

@ -59,10 +59,6 @@ static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later req
#define CHAISCRIPT_MODULE_EXPORT extern "C"
#endif
#if defined(CHAISCRIPT_MSVC) || (defined(__GNUC__) && __GNUC__ >= 5) || defined(CHAISCRIPT_CLANG)
#define CHAISCRIPT_UTF16_UTF32
#endif
#ifdef _DEBUG
#define CHAISCRIPT_DEBUG true
#else

View File

@ -45,11 +45,10 @@ namespace chaiscript {
auto lib = std::make_shared<Module>();
const bool no_io = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_IO) != t_opts.end();
const bool no_prelude = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_Prelude) != t_opts.end();
const bool no_json = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_JSON) != t_opts.end();
bootstrap::Bootstrap::bootstrap(*lib, no_io);
bootstrap::Bootstrap::bootstrap(*lib);
bootstrap::standard_library::vector_type<std::vector<Boxed_Value>>("Vector", *lib);
bootstrap::standard_library::string_type<std::string>("string", *lib);

View File

@ -26,7 +26,7 @@ namespace chaiscript::bootstrap {
throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < "
+ std::to_string(extent));
} else {
return t[index];
return *std::next(t, static_cast<std::ptrdiff_t>(index));
}
}),
"[]");
@ -37,7 +37,7 @@ namespace chaiscript::bootstrap {
throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < "
+ std::to_string(extent));
} else {
return t[index];
return *std::next(t, static_cast<std::ptrdiff_t>(index));
}
}),
"[]");
@ -269,7 +269,7 @@ namespace chaiscript::bootstrap {
/// \brief perform all common bootstrap functions for std::string, void and POD types
/// \param[in,out] m Module to add bootstrapped functions to
/// \param[in] t_no_io If true, skip registering print_string and println_string
static void bootstrap(Module &m, const bool t_no_io = false) {
static void bootstrap(Module &m) {
m.add(user_type<void>(), "void");
m.add(user_type<bool>(), "bool");
m.add(user_type<Boxed_Value>(), "Object");
@ -437,11 +437,6 @@ namespace chaiscript::bootstrap {
m.add(fun(&Build_Info::compiler_id), "compiler_id");
m.add(fun(&Build_Info::debug_build), "debug_build");
// print_string and println_string are registered in ChaiScript_Basic::build_eval_system()
// to support per-instance IO redirection via set_print_handler.
// When No_IO is set, the functions are still registered but the default handler
// is a no-op, so users can provide their own print handlers without any stdout output.
m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind");
m.add(fun(&shared_ptr_unconst_clone<dispatch::Proxy_Function_Base>), "clone");

View File

@ -41,7 +41,7 @@ namespace chaiscript {
// this is OK, so we're disabling size/and sign type warnings
#ifdef CHAISCRIPT_MSVC
#pragma warning(push)
#pragma warning(disable : 4244 4018 4389 4146 4365 4267 4242)
#pragma warning(disable : 4244 4018 4389 4146 4365 4267 4242 4702) // 4702 is for broken unreachable warning
#endif
#ifdef __GNUC__

View File

@ -34,7 +34,6 @@
#include "dynamic_object.hpp"
#include "proxy_constructors.hpp"
#include "proxy_functions.hpp"
#include "short_alloc.hpp"
#include "type_conversions.hpp"
#include "type_info.hpp"
@ -318,9 +317,6 @@ namespace chaiscript {
namespace detail {
struct Stack_Holder {
// template <class T, std::size_t BufSize = sizeof(T)*20000>
// using SmallVector = std::vector<T, short_alloc<T, BufSize>>;
template<class T>
using SmallVector = std::vector<T>;
@ -377,8 +373,8 @@ namespace chaiscript {
Dispatch_Engine(const Dispatch_Engine &) = delete;
Dispatch_Engine &operator=(const Dispatch_Engine &) = delete;
Dispatch_Engine(Dispatch_Engine &&) = default;
Dispatch_Engine &operator=(Dispatch_Engine &&) = default;
Dispatch_Engine(Dispatch_Engine &&) = delete;
Dispatch_Engine &operator=(Dispatch_Engine &&) = delete;
#ifndef CHAISCRIPT_NO_THREADS
/// Track an async thread so it can be joined during destruction
@ -790,13 +786,13 @@ namespace chaiscript {
t_loc = uint_fast32_t(funs.first);
}
const auto do_attribute_call = [this](int l_num_params,
const auto do_attribute_call = [this](std::size_t l_num_params,
Function_Params l_params,
const std::vector<Proxy_Function> &l_funs,
const Type_Conversions_State &l_conversions) -> Boxed_Value {
Function_Params attr_params(l_params.begin(), l_params.begin() + l_num_params);
Function_Params attr_params(l_params.first(l_num_params));
Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions);
if (l_num_params < int(l_params.size()) || bv.get_type_info().bare_equal(user_type<dispatch::Proxy_Function_Base>())) {
if (l_num_params < l_params.size() || bv.get_type_info().bare_equal(user_type<dispatch::Proxy_Function_Base>())) {
struct This_Foist {
This_Foist(Dispatch_Engine &e, const Boxed_Value &t_bv)
: m_e(e) {
@ -814,16 +810,16 @@ namespace chaiscript {
try {
auto func = boxed_cast<const dispatch::Proxy_Function_Base *>(bv);
try {
return (*func)({l_params.begin() + l_num_params, l_params.end()}, l_conversions);
return (*func)(l_params.subspan(l_num_params), l_conversions);
} catch (const chaiscript::exception::bad_boxed_cast &) {
} catch (const chaiscript::exception::arity_error &) {
} catch (const chaiscript::exception::guard_error &) {
}
throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()},
throw chaiscript::exception::dispatch_error(l_params.subspan(l_num_params),
std::vector<Const_Proxy_Function>{boxed_cast<Const_Proxy_Function>(bv)});
} catch (const chaiscript::exception::bad_boxed_cast &) {
// unable to convert bv into a Proxy_Function_Base
throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()},
throw chaiscript::exception::dispatch_error(l_params.subspan(l_num_params),
std::vector<Const_Proxy_Function>(l_funs.begin(), l_funs.end()));
}
} else {
@ -873,7 +869,7 @@ namespace chaiscript {
if (!functions.empty()) {
try {
if (is_no_param) {
auto tmp_params = params.to_vector();
auto tmp_params = std::vector<Boxed_Value>(params.begin(), params.end());
tmp_params.insert(tmp_params.begin() + 1, var(t_name));
return do_attribute_call(2, Function_Params(tmp_params), functions, t_conversions);
} else {
@ -948,7 +944,7 @@ namespace chaiscript {
const auto &f = this->boxed_cast<Const_Proxy_Function>(params[0]);
const Type_Conversions_State convs(m_conversions, m_conversions.conversion_saves());
return const_var(f->call_match(Function_Params(params.begin() + 1, params.end()), convs));
return const_var(f->call_match(Function_Params(params.subspan(1)), convs));
}
/// Dump all system info to stdout

View File

@ -12,55 +12,11 @@
#include "boxed_value.hpp"
#include <span>
namespace chaiscript {
class Function_Params {
public:
constexpr Function_Params(const Boxed_Value *const t_begin, const Boxed_Value *const t_end)
: m_begin(t_begin)
, m_end(t_end) {
}
explicit Function_Params(const Boxed_Value &bv)
: m_begin(&bv)
, m_end(m_begin + 1) {
}
explicit Function_Params(const std::vector<Boxed_Value> &vec)
: m_begin(vec.empty() ? nullptr : &vec.front())
, m_end(vec.empty() ? nullptr : &vec.front() + vec.size()) {
}
template<size_t Size>
constexpr explicit Function_Params(const std::array<Boxed_Value, Size> &a)
: m_begin(&a.front())
, m_end(&a.front() + Size) {
}
[[nodiscard]] constexpr const Boxed_Value &operator[](const std::size_t t_i) const noexcept { return m_begin[t_i]; }
[[nodiscard]] constexpr const Boxed_Value *begin() const noexcept { return m_begin; }
[[nodiscard]] constexpr const Boxed_Value &front() const noexcept { return *m_begin; }
[[nodiscard]] constexpr const Boxed_Value *end() const noexcept { return m_end; }
[[nodiscard]] constexpr std::size_t size() const noexcept { return std::size_t(m_end - m_begin); }
[[nodiscard]] std::vector<Boxed_Value> to_vector() const { return std::vector<Boxed_Value>{m_begin, m_end}; }
[[nodiscard]] constexpr bool empty() const noexcept { return m_begin == m_end; }
private:
const Boxed_Value *m_begin = nullptr;
const Boxed_Value *m_end = nullptr;
};
// Constructor specialization for array of size 0
template<>
constexpr Function_Params::Function_Params(const std::array<Boxed_Value, size_t{0}> & /* a */)
: m_begin(nullptr)
, m_end(nullptr) {
}
using Function_Params = std::span<const Boxed_Value>;
} // namespace chaiscript

View File

@ -65,7 +65,7 @@ namespace chaiscript {
bool operator==(const Param_Types &t_rhs) const noexcept { return m_types == t_rhs.m_types; }
std::vector<Boxed_Value> convert(Function_Params t_params, const Type_Conversions_State &t_conversions) const {
auto vals = t_params.to_vector();
auto vals = std::vector<Boxed_Value>{t_params.begin(), t_params.end()};
const auto dynamic_object_type_info = user_type<Dynamic_Object>();
for (size_t i = 0; i < vals.size(); ++i) {
const auto &name = m_types[i].first;
@ -682,13 +682,13 @@ namespace chaiscript {
public:
dispatch_error(const Function_Params &t_parameters, std::vector<Const_Proxy_Function> t_functions)
: std::runtime_error("Error with function dispatch")
, parameters(t_parameters.to_vector())
, parameters(t_parameters.begin(), t_parameters.end())
, functions(std::move(t_functions)) {
}
dispatch_error(const Function_Params &t_parameters, std::vector<Const_Proxy_Function> t_functions, const std::string &t_desc)
: std::runtime_error(t_desc)
, parameters(t_parameters.to_vector())
, parameters(t_parameters.begin(), t_parameters.end())
, functions(std::move(t_functions)) {
}

View File

@ -80,7 +80,7 @@ namespace chaiscript {
}
template<typename Callable, typename Ret, typename... Params, size_t... I>
Ret call_func(Ret (*)(Params...),
Ret call_func_impl(Ret (*)(Params...),
std::index_sequence<I...>,
const Callable &f,
[[maybe_unused]] const chaiscript::Function_Params &params,
@ -95,14 +95,24 @@ namespace chaiscript {
template<typename Callable, typename Ret, typename... Params>
Boxed_Value
call_func(Ret (*sig)(Params...), const Callable &f, const chaiscript::Function_Params &params, const Type_Conversions_State &t_conversions) {
if constexpr (std::is_same_v<Ret, void>) {
call_func(sig, std::index_sequence_for<Params...>{}, f, params, t_conversions);
return Handle_Return<void>::handle();
} else {
return Handle_Return<Ret>::handle(call_func(sig, std::index_sequence_for<Params...>{}, f, params, t_conversions));
}
return Handle_Return<Ret>::handle(call_func_impl(sig, std::index_sequence_for<Params...>{}, f, params, t_conversions));
}
// MSVC has a broken warning for unreachable code in this block
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4702)
#endif
template<typename Callable, typename... Params>
Boxed_Value
call_func(void (*sig)(Params...), const Callable &f, const chaiscript::Function_Params &params, const Type_Conversions_State &t_conversions) {
call_func_impl(sig, std::index_sequence_for<Params...>{}, f, params, t_conversions);
return Handle_Return<void>::handle();
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
} // namespace detail
} // namespace dispatch

View File

@ -1,137 +0,0 @@
#ifndef SHORT_ALLOC_H
#define SHORT_ALLOC_H
// The MIT License (MIT)
//
// Copyright (c) 2015 Howard Hinnant
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <cassert>
#include <cstddef>
template<std::size_t N, std::size_t alignment = alignof(std::max_align_t)>
class arena {
alignas(alignment) char buf_[N];
char *ptr_;
public:
~arena() { ptr_ = nullptr; }
arena() noexcept
: ptr_(buf_) {
}
arena(const arena &) = delete;
arena &operator=(const arena &) = delete;
template<std::size_t ReqAlign>
char *allocate(std::size_t n);
void deallocate(char *p, std::size_t n) noexcept;
static constexpr std::size_t size() noexcept { return N; }
std::size_t used() const noexcept { return static_cast<std::size_t>(ptr_ - buf_); }
void reset() noexcept { ptr_ = buf_; }
private:
static std::size_t align_up(std::size_t n) noexcept { return (n + (alignment - 1)) & ~(alignment - 1); }
bool pointer_in_buffer(char *p) noexcept { return buf_ <= p && p <= buf_ + N; }
};
template<std::size_t N, std::size_t alignment>
template<std::size_t ReqAlign>
char *arena<N, alignment>::allocate(std::size_t n) {
static_assert(ReqAlign <= alignment, "alignment is too small for this arena");
assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
auto const aligned_n = align_up(n);
if (static_cast<decltype(aligned_n)>(buf_ + N - ptr_) >= aligned_n) {
char *r = ptr_;
ptr_ += aligned_n;
return r;
}
static_assert(alignment <= alignof(std::max_align_t),
"you've chosen an "
"alignment that is larger than alignof(std::max_align_t), and "
"cannot be guaranteed by normal operator new");
return static_cast<char *>(::operator new(n));
}
template<std::size_t N, std::size_t alignment>
void arena<N, alignment>::deallocate(char *p, std::size_t n) noexcept {
assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
if (pointer_in_buffer(p)) {
n = align_up(n);
if (p + n == ptr_) {
ptr_ = p;
}
} else {
::operator delete(p);
}
}
template<class T, std::size_t N, std::size_t Align = alignof(std::max_align_t)>
class short_alloc {
public:
using value_type = T;
static auto constexpr alignment = Align;
static auto constexpr size = N;
using arena_type = arena<size, alignment>;
private:
arena_type &a_;
public:
short_alloc(const short_alloc &) = default;
short_alloc &operator=(const short_alloc &) = delete;
explicit short_alloc(arena_type &a) noexcept
: a_(a) {
static_assert(size % alignment == 0, "size N needs to be a multiple of alignment Align");
}
template<class U>
explicit short_alloc(const short_alloc<U, N, alignment> &a) noexcept
: a_(a.a_) {
}
template<class _Up>
struct rebind {
using other = short_alloc<_Up, N, alignment>;
};
T *allocate(std::size_t n) { return reinterpret_cast<T *>(a_.template allocate<alignof(T)>(n * sizeof(T))); }
void deallocate(T *p, std::size_t n) noexcept { a_.deallocate(reinterpret_cast<char *>(p), n * sizeof(T)); }
template<class T1, std::size_t N1, std::size_t A1, class U, std::size_t M, std::size_t A2>
friend bool operator==(const short_alloc<T1, N1, A1> &x, const short_alloc<U, M, A2> &y) noexcept;
template<class U, std::size_t M, std::size_t A>
friend class short_alloc;
};
template<class T, std::size_t N, std::size_t A1, class U, std::size_t M, std::size_t A2>
inline bool operator==(const short_alloc<T, N, A1> &x, const short_alloc<U, M, A2> &y) noexcept {
return N == M && A1 == A2 && &x.a_ == &y.a_;
}
template<class T, std::size_t N, std::size_t A1, class U, std::size_t M, std::size_t A2>
inline bool operator!=(const short_alloc<T, N, A1> &x, const short_alloc<U, M, A2> &y) noexcept {
return !(x == y);
}
#endif // SHORT_ALLOC_HPP

View File

@ -12,6 +12,7 @@
#include "../utility/hash.hpp"
#include <array>
#include <string>
namespace chaiscript {
@ -52,10 +53,10 @@ namespace chaiscript {
invalid
};
constexpr static const char *to_string(Opers t_oper) noexcept {
constexpr const char *opers[]
constexpr static std::string_view to_string(Opers t_oper) noexcept {
constexpr const std::array opers
= {"", "==", "<", ">", "<=", ">=", "!=", "", "=", "++", "--", "*=", "+=", "/=", "-=", "", "&=", "|=", "<<=", ">>=", "%=", "^=", "", "<<", ">>", "%", "&", "|", "^", "~", "", "+", "/", "*", "-", "+", "-", ""};
return opers[static_cast<int>(t_oper)];
return opers[static_cast<std::size_t>(t_oper)];
}
constexpr static Opers to_operator(std::string_view t_str, bool t_is_unary = false) noexcept {

View File

@ -130,9 +130,9 @@ namespace chaiscript {
namespace {
/// Helper lookup to get the name of each node type
constexpr const char *ast_node_type_to_string(AST_Node_Type ast_node_type) noexcept {
constexpr const char *const ast_node_types[] = {"Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", "Assign_Decl", "Array_Call", "Dot_Access", "Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", "Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Noop", "Class", "Binary", "Arg", "Global_Decl", "Constant", "Compiled", "Const_Var_Decl", "Const_Assign_Decl", "Using", "Enum", "Namespace_Block"};
constexpr std::array ast_node_types = {"Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", "Assign_Decl", "Array_Call", "Dot_Access", "Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", "Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Noop", "Class", "Binary", "Arg", "Global_Decl", "Constant", "Compiled", "Const_Var_Decl", "Const_Assign_Decl", "Using", "Enum", "Namespace_Block"};
return ast_node_types[static_cast<int>(ast_node_type)];
return ast_node_types[static_cast<std::size_t>(ast_node_type)];
}
} // namespace

View File

@ -122,7 +122,7 @@ namespace chaiscript {
chaiscript::detail::Dispatch_Engine &get_eval_engine() noexcept { return m_engine; }
/// Builds all the requirements for ChaiScript, including its evaluator and a run of its prelude.
void build_eval_system(const ModulePtr &t_lib, const std::vector<Options> &t_opts, const bool t_no_io = false) {
void build_eval_system(const ModulePtr &t_lib, const std::vector<Options> &t_opts, const bool t_no_io) {
if (t_lib) {
add(t_lib);
}
@ -317,7 +317,7 @@ namespace chaiscript {
std::vector<std::string> t_module_paths = {},
std::vector<std::string> t_use_paths = {},
const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options(),
const bool t_no_io = false)
const bool t_no_io=false)
: m_module_paths(ensure_minimum_path_vec(std::move(t_module_paths)))
, m_use_paths(ensure_minimum_path_vec(std::move(t_use_paths)))
, m_parser(std::move(parser))
@ -611,7 +611,7 @@ namespace chaiscript {
/// (the symbol mentioned above), an exception is thrown.
///
/// \throw chaiscript::exception::load_module_error In the event that no matching module can be found.
std::string load_module(const std::string &t_module_name) {
std::string load_module([[maybe_unused]] const std::string &t_module_name) {
#ifdef CHAISCRIPT_NO_DYNLOAD
throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)");
#else
@ -777,7 +777,7 @@ namespace chaiscript {
throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered.");
}
m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace & {
m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable noexcept -> Namespace & {
t_namespace_generator(space);
return space;
}));
@ -786,7 +786,7 @@ namespace chaiscript {
while (pos != std::string::npos) {
const std::string parent = t_namespace_name.substr(0, pos);
if (!m_namespace_generators.count(parent)) {
m_namespace_generators.emplace(std::make_pair(parent, [space = Namespace()]() mutable -> Namespace & {
m_namespace_generators.emplace(std::make_pair(parent, [space = Namespace()]() mutable noexcept -> Namespace & {
return space;
}));
}

View File

@ -1246,7 +1246,7 @@ namespace chaiscript {
const auto &ns_name = this->children[0]->text;
auto ns_name_bv = const_var(ns_name);
t_ss->call_function("namespace", m_ns_loc, Function_Params{ns_name_bv}, t_ss.conversions());
t_ss->call_function("namespace", m_ns_loc, Function_Params{&ns_name_bv, 1}, t_ss.conversions());
std::vector<std::string> parts;
{
@ -1350,7 +1350,7 @@ namespace chaiscript {
};
const auto call_function = [&t_ss](const auto &t_funcs, const Boxed_Value &t_param) {
return dispatch::dispatch(*t_funcs, Function_Params{t_param}, t_ss.conversions());
return dispatch::dispatch(*t_funcs, Function_Params{&t_param, 1}, t_ss.conversions());
};
const std::string &loop_var_name = this->children[0]->text;
@ -1646,8 +1646,8 @@ namespace chaiscript {
return Boxed_Number::do_oper(m_oper, bv);
} else {
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
fpp.save_params(Function_Params{bv});
return t_ss->call_function(this->text, m_loc, Function_Params{bv}, t_ss.conversions());
fpp.save_params(Function_Params{&bv, 1});
return t_ss->call_function(this->text, m_loc, Function_Params{&bv, 1}, t_ss.conversions());
}
} catch (const exception::dispatch_error &e) {
throw exception::eval_error("Error with prefix operator evaluation: '" + this->text + "'", e.parameters, e.functions, false, *t_ss);
@ -1755,7 +1755,7 @@ namespace chaiscript {
if (dispatch::Param_Types(
std::vector<std::pair<std::string, Type_Info>>{Arg_List_AST_Node<T>::get_arg_type(*catch_block.children[0], t_ss)})
.match(Function_Params{t_except}, t_ss.conversions())
.match(Function_Params{&t_except, 1}, t_ss.conversions())
.first) {
t_ss.add_object(name, t_except);

View File

@ -14,6 +14,7 @@
#include <cstring>
#include <exception>
#include <iostream>
#include <iterator>
#include <memory>
#include <sstream>
#include <string>
@ -22,15 +23,11 @@
#include "../dispatchkit/boxed_value.hpp"
#include "../utility/hash.hpp"
#include "../utility/static_string.hpp"
#include "../utility/unicode.hpp"
#include "chaiscript_common.hpp"
#include "chaiscript_optimizer.hpp"
#include "chaiscript_tracer.hpp"
#if defined(CHAISCRIPT_UTF16_UTF32)
#include <codecvt>
#include <locale>
#endif
#if defined(CHAISCRIPT_MSVC) && defined(max) && defined(min)
#define CHAISCRIPT_PUSHED_MIN_MAX
#pragma push_macro("max") // Why Microsoft? why? This is worse than bad
@ -64,41 +61,26 @@ namespace chaiscript {
// Generic for u16, u32 and wchar
template<typename string_type>
struct Char_Parser_Helper {
// common for all implementations
static std::string u8str_from_ll(long long val) {
using char_type = std::string::value_type;
char_type c[2];
c[1] = char_type(val);
c[0] = char_type(val >> 8);
if (c[0] == 0) {
return std::string(1, c[1]); // size, character
}
return std::string(c, 2); // char buffer, size
}
static string_type str_from_ll(long long val) {
using target_char_type = typename string_type::value_type;
#if defined(CHAISCRIPT_UTF16_UTF32)
// prepare converter
std::wstring_convert<std::codecvt_utf8<target_char_type>, target_char_type> converter;
// convert
return converter.from_bytes(u8str_from_ll(val));
#else
// no conversion available, just put value as character
return string_type(1, target_char_type(val)); // size, character
#endif
string_type out;
utility::unicode::append_codepoint(out, static_cast<std::uint32_t>(val));
return out;
}
};
// Specialization for char AKA UTF-8
// Specialization for char AKA UTF-8: preserve raw two-byte packing
// of multi-character literals.
template<>
struct Char_Parser_Helper<std::string> {
static std::string str_from_ll(long long val) {
// little SFINAE trick to avoid base class
return Char_Parser_Helper<std::true_type>::u8str_from_ll(val);
using char_type = std::string::value_type;
char_type c[2];
c[1] = char_type(val);
c[0] = char_type(val >> 8);
if (c[0] == 0) {
return std::string(1, c[1]);
}
return std::string(c, 2);
}
};
} // namespace detail
@ -128,9 +110,7 @@ namespace chaiscript {
template<typename Array2D, typename First, typename Second>
constexpr static void set_alphabet(Array2D &array, const First first, const Second second) noexcept {
auto *first_ptr = &std::get<0>(array) + static_cast<std::size_t>(first);
auto *second_ptr = &std::get<0>(*first_ptr) + static_cast<std::size_t>(second);
*second_ptr = true;
array[static_cast<std::size_t>(first)][static_cast<std::size_t>(second)] = true;
}
constexpr static std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> build_alphabet() noexcept {
@ -360,13 +340,13 @@ namespace chaiscript {
++col;
}
++m_pos;
std::advance(m_pos, 1);
}
return *this;
}
constexpr Position &operator--() noexcept {
--m_pos;
std::advance(m_pos, -1);
if (*m_pos == '\n') {
--line;
col = m_last_col;
@ -377,7 +357,7 @@ namespace chaiscript {
}
constexpr Position &operator+=(size_t t_distance) noexcept {
*this = (*this) + t_distance;
*this = *this + t_distance;
return *this;
}
@ -406,9 +386,9 @@ namespace chaiscript {
constexpr bool operator!=(const Position &t_rhs) const noexcept { return m_pos != t_rhs.m_pos; }
constexpr bool has_more() const noexcept { return m_pos != m_end; }
[[nodiscard]] constexpr bool has_more() const noexcept { return m_pos != m_end; }
constexpr size_t remaining() const noexcept { return static_cast<size_t>(m_end - m_pos); }
[[nodiscard]] constexpr size_t remaining() const noexcept { return static_cast<size_t>(m_end - m_pos); }
constexpr const char &operator*() const noexcept {
if (m_pos == m_end) {
@ -458,7 +438,7 @@ namespace chaiscript {
constexpr static Operator_Matches m_operator_matches{};
/// test a char in an m_alphabet
constexpr bool char_in_alphabet(char c, detail::Alphabet a) const noexcept { return m_alphabet[a][static_cast<uint8_t>(c)]; }
[[nodiscard]] constexpr bool char_in_alphabet(char c, detail::Alphabet a) const noexcept { return m_alphabet[a][static_cast<uint8_t>(c)]; }
/// Prints the parsed ast_nodes as a tree
void debug_print(const AST_Node &t, std::string prepend = "") const override {
@ -509,7 +489,7 @@ namespace chaiscript {
if (m_position.remaining() >= len) {
const char *file_pos = &(*m_position);
for (size_t pos = 0; pos < len; ++pos) {
if (sym.c_str()[pos] != file_pos[pos]) {
if (sym[pos] != *std::next(file_pos, static_cast<std::ptrdiff_t>(pos))) {
return false;
}
}
@ -530,20 +510,9 @@ namespace chaiscript {
}
}
return true;
} else if (Symbol_(m_singleline_comment)) {
while (m_position.has_more()) {
if (Symbol_(m_cr_lf)) {
m_position -= 2;
break;
} else if (Char_('\n')) {
--m_position;
break;
} else {
++m_position;
}
}
return true;
} else if (Symbol_(m_annotation)) {
}
if (Symbol_(m_singleline_comment) || Symbol_(m_annotation)) {
while (m_position.has_more()) {
if (Symbol_(m_cr_lf)) {
m_position -= 2;
@ -1060,7 +1029,7 @@ namespace chaiscript {
template<typename string_type>
struct Char_Parser {
string_type &match;
using char_type = typename string_type::value_type;
using char_type = string_type::value_type;
bool is_escaped = false;
bool is_interpolated = false;
bool saw_interpolation_marker = false;
@ -1119,40 +1088,20 @@ namespace chaiscript {
}
void process_unicode() {
const auto ch = static_cast<uint32_t>(std::stoi(hex_matches, nullptr, 16));
const auto ch = static_cast<std::uint32_t>(std::stoi(hex_matches, nullptr, 16));
const auto match_size = hex_matches.size();
hex_matches.clear();
is_escaped = false;
const auto u_size = unicode_size;
unicode_size = 0;
char buf[4];
if (u_size != match_size) {
throw exception::eval_error("Incomplete unicode escape sequence");
}
if (u_size == 4 && ch >= 0xD800 && ch <= 0xDFFF) {
if (u_size == 4 && utility::unicode::is_surrogate(ch)) {
throw exception::eval_error("Invalid 16 bit universal character");
}
if (ch < 0x80) {
match += static_cast<char>(ch);
} else if (ch < 0x800) {
buf[0] = static_cast<char>(0xC0 | (ch >> 6));
buf[1] = static_cast<char>(0x80 | (ch & 0x3F));
match.append(buf, 2);
} else if (ch < 0x10000) {
buf[0] = static_cast<char>(0xE0 | (ch >> 12));
buf[1] = static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
buf[2] = static_cast<char>(0x80 | (ch & 0x3F));
match.append(buf, 3);
} else if (ch < 0x200000) {
buf[0] = static_cast<char>(0xF0 | (ch >> 18));
buf[1] = static_cast<char>(0x80 | ((ch >> 12) & 0x3F));
buf[2] = static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
buf[3] = static_cast<char>(0x80 | (ch & 0x3F));
match.append(buf, 4);
} else {
// this must be an invalid escape sequence?
if (utility::unicode::append_utf8(match, ch) == 0) {
throw exception::eval_error("Invalid 32 bit universal character");
}
}
@ -1548,7 +1497,7 @@ namespace chaiscript {
if (m_position.remaining() >= len) {
auto tmp = m_position;
for (size_t i = 0; tmp.has_more() && i < len; ++i) {
if (*tmp != t_s.c_str()[i]) {
if (*tmp != t_s[i]) {
return false;
}
++tmp;
@ -1575,7 +1524,7 @@ namespace chaiscript {
return retval;
}
bool is_operator(std::string_view t_s) const noexcept { return m_operator_matches.is_match(t_s); }
[[nodiscard]] bool is_operator(std::string_view t_s) const noexcept { return m_operator_matches.is_match(t_s); }
/// Reads (and potentially captures) a symbol group from input if it matches the parameter
bool Symbol(const utility::Static_String &t_s, const bool t_disallow_prevention = false) {
@ -2749,7 +2698,7 @@ namespace chaiscript {
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
if (m_operators[t_precedence] != Operator_Precedence::Prefix) {
if (t_precedence < m_operators.size() && m_operators[t_precedence] < Operator_Precedence::Prefix) {
if (Operator(t_precedence + 1)) {
retval = true;
std::string oper;
@ -2763,7 +2712,7 @@ namespace chaiscript {
}
switch (m_operators[t_precedence]) {
case (Operator_Precedence::Ternary_Cond):
case Operator_Precedence::Ternary_Cond:
if (Symbol(":")) {
if (!Operator(t_precedence + 1)) {
throw exception::eval_error("Incomplete '" + oper + "' expression",
@ -2778,24 +2727,24 @@ namespace chaiscript {
}
break;
case (Operator_Precedence::Addition):
case (Operator_Precedence::Multiplication):
case (Operator_Precedence::Shift):
case (Operator_Precedence::Equality):
case (Operator_Precedence::Bitwise_And):
case (Operator_Precedence::Bitwise_Xor):
case (Operator_Precedence::Bitwise_Or):
case (Operator_Precedence::Comparison):
case Operator_Precedence::Addition:
case Operator_Precedence::Multiplication:
case Operator_Precedence::Shift:
case Operator_Precedence::Equality:
case Operator_Precedence::Bitwise_And:
case Operator_Precedence::Bitwise_Xor:
case Operator_Precedence::Bitwise_Or:
case Operator_Precedence::Comparison:
build_match<eval::Binary_Operator_AST_Node<Tracer>>(prev_stack_top, oper);
break;
case (Operator_Precedence::Logical_And):
case Operator_Precedence::Logical_And:
build_match<eval::Logical_And_AST_Node<Tracer>>(prev_stack_top, oper);
break;
case (Operator_Precedence::Logical_Or):
case Operator_Precedence::Logical_Or:
build_match<eval::Logical_Or_AST_Node<Tracer>>(prev_stack_top, oper);
break;
case (Operator_Precedence::Prefix):
case Operator_Precedence::Prefix:
assert(false); // cannot reach here because of if() statement at the top
break;
@ -2992,7 +2941,7 @@ namespace chaiscript {
/// Parses the given input string, tagging parsed ast_nodes with the given m_filename.
AST_NodePtr parse_internal(const std::string &t_input, std::string t_fname) {
const auto begin = t_input.empty() ? nullptr : &t_input.front();
const auto end = begin == nullptr ? nullptr : begin + t_input.size();
const auto end = begin == nullptr ? nullptr : std::next(begin, std::ssize(t_input));
m_position = Position(begin, end);
m_filename = std::make_shared<std::string>(std::move(t_fname));

View File

@ -8,6 +8,8 @@
#define CHAISCRIPT_UTILITY_FNV1A_HPP_
#include "../chaiscript_defines.hpp"
#include <iterator>
#include <cstdint>
namespace chaiscript {
@ -28,7 +30,7 @@ namespace chaiscript {
while (begin != end) {
h = (h ^ (*begin)) * 0x01000193;
++begin;
std::advance(begin, 1);
}
return h;
@ -43,7 +45,7 @@ namespace chaiscript {
template<size_t N>
static constexpr std::uint32_t hash(const char (&str)[N]) noexcept {
return hash(std::begin(str), std::end(str) - 1);
return hash(std::begin(str), std::prev(std::end(str)));
}
static constexpr std::uint32_t hash(std::string_view sv) noexcept {
@ -64,7 +66,7 @@ namespace chaiscript {
hash += std::uint32_t(*begin);
hash += hash << 10;
hash ^= hash >> 6;
++begin;
std::advance(begin, 1);
}
hash += hash << 3;

View File

@ -7,6 +7,7 @@
#include "../chaiscript_defines.hpp"
#include "quick_flat_map.hpp"
#include "unicode.hpp"
#include <cctype>
#include <cmath>
#include <cstdint>
@ -44,21 +45,17 @@ namespace chaiscript::json {
= std::variant<std::nullptr_t, chaiscript::utility::QuickFlatMap<std::string, JSON>, std::vector<JSON>, std::string, double, std::int64_t, bool>;
struct Internal {
Internal(std::nullptr_t)
: d(nullptr) {
}
Internal()
: d(nullptr) {
}
Internal(Class c)
explicit Internal(std::nullptr_t) {}
Internal() = default;
Internal(const Class c)
: d(make_type(c)) {
}
template<typename T>
Internal(T t)
explicit Internal(T t)
: d(std::move(t)) {
}
static Data make_type(Class c) {
static Data make_type(const Class c) {
switch (c) {
case Class::Null:
return nullptr;
@ -84,7 +81,7 @@ namespace chaiscript::json {
}
}
Class type() const noexcept { return Class(d.index()); }
[[nodiscard]] Class type() const noexcept { return Class(d.index()); }
template<auto ClassValue, typename Visitor, typename Or>
decltype(auto) visit_or(Visitor &&visitor, Or &&other) const {
@ -108,14 +105,14 @@ namespace chaiscript::json {
auto &Float() { return get_set_type<Class::Floating>(); }
auto &Bool() { return get_set_type<Class::Boolean>(); }
auto Map() const noexcept { return std::get_if<static_cast<std::size_t>(Class::Object)>(&d); }
auto Vector() const noexcept { return std::get_if<static_cast<std::size_t>(Class::Array)>(&d); }
auto String() const noexcept { return std::get_if<static_cast<std::size_t>(Class::String)>(&d); }
auto Int() const noexcept { return std::get_if<static_cast<std::size_t>(Class::Integral)>(&d); }
auto Float() const noexcept { return std::get_if<static_cast<std::size_t>(Class::Floating)>(&d); }
auto Bool() const noexcept { return std::get_if<static_cast<std::size_t>(Class::Boolean)>(&d); }
[[nodiscard]] auto Map() const noexcept { return std::get_if<static_cast<std::size_t>(Class::Object)>(&d); }
[[nodiscard]] auto Vector() const noexcept { return std::get_if<static_cast<std::size_t>(Class::Array)>(&d); }
[[nodiscard]] auto String() const noexcept { return std::get_if<static_cast<std::size_t>(Class::String)>(&d); }
[[nodiscard]] auto Int() const noexcept { return std::get_if<static_cast<std::size_t>(Class::Integral)>(&d); }
[[nodiscard]] auto Float() const noexcept { return std::get_if<static_cast<std::size_t>(Class::Floating)>(&d); }
[[nodiscard]] auto Bool() const noexcept { return std::get_if<static_cast<std::size_t>(Class::Boolean)>(&d); }
Data d;
Data d{nullptr};
};
Internal internal;
@ -129,12 +126,12 @@ namespace chaiscript::json {
JSONWrapper(Container *val)
: object(val) {
}
JSONWrapper(std::nullptr_t) {}
JSONWrapper(std::nullptr_t) { }
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(); }
Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); }
Container::iterator end() { return object ? object->end() : typename Container::iterator(); }
[[nodiscard]] Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); }
[[nodiscard]] Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); }
};
template<typename Container>
@ -147,10 +144,10 @@ namespace chaiscript::json {
}
JSONConstWrapper(std::nullptr_t) {}
typename Container::const_iterator begin() const noexcept {
[[nodiscard]] Container::const_iterator begin() const noexcept {
return object ? object->begin() : typename Container::const_iterator();
}
typename Container::const_iterator end() const noexcept { return object ? object->end() : typename Container::const_iterator(); }
[[nodiscard]] Container::const_iterator end() const noexcept { return object ? object->end() : typename Container::const_iterator(); }
};
JSON() = default;
@ -162,7 +159,9 @@ namespace chaiscript::json {
JSON(initializer_list<JSON> list)
: internal(Class::Object) {
for (auto i = list.begin(), e = list.end(); i != e; ++i, ++i) {
for (auto i = list.begin(), e = list.end();
i != e;
std::advance(i, 2)) {
operator[](i->to_string()) = *std::next(i);
}
}
@ -471,22 +470,8 @@ namespace chaiscript::json {
}
}
offset += 4;
const auto ch = static_cast<uint32_t>(std::stoi(hex_matches, nullptr, 16));
if (ch < 0x80) {
val += static_cast<char>(ch);
} else if (ch < 0x800) {
val += static_cast<char>(0xC0 | (ch >> 6));
val += static_cast<char>(0x80 | (ch & 0x3F));
} else if (ch < 0x10000) {
val += static_cast<char>(0xE0 | (ch >> 12));
val += static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
val += static_cast<char>(0x80 | (ch & 0x3F));
} else if (ch < 0x200000) {
val += static_cast<char>(0xF0 | (ch >> 18));
val += static_cast<char>(0x80 | ((ch >> 12) & 0x3F));
val += static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
val += static_cast<char>(0x80 | (ch & 0x3F));
} else {
const auto ch = static_cast<std::uint32_t>(std::stoi(hex_matches, nullptr, 16));
if (chaiscript::utility::unicode::append_utf8(val, ch) == 0) {
throw std::runtime_error(std::string("JSON ERROR: String: Invalid 32 bit universal character"));
}
} break;

View File

@ -7,6 +7,8 @@
#ifndef CHAISCRIPT_UTILITY_STATIC_STRING_HPP_
#define CHAISCRIPT_UTILITY_STATIC_STRING_HPP_
#include <iterator>
namespace chaiscript::utility {
struct Static_String {
template<size_t N>
@ -21,30 +23,17 @@ namespace chaiscript::utility {
constexpr auto begin() const noexcept { return data; }
constexpr auto end() const noexcept { return data + m_size; }
constexpr auto end() const noexcept { return std::next(data, static_cast<std::ptrdiff_t>(m_size)); }
constexpr bool operator==(std::string_view other) const noexcept {
// return std::string_view(data, m_size) == other;
auto b1 = begin();
const auto e1 = end();
auto b2 = other.begin();
const auto e2 = other.end();
if (e1 - b1 != e2 - b2) {
return false;
}
while (b1 != e1) {
if (*b1 != *b2) {
return false;
}
++b1;
++b2;
}
return true;
constexpr auto operator[](const std::size_t idx) const noexcept {
return *std::next(data, static_cast<std::ptrdiff_t>(idx));
}
bool operator==(const std::string &t_str) const noexcept { return std::equal(begin(), end(), std::cbegin(t_str), std::cend(t_str)); }
constexpr bool operator==(std::string_view other) const noexcept {
return std::string_view(data, m_size) == other;
}
constexpr bool operator==(const std::string &t_str) const noexcept { return std::equal(begin(), end(), std::cbegin(t_str), std::cend(t_str)); }
const size_t m_size;
const char *data = nullptr;

View File

@ -0,0 +1,91 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// http://www.chaiscript.com
#ifndef CHAISCRIPT_UTILITY_UNICODE_HPP_
#define CHAISCRIPT_UTILITY_UNICODE_HPP_
#include <cstddef>
#include <cstdint>
#include <string>
namespace chaiscript {
namespace utility {
namespace unicode {
inline constexpr std::uint32_t max_codepoint = 0x10FFFF;
constexpr bool is_surrogate(std::uint32_t cp) noexcept { return cp >= 0xD800 && cp <= 0xDFFF; }
// Append cp to out as UTF-8. Returns bytes written, or 0 if cp >= 0x200000.
// Surrogates are not rejected here; callers that care check is_surrogate() first.
inline std::size_t append_utf8(std::string &out, std::uint32_t cp) {
if (cp < 0x80) {
out += static_cast<char>(cp);
return 1;
}
if (cp < 0x800) {
out += static_cast<char>(0xC0 | (cp >> 6));
out += static_cast<char>(0x80 | (cp & 0x3F));
return 2;
}
if (cp < 0x10000) {
out += static_cast<char>(0xE0 | (cp >> 12));
out += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
out += static_cast<char>(0x80 | (cp & 0x3F));
return 3;
}
if (cp < 0x200000) {
out += static_cast<char>(0xF0 | (cp >> 18));
out += static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
out += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
out += static_cast<char>(0x80 | (cp & 0x3F));
return 4;
}
return 0;
}
// Append cp to out as UTF-16. Returns code units written, or 0 if cp is
// a surrogate or > max_codepoint.
template<typename CharT>
inline std::size_t append_utf16(std::basic_string<CharT> &out, std::uint32_t cp) {
if (is_surrogate(cp) || cp > max_codepoint) {
return 0;
}
if (cp < 0x10000) {
out += static_cast<CharT>(cp);
return 1;
}
const std::uint32_t v = cp - 0x10000;
out += static_cast<CharT>(0xD800 | (v >> 10));
out += static_cast<CharT>(0xDC00 | (v & 0x3FF));
return 2;
}
// Append cp to a basic_string<CharT>. Dispatches on sizeof(CharT):
// 1 byte -> UTF-8, 2 bytes -> UTF-16, 4 bytes -> UTF-32.
// Returns code units written, or 0 if the codepoint is invalid.
template<typename CharT>
inline std::size_t append_codepoint(std::basic_string<CharT> &out, std::uint32_t cp) {
if constexpr (sizeof(CharT) == 1) {
std::string tmp;
const auto n = append_utf8(tmp, cp);
out.append(tmp.begin(), tmp.end());
return n;
} else if constexpr (sizeof(CharT) == 2) {
return append_utf16(out, cp);
} else {
static_assert(sizeof(CharT) == 4, "append_codepoint: unsupported CharT size");
if (is_surrogate(cp) || cp > max_codepoint) {
return 0;
}
out += static_cast<CharT>(cp);
return 1;
}
}
} // namespace unicode
} // namespace utility
} // namespace chaiscript
#endif

View File

@ -30,9 +30,8 @@ char *mystrdup(const char *s) {
#ifdef CHAISCRIPT_MSVC
strcpy_s(d, len + 1, s); // Copy the characters
#else
strncpy(d, s, len); // Copy the characters
strncpy(d, s, len + 1); // Copy the characters
#endif
d[len] = '\0';
return d; // Return the new string
}
@ -297,7 +296,7 @@ int main(int argc, char *argv[]) {
++i;
}
std::string arg(i ? argv[i] : "--interactive");
std::string arg(i ? *std::next(argv, i) : "--interactive");
enum {
eInteractive,
@ -311,7 +310,7 @@ int main(int argc, char *argv[]) {
std::cout << "insufficient input following " << arg << std::endl;
return EXIT_FAILURE;
} else {
arg = argv[++i];
arg = *std::next(argv, ++i);
}
} else if (arg == "-" || arg == "--stdin") {
arg = "";

View File

@ -35,9 +35,8 @@ char *mystrdup(const char *s) {
#ifdef CHAISCRIPT_MSVC
strcpy_s(d, len + 1, s); // Copy the characters
#else
strncpy(d, s, len); // Copy the characters
strncpy(d, s, len + 1); // Copy the characters
#endif
d[len] = '\0';
return d; // Return the new string
}

View File

@ -8,6 +8,7 @@
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <chrono>
#include <cstddef>
#include <iostream>
#include <list>
#include <regex>
@ -34,9 +35,8 @@ char *mystrdup(const char *s) {
#ifdef CHAISCRIPT_MSVC
strcpy_s(d, len + 1, s); // Copy the characters
#else
strncpy(d, s, len); // Copy the characters
strncpy(d, s, len + 1); // Copy the characters
#endif
d[len] = '\0';
return d; // Return the new string
}
@ -283,7 +283,7 @@ int main(int argc, char *argv[]) {
++i;
}
std::string arg(i != 0 ? argv[i] : "--interactive");
std::string arg(i != 0 ? *std::next(argv, i) : "--interactive");
enum {
eInteractive,
@ -297,7 +297,7 @@ int main(int argc, char *argv[]) {
std::cout << "insufficient input following " << arg << '\n';
return EXIT_FAILURE;
}
arg = argv[++i];
arg = *std::next(argv, ++i);
} else if (arg == "-" || arg == "--stdin") {
arg = "";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -22,11 +22,10 @@
#include "../static_libs/chaiscript_parser.hpp"
#include "../static_libs/chaiscript_stdlib.hpp"
#define CATCH_CONFIG_MAIN
#include "catch_amalgamated.hpp"
#include <clocale>
#include "catch.hpp"
// lambda_tests
TEST_CASE("C++11 Lambdas Can Be Registered") {
@ -190,7 +189,7 @@ TEST_CASE("Throw int or double") {
chai.eval("throw(1.0)", chaiscript::exception_specification<int, double>());
REQUIRE(false);
} catch (const double e) {
CHECK(e == Approx(1.0));
CHECK(e == Catch::Approx(1.0));
}
}
@ -964,7 +963,7 @@ TEST_CASE("Pair conversions") {
Pair(5, 3.14);
)cs");
CHECK(p.first == 5);
CHECK(p.second == Approx(3.14));
CHECK(p.second == Catch::Approx(3.14));
}
}
@ -976,7 +975,7 @@ TEST_CASE("Parse floats with non-posix locale") {
#endif
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
const double parsed = chai.eval<double>("print(1.3); 1.3");
CHECK(parsed == Approx(1.3));
CHECK(parsed == Catch::Approx(1.3));
const std::string str = chai.eval<std::string>("to_string(1.3)");
CHECK(str == "1.3");
}
@ -1270,7 +1269,7 @@ TEST_CASE("Test reference member being registered") {
double d;
chai.add(chaiscript::var(Reference_MyClass(d)), "ref");
chai.eval("ref.x = 2.3");
CHECK(d == Approx(2.3));
CHECK(d == Catch::Approx(2.3));
}
// starting with C++20 u8"" strings cannot be compared with std::string
@ -1827,7 +1826,7 @@ TEST_CASE("vector of vectors conversion (issue #374)") {
}),
"sum_nested");
CHECK(chai.eval<double>("sum_nested([[1.0, 2.0], [3.0, 4.0]])") == Approx(10.0));
CHECK(chai.eval<double>("sum_nested([[1.0, 2.0], [3.0, 4.0]])") == Catch::Approx(10.0));
CHECK(chai.eval<bool>(
"auto v = VectorVectorDouble();"
@ -1910,12 +1909,12 @@ TEST_CASE("Nested namespaces via register_namespace with :: separator") {
chai.import("constants");
CHECK(chai.eval<double>("constants.si.mu_B") == Approx(9.274));
CHECK(chai.eval<double>("constants.mm.mu_B") == Approx(0.05788));
CHECK(chai.eval<double>("constants.si.mu_B") == Catch::Approx(9.274));
CHECK(chai.eval<double>("constants.mm.mu_B") == Catch::Approx(0.05788));
// Scope resolution via :: works the same as . for access
CHECK(chai.eval<double>("constants::si::mu_B") == Approx(9.274));
CHECK(chai.eval<double>("constants::mm::mu_B") == Approx(0.05788));
CHECK(chai.eval<double>("constants::si::mu_B") == Catch::Approx(9.274));
CHECK(chai.eval<double>("constants::mm::mu_B") == Catch::Approx(0.05788));
}
TEST_CASE("Deeply nested namespaces via register_namespace") {
@ -1984,7 +1983,7 @@ TEST_CASE("Namespace block with var declarations") {
}
)");
CHECK(chai.eval<double>("config::pi") == Approx(3.14));
CHECK(chai.eval<double>("config::pi") == Catch::Approx(3.14));
CHECK(chai.eval<std::string>("config::name") == "hello");
}

View File

@ -20,31 +20,31 @@ int main() {
chaiscript_eval("var x = 42");
// Test evalString - same as Emscripten evalString()
std::string s = chaiscript_eval_string("to_string(x)");
[[maybe_unused]] std::string s = chaiscript_eval_string("to_string(x)");
assert(s == "42");
// Test evalInt - same as Emscripten evalInt()
int i = chaiscript_eval_int("1 + 2");
[[maybe_unused]] int i = chaiscript_eval_int("1 + 2");
assert(i == 3);
// Test evalBool - same as Emscripten evalBool()
bool b = chaiscript_eval_bool("true");
[[maybe_unused]] bool b = chaiscript_eval_bool("true");
assert(b == true);
b = chaiscript_eval_bool("false");
assert(b == false);
// Test evalFloat - same as Emscripten evalFloat()
float f = chaiscript_eval_float("1.5f");
[[maybe_unused]] float f = chaiscript_eval_float("1.5f");
assert(std::abs(f - 1.5f) < 0.001f);
// Test evalDouble - same as Emscripten evalDouble()
double d = chaiscript_eval_double("3.14");
[[maybe_unused]] double d = chaiscript_eval_double("3.14");
assert(std::abs(d - 3.14) < 0.001);
// Test a more complex expression
chaiscript_eval("def square(n) { return n * n; }");
int sq = chaiscript_eval_int("square(7)");
[[maybe_unused]] int sq = chaiscript_eval_int("square(7)");
assert(sq == 49);
return 0;

View File

@ -22,7 +22,7 @@ int main() {
// through the eval wrapper functions. In WASM builds without exception
// support, these would abort instead of throwing.
bool caught = false;
[[maybe_unused]] bool caught = false;
// Test 1: eval with undefined variable should throw
caught = false;
@ -64,7 +64,8 @@ int main() {
// Test 5: Verify normal operation still works after caught exceptions
chaiscript_eval("var post_exception_test = 100");
const int result = chaiscript_eval_int("post_exception_test");
[[maybe_unused]] const int result = chaiscript_eval_int("post_exception_test");
assert(result == 100 && "normal eval must work after caught exceptions");
std::cout << "All emscripten exception tests passed.\n";

View File

@ -16,9 +16,7 @@
#include <cstdlib>
#include <iostream>
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "catch_amalgamated.hpp"
TEST_CASE("Type_Info objects generate expected results") {
const auto test_type