Address review: merge upstream/develop and resolve test conflicts

Requested by @lefticus in PR #682 review.

Resolved conflicts in unittests/compiled_tests.cpp where upstream
clang-formatted the "Typed catch with no match" test cases while this
branch updated the expected exception type to chaiscript::exception::eval_error
(the new wrapping behavior introduced by this PR). Kept the eval_error
expectation and applied upstream's clang-format style.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
leftibot 2026-04-28 20:19:26 -06:00
commit b3046bf941
45 changed files with 28213 additions and 18754 deletions

View File

@ -1,10 +1,14 @@
# clang-format: 11
# clang-format: 19
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveBitFields: false
AllowShortBlocksOnASingleLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: true
BasedOnStyle: WebKit
BinPackArguments: true
@ -13,12 +17,14 @@ BreakBeforeBraces: Attach
ColumnLimit: 0
Cpp11BracedListStyle: true
FixNamespaceComments: true
IfMacros: ['SECTION']
IncludeBlocks: Preserve
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 2
KeepEmptyLinesAtTheStartOfBlocks: false
NamespaceIndentation: All
PackConstructorInitializers: CurrentLine
PenaltyBreakBeforeFirstCallParameter: 200
PenaltyBreakComment: 5
PenaltyBreakFirstLessLess: 50
@ -27,7 +33,11 @@ PointerAlignment: Right
SortIncludes: true
SpaceAfterTemplateKeyword: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeParens: Custom
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterIfMacros: false
SpaceInEmptyBlock: false
Standard: Latest
TabWidth: 2
UseTab: Never
UseTab: Never

27
.github/workflows/auto-clang-format.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: auto-clang-format
on:
push:
branches:
- develop
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: DoozyX/clang-format-lint-action@v0.20
with:
source: '.'
exclude: './third_party ./external ./unittests/catch.hpp'
extensions: 'h,cpp,hpp'
clangFormatVersion: 19
inplace: True
- uses: EndBug/add-and-commit@v9
with:
author_name: Clang Robot
author_email: robot@example.com
message: ':art: Committing clang-format changes'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

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

@ -11,14 +11,14 @@
#ifndef CHAISCRIPT_EMSCRIPTEN_EVAL_HPP_
#define CHAISCRIPT_EMSCRIPTEN_EVAL_HPP_
#include <string>
#include <chaiscript/chaiscript.hpp>
#include <string>
namespace detail {
inline chaiscript::ChaiScript &get_chai_instance() {
static chaiscript::ChaiScript chai;
return chai;
}
inline chaiscript::ChaiScript &get_chai_instance() {
static chaiscript::ChaiScript chai;
return chai;
}
} // namespace detail
inline void chaiscript_eval(const std::string &input) {

View File

@ -7,6 +7,16 @@
#ifndef CHAISCRIPT_DEFINES_HPP_
#define CHAISCRIPT_DEFINES_HPP_
// MacOSX Fix: std::get/std::get_if/std::visit for std::variant are annotated
// __attribute__((availability(macos,strict,introduced=10.14))) in Apple libc++.
// Defining this macro before any libc++ header strips those annotations so
// ChaiScript can be built against older MacOSX deployment targets.
#if defined(__APPLE__) && defined(__clang__)
#ifndef _LIBCPP_DISABLE_AVAILABILITY
#define _LIBCPP_DISABLE_AVAILABILITY
#endif
#endif
#ifdef _MSC_VER
#define CHAISCRIPT_STRINGIZE(x) "" #x
#define CHAISCRIPT_STRINGIZE_EXPANDED(x) CHAISCRIPT_STRINGIZE(x)
@ -59,10 +69,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

@ -18,11 +18,11 @@
#include "dispatchkit/function_call.hpp"
//#include "dispatchkit/dispatchkit.hpp"
// #include "dispatchkit/dispatchkit.hpp"
#include "dispatchkit/bootstrap.hpp"
#include "dispatchkit/bootstrap_stl.hpp"
#include "dispatchkit/operators.hpp"
//#include "dispatchkit/boxed_value.hpp"
// #include "dispatchkit/boxed_value.hpp"
#include "dispatchkit/register_function.hpp"
#include "language/chaiscript_prelude.hpp"
#include "utility/json_wrap.hpp"
@ -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

@ -65,7 +65,6 @@ namespace chaiscript {
return false;
}
bool is_explicit() const noexcept { return m_option_explicit; }
void set_explicit(const bool t_explicit) noexcept { m_option_explicit = t_explicit; }

View File

@ -148,11 +148,11 @@ namespace chaiscript {
};
/**
* A Proxy_Function implementation designed for creating a new
* Dynamic_Object
* that is automatically guarded based on the first param based on the
* param's type name
*/
* A Proxy_Function implementation designed for creating a new
* Dynamic_Object
* that is automatically guarded based on the first param based on the
* param's type name
*/
class Dynamic_Object_Constructor final : public Proxy_Function_Base {
public:
Dynamic_Object_Constructor(std::string t_type_name, const Proxy_Function &t_func)

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

@ -64,7 +64,7 @@ namespace chaiscript::dispatch::detail {
Function_Signature(Ret (Class::*f)(Param...) volatile &) -> Function_Signature<Ret, Function_Params<volatile Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile &noexcept)
Function_Signature(Ret (Class::*f)(Param...) volatile & noexcept)
-> Function_Signature<Ret, Function_Params<volatile Class &, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
@ -72,20 +72,20 @@ namespace chaiscript::dispatch::detail {
-> Function_Signature<Ret, Function_Params<volatile const Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile const &noexcept)
Function_Signature(Ret (Class::*f)(Param...) volatile const & noexcept)
-> Function_Signature<Ret, Function_Params<volatile const Class &, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) &) -> Function_Signature<Ret, Function_Params<Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) &noexcept) -> Function_Signature<Ret, Function_Params<Class &, Param...>, true, true>;
Function_Signature(Ret (Class::*f)(Param...) & noexcept) -> Function_Signature<Ret, Function_Params<Class &, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) const &) -> Function_Signature<Ret, Function_Params<const Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) const &noexcept) -> Function_Signature<Ret, Function_Params<const Class &, Param...>, true, true>;
Function_Signature(Ret (Class::*f)(Param...) const & noexcept) -> Function_Signature<Ret, Function_Params<const Class &, Param...>, true, true>;
// && reference specifier
@ -93,7 +93,7 @@ namespace chaiscript::dispatch::detail {
Function_Signature(Ret (Class::*f)(Param...) volatile &&) -> Function_Signature<Ret, Function_Params<volatile Class &&, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile &&noexcept)
Function_Signature(Ret (Class::*f)(Param...) volatile && noexcept)
-> Function_Signature<Ret, Function_Params<volatile Class &&, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
@ -101,20 +101,20 @@ namespace chaiscript::dispatch::detail {
-> Function_Signature<Ret, Function_Params<volatile const Class &&, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile const &&noexcept)
Function_Signature(Ret (Class::*f)(Param...) volatile const && noexcept)
-> Function_Signature<Ret, Function_Params<volatile const Class &&, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) &&) -> Function_Signature<Ret, Function_Params<Class &&, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) &&noexcept) -> Function_Signature<Ret, Function_Params<Class &&, Param...>, true, true>;
Function_Signature(Ret (Class::*f)(Param...) && noexcept) -> Function_Signature<Ret, Function_Params<Class &&, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) const &&) -> Function_Signature<Ret, Function_Params<const Class &&, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) const &&noexcept)
Function_Signature(Ret (Class::*f)(Param...) const && noexcept)
-> Function_Signature<Ret, Function_Params<const Class &&, Param...>, true, true>;
template<typename Ret, typename Class>

View File

@ -170,8 +170,8 @@ namespace chaiscript {
};
/**
* Used internally for handling a return value from a Proxy_Function call
*/
* Used internally for handling a return value from a Proxy_Function call
*/
template<>
struct Handle_Return<Boxed_Number> {
static Boxed_Value handle(const Boxed_Number &r) noexcept { return r.bv; }
@ -182,8 +182,8 @@ namespace chaiscript {
};
/**
* Used internally for handling a return value from a Proxy_Function call
*/
* Used internally for handling a return value from a Proxy_Function call
*/
template<>
struct Handle_Return<void> {
static Boxed_Value handle() { return void_var(); }

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;
@ -165,13 +165,13 @@ namespace chaiscript {
};
/**
* Pure virtual base class for all Proxy_Function implementations
* Proxy_Functions are a type erasure of type safe C++
* function calls. At runtime parameter types are expected to be
* tested against passed in types.
* Dispatch_Engine only knows how to work with Proxy_Function, no other
* function classes.
*/
* Pure virtual base class for all Proxy_Function implementations
* Proxy_Functions are a type erasure of type safe C++
* function calls. At runtime parameter types are expected to be
* tested against passed in types.
* Dispatch_Engine only knows how to work with Proxy_Function, no other
* function classes.
*/
class Proxy_Function_Base {
public:
virtual ~Proxy_Function_Base() = default;
@ -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

@ -32,9 +32,9 @@ namespace chaiscript {
namespace chaiscript {
namespace exception {
/**
* Exception thrown when there is a mismatch in number of
* parameters during Proxy_Function execution
*/
* Exception thrown when there is a mismatch in number of
* parameters during Proxy_Function execution
*/
struct arity_error : std::range_error {
arity_error(int t_got, int t_expected)
: std::range_error("Function dispatch arity mismatch")
@ -54,9 +54,9 @@ namespace chaiscript {
namespace dispatch {
namespace detail {
/**
* Used by Proxy_Function_Impl to return a list of all param types
* it contains.
*/
* Used by Proxy_Function_Impl to return a list of all param types
* it contains.
*/
template<typename Ret, typename... Params>
std::vector<Type_Info> build_param_type_list(Ret (*)(Params...)) {
/// \note somehow this is responsible for a large part of the code generation
@ -64,10 +64,10 @@ namespace chaiscript {
}
/**
* Used by Proxy_Function_Impl to determine if it is equivalent to another
* Proxy_Function_Impl object. This function is primarily used to prevent
* registration of two functions with the exact same signatures
*/
* Used by Proxy_Function_Impl to determine if it is equivalent to another
* Proxy_Function_Impl object. This function is primarily used to prevent
* registration of two functions with the exact same signatures
*/
template<typename Ret, typename... Params>
bool compare_types_cast(Ret (*)(Params...), const chaiscript::Function_Params &params, const Type_Conversions_State &t_conversions) noexcept {
try {
@ -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

@ -498,6 +498,29 @@ namespace chaiscript {
func);
}
namespace detail {
template<typename T>
struct is_std_vector : std::false_type {};
template<typename T, typename A>
struct is_std_vector<std::vector<T, A>> : std::true_type {};
template<typename T>
T convert_vector_element(const Boxed_Value &bv) {
if constexpr (is_std_vector<T>::value) {
const auto &inner = Cast_Helper<const std::vector<Boxed_Value> &>::cast(bv, nullptr);
T result;
result.reserve(inner.size());
for (const Boxed_Value &elem : inner) {
result.push_back(convert_vector_element<typename T::value_type>(elem));
}
return result;
} else {
return Cast_Helper<T>::cast(bv, nullptr);
}
}
} // namespace detail
template<typename To>
Type_Conversion vector_conversion() {
auto func = [](const Boxed_Value &t_bv) -> Boxed_Value {
@ -506,7 +529,7 @@ namespace chaiscript {
To vec;
vec.reserve(from_vec.size());
for (const Boxed_Value &bv : from_vec) {
vec.push_back(detail::Cast_Helper<typename To::value_type>::cast(bv, nullptr));
vec.push_back(detail::convert_vector_element<typename To::value_type>(bv));
}
return Boxed_Value(std::move(vec));
@ -542,9 +565,8 @@ namespace chaiscript {
= detail::Cast_Helper<const std::pair<Boxed_Value, Boxed_Value> &>::cast(t_bv, nullptr);
auto pair = std::make_pair(
detail::Cast_Helper<Left>::cast(from_pair.first, nullptr),
detail::Cast_Helper<Right>::cast(from_pair.second, nullptr)
);
detail::Cast_Helper<Left>::cast(from_pair.first, nullptr),
detail::Cast_Helper<Right>::cast(from_pair.second, nullptr));
return Boxed_Value(std::move(pair));
};

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

@ -131,9 +131,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
@ -627,13 +627,13 @@ namespace chaiscript {
} // namespace exception
//static
// static
bool AST_Node::get_bool_condition(const Boxed_Value &t_bv, const chaiscript::detail::Dispatch_State &t_ss) {
try {
return t_ss->boxed_cast<bool>(t_bv);
} catch (const exception::bad_boxed_cast &) {
throw exception::eval_error("Condition not boolean");
}
try {
return t_ss->boxed_cast<bool>(t_bv);
} catch (const exception::bad_boxed_cast &) {
throw exception::eval_error("Condition not boolean");
}
}
namespace parser {

View File

@ -128,7 +128,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);
}
@ -143,12 +143,14 @@ namespace chaiscript {
m_engine.add(fun([this](const std::string &s) { m_print_handler(s + "\n"); }), "println_string");
m_engine.add(fun([this](const std::function<void(const std::string &)> &t_handler) {
m_print_handler = t_handler;
}), "set_print_handler");
m_print_handler = t_handler;
}),
"set_print_handler");
m_engine.add(fun([this](const std::function<std::string(const std::string &)> &t_reader) {
m_file_reader = t_reader;
}), "set_file_reader");
m_file_reader = t_reader;
}),
"set_file_reader");
m_engine.add(fun([this]() { m_engine.dump_system(); }), "dump_system");
m_engine.add(fun([this](const Boxed_Value &t_bv) { m_engine.dump_object(t_bv); }), "dump_object");
@ -297,7 +299,6 @@ namespace chaiscript {
}
public:
/// \brief Set a custom handler for print output, used by both print_string and println_string
/// \param[in] t_handler Function to call with the string to print
void set_print_handler(std::function<void(const std::string &)> t_handler) {
@ -312,7 +313,7 @@ namespace chaiscript {
/// \brief Virtual destructor for ChaiScript
virtual ~ChaiScript_Basic() = default;
/// \brief Constructor for ChaiScript
/// \param[in] t_lib Standard library to apply to this ChaiScript instance
/// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module
@ -322,7 +323,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))
@ -616,7 +617,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
@ -792,7 +793,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;
}));
@ -801,7 +802,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

@ -10,13 +10,13 @@
#ifndef CHAISCRIPT_EVAL_HPP_
#define CHAISCRIPT_EVAL_HPP_
#include <algorithm>
#include <exception>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <ostream>
#include <algorithm>
#include <stdexcept>
#include <string>
#include <vector>
@ -393,11 +393,11 @@ namespace chaiscript {
// static and public so we can use this to process Switch_AST_Node case equality
static Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss,
Operators::Opers t_oper,
const std::string &t_oper_string,
const Boxed_Value &t_lhs,
const Boxed_Value &t_rhs,
std::atomic_uint_fast32_t &t_loc) {
Operators::Opers t_oper,
const std::string &t_oper_string,
const Boxed_Value &t_lhs,
const Boxed_Value &t_rhs,
std::atomic_uint_fast32_t &t_loc) {
try {
if (t_oper != Operators::Opers::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic()) {
// If it's an arithmetic operation we want to short circuit dispatch
@ -1250,7 +1250,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;
{
@ -1275,8 +1275,7 @@ namespace chaiscript {
const auto process_statement = [&](const AST_Node_Impl<T> &stmt) {
if (stmt.identifier == AST_Node_Type::Def) {
const auto &def_node = static_cast<const Def_AST_Node<T> &>(stmt);
target_ns[def_node.children[0]->text] =
Boxed_Value(Def_AST_Node<T>::make_proxy_function(def_node, t_ss));
target_ns[def_node.children[0]->text] = Boxed_Value(Def_AST_Node<T>::make_proxy_function(def_node, t_ss));
} else if (stmt.identifier == AST_Node_Type::Assign_Decl
|| stmt.identifier == AST_Node_Type::Const_Assign_Decl) {
const auto &var_name = stmt.children[0]->text;
@ -1355,7 +1354,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;
@ -1651,8 +1650,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);
@ -1760,7 +1759,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

@ -1,8 +1,8 @@
#ifndef CHAISCRIPT_SIMPLEJSON_WRAP_HPP
#define CHAISCRIPT_SIMPLEJSON_WRAP_HPP
#include "json.hpp"
#include "../dispatchkit/dynamic_object.hpp"
#include "json.hpp"
namespace chaiscript {
class json_wrap {

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 = "";

View File

@ -6,20 +6,19 @@
#pragma once
//#include "hash.h"
// #include "hash.h"
#include <string>
// define fixed size integer types
#ifdef _MSC_VER
// Windows
typedef unsigned __int8 uint8_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int64 uint64_t;
#else
// GCC
#include <stdint.h>
#endif
/// compute SHA3 hash
/** Usage:
SHA3 sha3;
@ -37,18 +36,21 @@ class SHA3 //: public Hash
{
public:
/// algorithm variants
enum Bits { Bits224 = 224, Bits256 = 256, Bits384 = 384, Bits512 = 512 };
enum Bits { Bits224 = 224,
Bits256 = 256,
Bits384 = 384,
Bits512 = 512 };
/// same as reset()
explicit SHA3(Bits bits = Bits256);
/// compute hash of a memory block
std::string operator()(const void* data, size_t numBytes);
std::string operator()(const void *data, size_t numBytes);
/// compute hash of a string, excluding final zero
std::string operator()(const std::string& text);
std::string operator()(const std::string &text);
/// add arbitrary number of bytes
void add(const void* data, size_t numBytes);
void add(const void *data, size_t numBytes);
/// return latest hash as hex characters
std::string getHash();
@ -58,24 +60,24 @@ public:
private:
/// process a full block
void processBlock(const void* data);
void processBlock(const void *data);
/// process everything left in the internal buffer
void processBuffer();
/// 1600 bits, stored as 25x64 bit, BlockSize is no more than 1152 bits (Keccak224)
enum { StateSize = 1600 / (8 * 8),
MaxBlockSize = 200 - 2 * (224 / 8) };
enum { StateSize = 1600 / (8 * 8),
MaxBlockSize = 200 - 2 * (224 / 8) };
/// hash
uint64_t m_hash[StateSize];
/// size of processed data in bytes
uint64_t m_numBytes;
/// block size (less or equal to MaxBlockSize)
size_t m_blockSize;
size_t m_blockSize;
/// valid bytes in m_buffer
size_t m_bufferSize;
size_t m_bufferSize;
/// bytes not processed yet
uint8_t m_buffer[MaxBlockSize];
uint8_t m_buffer[MaxBlockSize];
/// variant
Bits m_bits;
Bits m_bits;
};

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") {
@ -196,7 +195,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));
}
}
@ -766,7 +765,9 @@ TEST_CASE("Utility_Test utility class wrapper for enum") {
}
// Issue #601: add_class for enums should work directly with ChaiScript reference
enum class Issue601_EnumClass { Apple, Banana, Pear };
enum class Issue601_EnumClass { Apple,
Banana,
Pear };
TEST_CASE("Issue 601: add_class enum with ChaiScript reference directly") {
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
@ -787,7 +788,9 @@ TEST_CASE("Issue 601: add_class enum with ChaiScript reference directly") {
}
// Also test non-scoped enum directly with ChaiScript reference
enum Issue601_PlainEnum { Issue601_Red = 0, Issue601_Green = 1, Issue601_Blue = 2 };
enum Issue601_PlainEnum { Issue601_Red = 0,
Issue601_Green = 1,
Issue601_Blue = 2 };
TEST_CASE("Issue 601: add_class plain enum with ChaiScript reference directly") {
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
@ -1113,7 +1116,7 @@ TEST_CASE("Pair conversions") {
const auto p = chai.eval<std::pair<std::string, std::string>>(R"cs(
Pair("chai", "script");
)cs");
CHECK(p.first == std::string{ "chai" });
CHECK(p.first == std::string{"chai"});
CHECK(p.second == "script");
}
{
@ -1121,7 +1124,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));
}
}
@ -1133,7 +1136,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");
}
@ -1427,7 +1430,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
@ -1693,7 +1696,8 @@ TEST_CASE("ChaiScript default has all functions") {
TEST_CASE("Issue #421 - Switch with type_conversion does not compare destroyed objects") {
struct MyType {
int value;
explicit MyType(int v) : value(v) {}
explicit MyType(int v)
: value(v) {}
};
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
@ -1916,51 +1920,79 @@ TEST_CASE("push_back on script vector with vector_conversion") {
// Register a C++ function that accepts the converted type, so we can
// verify that vector_conversion actually works for passing vectors
chai.add(chaiscript::fun([](const std::vector<std::string> &v) -> std::string {
std::string result;
for (const auto &s : v) {
if (!result.empty()) { result += ","; }
result += s;
}
return result;
}), "join_strings");
std::string result;
for (const auto &s : v) {
if (!result.empty()) { result += ","; }
result += s;
}
return result;
}),
"join_strings");
// push_back on an empty script-created vector must be visible
CHECK(chai.eval<bool>(
"auto x = [];"
"x.push_back(\"Hello\");"
"x.size() == 1"
));
"auto x = [];"
"x.push_back(\"Hello\");"
"x.size() == 1"));
// push_back on a vector with initial elements must grow correctly
CHECK(chai.eval<bool>(
"auto y = [\"a\", \"b\"];"
"y.push_back(\"c\");"
"y.push_back(\"d\");"
"y.size() == 4"
));
"auto y = [\"a\", \"b\"];"
"y.push_back(\"c\");"
"y.push_back(\"d\");"
"y.size() == 4"));
// Verify the actual content is preserved after push_back
CHECK(chai.eval<std::string>(
"auto z = [];"
"z.push_back(\"World\");"
"z[0]"
) == "World");
"auto z = [];"
"z.push_back(\"World\");"
"z[0]")
== "World");
// Round-trip: build a vector in script, push_back elements, then pass it
// to a C++ function via vector_conversion and verify the contents
CHECK(chai.eval<std::string>(
"auto v = [\"one\", \"two\"];"
"v.push_back(\"three\");"
"join_strings(v)"
) == "one,two,three");
"auto v = [\"one\", \"two\"];"
"v.push_back(\"three\");"
"join_strings(v)")
== "one,two,three");
// Verify conversion works on a freshly created vector too
CHECK(chai.eval<std::string>(
"auto w = [];"
"w.push_back(\"hello\");"
"w.push_back(\"world\");"
"join_strings(w)"
) == "hello,world");
"auto w = [];"
"w.push_back(\"hello\");"
"w.push_back(\"world\");"
"join_strings(w)")
== "hello,world");
}
TEST_CASE("vector of vectors conversion (issue #374)") {
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
auto m = std::make_shared<chaiscript::Module>();
chaiscript::bootstrap::standard_library::vector_type<std::vector<double>>("VectorDouble", *m);
chaiscript::bootstrap::standard_library::vector_type<std::vector<std::vector<double>>>("VectorVectorDouble", *m);
m->add(chaiscript::vector_conversion<std::vector<double>>());
m->add(chaiscript::vector_conversion<std::vector<std::vector<double>>>());
chai.add(m);
chai.add(chaiscript::fun([](const std::vector<std::vector<double>> &v) -> double {
double sum = 0;
for (const auto &inner : v) {
for (const auto d : inner) {
sum += d;
}
}
return sum;
}),
"sum_nested");
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();"
"v = [[1.0, 2.0], [3.0, 4.0]];"
"v.size() == 2"));
}
// Regression test for issue #607: AST_Node_Trace must be a complete type
@ -2038,12 +2070,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") {
@ -2112,7 +2144,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");
}
@ -2123,20 +2155,23 @@ TEST_CASE("Namespace block rejects non-declaration statements") {
namespace bad {
1 + 2
}
)"), chaiscript::exception::eval_error);
)"),
chaiscript::exception::eval_error);
CHECK_THROWS_AS(chai.eval(R"(
namespace bad {
print("hello")
}
)"), chaiscript::exception::eval_error);
)"),
chaiscript::exception::eval_error);
CHECK_THROWS_AS(chai.eval(R"(
var x = 5
namespace bad {
x = 10
}
)"), chaiscript::exception::eval_error);
)"),
chaiscript::exception::eval_error);
}
TEST_CASE("C++ runtime_error thrown from registered function is catchable in ChaiScript") {
@ -2224,7 +2259,8 @@ TEST_CASE("Typed catch with no match propagates exception") {
catch(string e) {
// wrong type, should not match — exception propagates
}
)"), chaiscript::exception::eval_error);
)"),
chaiscript::exception::eval_error);
}
TEST_CASE("Typed catch with no match still runs finally block") {
@ -2241,7 +2277,8 @@ TEST_CASE("Typed catch with no match still runs finally block") {
finally {
finally_ran = true
}
)"), chaiscript::exception::eval_error);
)"),
chaiscript::exception::eval_error);
CHECK(chai.eval<bool>("finally_ran") == true);
}
@ -2250,13 +2287,14 @@ TEST_CASE("Multiple C++ exception types from registered functions") {
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
chai.add(chaiscript::fun([](int which) -> int {
switch (which) {
case 0: throw std::runtime_error("runtime");
case 1: throw std::out_of_range("range");
case 2: throw std::logic_error("logic");
default: return which;
}
}), "cpp_multi_throw");
switch (which) {
case 0: throw std::runtime_error("runtime");
case 1: throw std::out_of_range("range");
case 2: throw std::logic_error("logic");
default: return which;
}
}),
"cpp_multi_throw");
CHECK(chai.eval<int>(R"(
var catch_count = 0
@ -2284,8 +2322,9 @@ TEST_CASE("Exception from C++ binary operator is catchable in ChaiScript") {
chai.add(chaiscript::user_type<ThrowingType>(), "ThrowingType");
chai.add(chaiscript::constructor<ThrowingType(int)>(), "ThrowingType");
chai.add(chaiscript::fun([](const ThrowingType &, const ThrowingType &) -> ThrowingType {
throw std::runtime_error("cpp operator+ threw");
}), "+");
throw std::runtime_error("cpp operator+ threw");
}),
"+");
CHECK(chai.eval<bool>(R"(
var caught = false
@ -2311,9 +2350,10 @@ TEST_CASE("Exception from C++ [] operator is catchable in ChaiScript") {
chai.add(chaiscript::user_type<IndexableType>(), "IndexableType");
chai.add(chaiscript::constructor<IndexableType(int)>(), "IndexableType");
chai.add(chaiscript::fun([](const IndexableType &, int idx) -> int {
if (idx < 0) { throw std::out_of_range("negative index"); }
return idx;
}), "[]");
if (idx < 0) { throw std::out_of_range("negative index"); }
return idx;
}),
"[]");
CHECK(chai.eval<int>("var obj = IndexableType(0); obj[5]") == 5);

View File

@ -9,9 +9,9 @@
#define CHAISCRIPT_NO_DYNLOAD
#endif
#include <chaiscript/chaiscript.hpp>
#include "../emscripten/chaiscript_eval.hpp"
#include <cassert>
#include <chaiscript/chaiscript.hpp>
#include <cmath>
#include <string>
@ -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

@ -10,9 +10,9 @@
#define CHAISCRIPT_NO_DYNLOAD
#endif
#include <chaiscript/chaiscript.hpp>
#include "../emscripten/chaiscript_eval.hpp"
#include <cassert>
#include <chaiscript/chaiscript.hpp>
#include <iostream>
#include <stdexcept>
#include <string>
@ -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

@ -5,8 +5,8 @@
Multi_Test_Chai::Multi_Test_Chai()
: m_chai(new chaiscript::ChaiScript_Basic(
chaiscript::Std_Lib::library(),
std::make_unique<chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Noop_Tracer, chaiscript::optimizer::Optimizer_Default>>())) {
chaiscript::Std_Lib::library(),
std::make_unique<chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Noop_Tracer, chaiscript::optimizer::Optimizer_Default>>())) {
}
std::shared_ptr<chaiscript::ChaiScript_Basic> Multi_Test_Chai::get_chai() {

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