Merge branch 'develop' into best_practices

This commit is contained in:
Jason Turner 2018-05-30 08:30:29 -06:00
commit a880319db8
91 changed files with 12608 additions and 8808 deletions

View File

@ -24,10 +24,10 @@ matrix:
sudo: false
env: GCC_VER="4.9"
compiler: gcc
- os: linux
sudo: false
env: GCC_VER="4.9" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1
compiler: gcc
# - os: linux
#sudo: false
#env: GCC_VER="6" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1
#compiler: gcc
- os: linux
sudo: false
env: GCC_VER="5" CPPCHECK=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE"

View File

@ -4,13 +4,6 @@ if(NOT ${CMAKE_VERSION} VERSION_LESS "3.1")
cmake_policy(SET CMP0054 NEW)
endif()
IF(BIICODE)
INIT_BIICODE_BLOCK()
ADD_BIICODE_TARGETS()
ELSE()
# Your regular CMakeLists configuration here
project(chaiscript)
option(MULTITHREAD_SUPPORT_ENABLED "Multithreaded Support Enabled" TRUE)
@ -49,6 +42,8 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if(ENABLE_ADDRESS_SANITIZER)
add_definitions(-fsanitize=address -g)
set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=address")
option(BUILD_LIBFUZZ_TESTER "Build libfuzzer tool" FALSE)
endif()
option(ENABLE_MEMORY_SANITIZER "Enable memory sanitizer testing in gcc/clang" FALSE)
@ -102,7 +97,7 @@ set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/license.txt")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/readme.md")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/description.txt")
set(CPACK_PACKAGE_VERSION_MAJOR 6)
set(CPACK_PACKAGE_VERSION_MAJOR 7)
set(CPACK_PACKAGE_VERSION_MINOR 0)
set(CPACK_PACKAGE_VERSION_PATCH 0)
@ -123,6 +118,7 @@ configure_file(Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile)
include(CTest)
include(CPack)
include(cmake/Catch.cmake)
if(NOT MINGW)
find_library(READLINE_LIBRARY NAMES readline PATH /usr/lib /usr/local/lib /opt/local/lib)
@ -155,6 +151,9 @@ endif()
if(MSVC)
add_definitions(/std:c++latest /W4 /w14545 /w34242 /w34254 /w34287 /w44263 /w44265 /w44296 /w44311 /w44826 /we4289 /w14546 /w14547 /w14549 /w14555 /w14619 /w14905 /w14906 /w14928)
add_definitions(/std:c++17)
if (MSVC_VERSION STREQUAL "1800")
# VS2013 doesn't have magic statics
add_definitions(/w44640)
@ -163,7 +162,7 @@ if(MSVC)
add_definitions(/w34062)
endif()
add_definitions(/bigobj)
add_definitions(/bigobj /permissive-)
# Note on MSVC compiler flags.
# The code base selective disables warnings as necessary when the compiler is complaining too much
# about something that is perfectly valid, or there is simply no technical way around it
@ -262,8 +261,8 @@ add_executable(chai src/main.cpp ${Chai_INCLUDES})
target_link_libraries(chai ${LIBS} ${CHAISCRIPT_LIBS})
if(BUILD_SAMPLES)
add_executable(example samples/example.cpp)
target_link_libraries(example ${LIBS})
add_executable(sanity_checks src/sanity_checks.cpp)
target_link_libraries(sanity_checks ${LIBS})
add_executable(test_num_exceptions samples/test_num_exceptions.cpp)
target_link_libraries(test_num_exceptions ${LIBS} ${CHAISCRIPT_LIBS})
add_executable(memory_leak_test samples/memory_leak_test.cpp)
@ -287,6 +286,7 @@ if(BUILD_MODULES)
set(MODULES stl_extra)
endif()
file(GLOB UNIT_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/unittests/ ${CMAKE_CURRENT_SOURCE_DIR}/unittests/*.chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/3.x/*.chai)
list(SORT UNIT_TESTS)
@ -411,7 +411,7 @@ if(BUILD_TESTING)
if(NOT UNIT_TEST_LIGHT)
add_executable(compiled_tests unittests/compiled_tests.cpp)
target_link_libraries(compiled_tests ${LIBS} ${CHAISCRIPT_LIBS})
ADD_CATCH_TESTS(compiled_tests)
catch_discover_tests(compiled_tests TEST_PREFIX "compiled.")
add_executable(static_chaiscript_test unittests/static_chaiscript.cpp)
target_link_libraries(static_chaiscript_test ${LIBS})
@ -458,6 +458,15 @@ if(BUILD_TESTING)
endif()
endif()
if(BUILD_LIBFUZZ_TESTER)
add_executable(fuzzer src/libfuzzer_client.cpp src/sha3.cpp)
target_compile_options(fuzzer PRIVATE "-fsanitize=fuzzer,address")
target_link_libraries(fuzzer PRIVATE ${LIBS} "-fsanitize=fuzzer,address")
endif()
install(TARGETS chai chaiscript_stdlib-${CHAI_VERSION} ${MODULES} RUNTIME DESTINATION bin LIBRARY DESTINATION lib/chaiscript)
install(DIRECTORY include/chaiscript DESTINATION include
@ -482,5 +491,3 @@ install(FILES "${chaiscript_BINARY_DIR}/lib/pkgconfig/chaiscript.pc"
DESTINATION lib/pkgconfig)
ENDIF()

View File

@ -1,4 +1,6 @@
Copyright 2009-2016 Jason Turner
BSD-3-Clause License
Copyright 2009-2018 Jason Turner
Copyright 2009-2012 Jonathan Turner.
All Rights Reserved.

View File

@ -1,4 +1,4 @@
version: 5.8.x.{build}
version: 6.1.x.{build}
image:
- Visual Studio 2017
environment:
@ -11,7 +11,7 @@ build_script:
cd build
cmake c:\Projects\chaiscript -G "%VS_VERSION%"
cmake c:\Projects\chaiscript -G "%VS_VERSION%" -DBUILD_TESTING:BOOL=ON -DBUILD_MODULES:BOOL=ON
cmake --build . --config Debug
test_script:

View File

@ -370,6 +370,25 @@ if (expression) { }
if (statement; expression) { }
```
## Switch Statements
``` chaiscript
var myvalue = 2
switch (myvalue) {
case (1) {
print("My Value is 1");
break;
}
case (2) {
print("My Value is 2");
break;
}
default {
print("My Value is something else.";
}
}
```
## Built in Types
```

175
cmake/Catch.cmake Normal file
View File

@ -0,0 +1,175 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
Catch
-----
This module defines a function to help use the Catch test framework.
The :command:`catch_discover_tests` discovers tests by asking the compiled test
executable to enumerate its tests. This does not require CMake to be re-run
when tests change. However, it may not work in a cross-compiling environment,
and setting test properties is less convenient.
This command is intended to replace use of :command:`add_test` to register
tests, and will create a separate CTest test for each Catch test case. Note
that this is in some cases less efficient, as common set-up and tear-down logic
cannot be shared by multiple test cases executing in the same instance.
However, it provides more fine-grained pass/fail information to CTest, which is
usually considered as more beneficial. By default, the CTest test name is the
same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
.. command:: catch_discover_tests
Automatically add tests with CTest by querying the compiled test executable
for available tests::
catch_discover_tests(target
[TEST_SPEC arg1...]
[EXTRA_ARGS arg1...]
[WORKING_DIRECTORY dir]
[TEST_PREFIX prefix]
[TEST_SUFFIX suffix]
[PROPERTIES name1 value1...]
[TEST_LIST var]
)
``catch_discover_tests`` sets up a post-build command on the test executable
that generates the list of tests by parsing the output from running the test
with the ``--list-test-names-only`` argument. This ensures that the full
list of tests is obtained. Since test discovery occurs at build time, it is
not necessary to re-run CMake when the list of tests changes.
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
in order to function in a cross-compiling environment.
Additionally, setting properties on tests is somewhat less convenient, since
the tests are not available at CMake time. Additional test properties may be
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
more fine-grained test control is needed, custom content may be provided
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
directory property. The set of discovered tests is made accessible to such a
script via the ``<target>_TESTS`` variable.
The options are:
``target``
Specifies the Catch executable, which must be a known CMake executable
target. CMake will substitute the location of the built executable when
running the test.
``TEST_SPEC arg1...``
Specifies test cases, wildcarded test cases, tags and tag expressions to
pass to the Catch executable with the ``--list-test-names-only`` argument.
``EXTRA_ARGS arg1...``
Any extra arguments to pass on the command line to each test case.
``WORKING_DIRECTORY dir``
Specifies the directory in which to run the discovered test cases. If this
option is not provided, the current binary directory is used.
``TEST_PREFIX prefix``
Specifies a ``prefix`` to be prepended to the name of each discovered test
case. This can be useful when the same test executable is being used in
multiple calls to ``catch_discover_tests()`` but with different
``TEST_SPEC`` or ``EXTRA_ARGS``.
``TEST_SUFFIX suffix``
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
be specified.
``PROPERTIES name1 value1...``
Specifies additional properties to be set on all tests discovered by this
invocation of ``catch_discover_tests``.
``TEST_LIST var``
Make the list of tests available in the variable ``var``, rather than the
default ``<target>_TESTS``. This can be useful when the same test
executable is being used in multiple calls to ``catch_discover_tests()``.
Note that this variable is only available in CTest.
#]=======================================================================]
#------------------------------------------------------------------------------
function(catch_discover_tests TARGET)
cmake_parse_arguments(
""
""
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST"
"TEST_SPEC;EXTRA_ARGS;PROPERTIES"
${ARGN}
)
if(NOT _WORKING_DIRECTORY)
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
endif()
if(NOT _TEST_LIST)
set(_TEST_LIST ${TARGET}_TESTS)
endif()
## Generate a unique name based on the extra arguments
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
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")
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
)
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}"
)
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}"
)
else()
message(FATAL_ERROR
"Cannot set more than one TEST_INCLUDE_FILE"
)
endif()
endif()
endfunction()
###############################################################################
set(_CATCH_DISCOVER_TESTS_SCRIPT
${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
)

76
cmake/CatchAddTests.cmake Normal file
View File

@ -0,0 +1,76 @@
# 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})
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
else()
set(_args "${_args} ${_arg}")
endif()
endforeach()
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"
)
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,185 @@
#==================================================================================================#
# supported macros #
# - TEST_CASE, #
# - SCENARIO, #
# - TEST_CASE_METHOD, #
# - CATCH_TEST_CASE, #
# - CATCH_SCENARIO, #
# - CATCH_TEST_CASE_METHOD. #
# #
# Usage #
# 1. make sure this module is in the path or add this otherwise: #
# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") #
# 2. make sure that you've enabled testing option for the project by the call: #
# enable_testing() #
# 3. add the lines to the script for testing target (sample CMakeLists.txt): #
# project(testing_target) #
# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") #
# enable_testing() #
# #
# find_path(CATCH_INCLUDE_DIR "catch.hpp") #
# include_directories(${INCLUDE_DIRECTORIES} ${CATCH_INCLUDE_DIR}) #
# #
# file(GLOB SOURCE_FILES "*.cpp") #
# add_executable(${PROJECT_NAME} ${SOURCE_FILES}) #
# #
# include(ParseAndAddCatchTests) #
# ParseAndAddCatchTests(${PROJECT_NAME}) #
# #
# The following variables affect the behavior of the script: #
# #
# PARSE_CATCH_TESTS_VERBOSE (Default OFF) #
# -- enables debug messages #
# PARSE_CATCH_TESTS_NO_HIDDEN_TESTS (Default OFF) #
# -- excludes tests marked with [!hide], [.] or [.foo] tags #
# PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME (Default ON) #
# -- adds fixture class name to the test name #
# PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME (Default ON) #
# -- adds cmake target name to the test name #
# 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 #
# #
#==================================================================================================#
cmake_minimum_required(VERSION 2.8.8)
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)
option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the test name" ON)
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()
endfunction()
# This removes the contents between
# - block comments (i.e. /* ... */)
# - full line comments (i.e. // ... )
# contents have been read into '${CppCode}'.
# !keep partial line comments
function(RemoveComments CppCode)
string(ASCII 2 CMakeBeginBlockComment)
string(ASCII 3 CMakeEndBlockComment)
string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}")
string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}")
string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}")
string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}")
set(${CppCode} "${${CppCode}}" PARENT_SCOPE)
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()
endif()
PrintDebugMessage("parsing ${SourceFile}")
file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME)
# Remove block and fullline comments
RemoveComments(Contents)
# 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}")
if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests)
PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property")
set_property(
DIRECTORY
APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile}
)
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()
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}")
endfunction()

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_BASIC_HPP_

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_DEFINES_HPP_
@ -76,7 +76,7 @@ static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later req
#include <cmath>
namespace chaiscript {
constexpr static const int version_major = 6;
constexpr static const int version_major = 7;
constexpr static const int version_minor = 0;
constexpr static const int version_patch = 0;
@ -153,8 +153,7 @@ namespace chaiscript {
template<typename T>
[[nodiscard]] constexpr auto parse_num(const std::string_view &t_str) noexcept
-> typename std::enable_if<std::is_integral<T>::value, T>::type
[[nodiscard]] constexpr auto parse_num(const std::string_view t_str) noexcept -> typename std::enable_if<std::is_integral<T>::value, T>::type
{
T t = 0;
for (const auto c : t_str) {
@ -168,60 +167,15 @@ namespace chaiscript {
}
template<typename T>
[[nodiscard]] auto parse_num(const std::string_view &t_str) noexcept
-> typename std::enable_if<!std::is_integral<T>::value, T>::type
{
T t = 0;
T base = 0;
T decimal_place = 0;
bool exponent = false;
bool neg_exponent = false;
const auto final_value = [](const T val, const T baseval, const bool hasexp, const bool negexp) -> T {
if (!hasexp) {
return val;
} else {
return baseval * std::pow(T(10), val*T(negexp?-1:1));
}
};
for (const auto c : t_str) {
if (c == '.') {
decimal_place = 10;
} else if (c == 'e' || c == 'E') {
exponent = true;
decimal_place = 0;
base = t;
t = 0;
} else if (c == '-' && exponent) {
neg_exponent = true;
} else if (c == '+' && exponent) {
neg_exponent = false;
} else if (c < '0' || c > '9') {
return final_value(t, base, exponent, neg_exponent);
} else if (decimal_place < T(10)) {
t *= T(10);
t += T(c - '0');
} else {
t += (T(c - '0') / (T(decimal_place)));
decimal_place *= 10;
}
}
return final_value(t, base, exponent, neg_exponent);
}
template<typename T>
auto parse_num(const char *t_str) -> typename std::enable_if<!std::is_integral<T>::value, T>::type
[[nodiscard]] auto parse_num(const std::string_view t_str) -> typename std::enable_if<!std::is_integral<T>::value, T>::type
{
T t = 0;
T base;
T base{};
T decimal_place = 0;
int exponent = 0;
for (char c;; ++t_str) {
c = *t_str;
for (const auto c : t_str) {
switch (c)
{
case '.':
@ -251,17 +205,18 @@ namespace chaiscript {
case '9':
if (decimal_place < 10) {
t *= 10;
t += c - '0';
t += static_cast<T>(c - '0');
}
else {
t += (c - '0') / decimal_place;
t += static_cast<T>(c - '0') / decimal_place;
decimal_place *= 10;
}
break;
default:
return exponent ? base * std::pow(T(10), t * exponent) : t;
break;
}
}
return exponent ? base * std::pow(T(10), t * static_cast<T>(exponent)) : t;
}
struct str_equal {

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
@ -135,6 +135,7 @@ namespace chaiscript
construct_pod<T>(name, m);
m.add(fun(&parse_string<T>), "to_" + name);
m.add(fun([](const T t){ return t; }), "to_" + name);
}
@ -454,7 +455,7 @@ namespace chaiscript
m.add(fun([](const char c) { return std::string(1, c); }), "to_string");
m.add(fun(&Boxed_Number::to_string), "to_string");
bootstrap_pod_type<double>("double", m);
bootstrap_pod_type<long double>("long_double", m);
bootstrap_pod_type<float>("float", m);

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -0,0 +1,107 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_CALLABLE_TRAITS_HPP_
#define CHAISCRIPT_CALLABLE_TRAITS_HPP_
#include <memory>
namespace chaiscript {
namespace dispatch {
namespace detail {
template<typename Class, typename ... Param>
struct Constructor
{
template<typename ... Inner>
std::shared_ptr<Class> operator()(Inner&& ... inner) const {
return std::make_shared<Class>(std::forward<Inner>(inner)...);
}
};
template<typename Ret, typename Class, typename ... Param>
struct Const_Caller
{
explicit Const_Caller(Ret (Class::*t_func)(Param...) const) : m_func(t_func) {}
template<typename ... Inner>
Ret operator()(const Class &o, Inner&& ... inner) const {
return (o.*m_func)(std::forward<Inner>(inner)...);
}
Ret (Class::*m_func)(Param...) const;
};
template<typename Ret, typename ... Param>
struct Fun_Caller
{
explicit Fun_Caller(Ret( * t_func)(Param...) ) : m_func(t_func) {}
template<typename ... Inner>
Ret operator()(Inner&& ... inner) const {
return (m_func)(std::forward<Inner>(inner)...);
}
Ret(*m_func)(Param...);
};
template<typename Ret, typename Class, typename ... Param>
struct Caller
{
explicit Caller(Ret (Class::*t_func)(Param...)) : m_func(t_func) {}
template<typename ... Inner>
Ret operator()(Class &o, Inner&& ... inner) const {
return (o.*m_func)(std::forward<Inner>(inner)...);
}
Ret (Class::*m_func)(Param...);
};
template<typename T>
struct Arity
{
};
template<typename Ret, typename ... Params>
struct Arity<Ret (Params...)>
{
static const size_t arity = sizeof...(Params);
};
template<typename T>
struct Function_Signature
{
};
template<typename Ret, typename ... Params>
struct Function_Signature<Ret (Params...)>
{
typedef Ret Return_Type;
typedef Ret (Signature)(Params...);
};
template<typename Ret, typename T, typename ... Params>
struct Function_Signature<Ret (T::*)(Params...) const>
{
typedef Ret Return_Type;
typedef Ret (Signature)(Params...);
};
template<typename T>
struct Callable_Traits
{
typedef typename Function_Signature<decltype(&T::operator())>::Signature Signature;
typedef typename Function_Signature<decltype(&T::operator())>::Return_Type Return_Type;
};
}
}
}
#endif

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
@ -682,7 +682,7 @@ namespace chaiscript
}
/// Returns the type info for a named type
Type_Info get_type(const std::string_view &name, bool t_throw = true) const
Type_Info get_type(std::string_view name, bool t_throw = true) const
{
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
@ -694,7 +694,7 @@ namespace chaiscript
}
if (t_throw) {
throw std::range_error("Type Not Known");
throw std::range_error("Type Not Known: " + std::string(name));
} else {
return Type_Info();
}
@ -730,8 +730,8 @@ namespace chaiscript
{
uint_fast32_t method_missing_loc = m_method_missing_loc;
auto method_missing_funs = get_function("method_missing", method_missing_loc);
if (method_missing_funs.first != method_missing_loc) {
m_method_missing_loc = uint_fast32_t(method_missing_funs.first);
if (method_missing_funs.first != method_missing_loc) {
m_method_missing_loc = uint_fast32_t(method_missing_funs.first);
}
return std::move(method_missing_funs.second);
@ -837,7 +837,7 @@ namespace chaiscript
for (auto itr = stack.rbegin(); itr != stack.rend(); ++itr)
{
retval.insert(itr->begin(), itr->end());
}
}
// add the global values
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
@ -950,11 +950,11 @@ namespace chaiscript
} 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.begin() + l_num_params, l_params.end()},
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.begin() + l_num_params, l_params.end()},
std::vector<Const_Proxy_Function>(l_funs.begin(), l_funs.end()));
}
} else {
@ -1011,7 +1011,7 @@ namespace chaiscript
tmp_params.insert(tmp_params.begin() + 1, var(t_name));
return do_attribute_call(2, Function_Params(tmp_params), functions, t_conversions);
} else {
std::array p{params[0], var(t_name), var(std::vector<Boxed_Value>(params.begin()+1, params.end()))};
std::array<Boxed_Value, 3> p{params[0], var(t_name), var(std::vector<Boxed_Value>(params.begin()+1, params.end()))};
return dispatch::dispatch(functions, Function_Params{p}, t_conversions);
}
} catch (const dispatch::option_explicit_set &e) {

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_DYNAMIC_OBJECT_DETAIL_HPP_

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
@ -46,9 +46,9 @@ namespace chaiscript
constexpr Type_Info() noexcept = default;
constexpr bool operator<(const Type_Info &ti) const noexcept
bool operator<(const Type_Info &ti) const noexcept
{
return m_type_info < ti.m_type_info;
return m_type_info->before(*ti.m_type_info);
}
constexpr bool operator!=(const Type_Info &ti) const noexcept

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
@ -506,11 +506,14 @@ namespace chaiscript
/// Errors generated when loading a file
struct file_not_found_error : std::runtime_error {
explicit file_not_found_error(const std::string &t_filename)
: std::runtime_error("File Not Found: " + t_filename)
: std::runtime_error("File Not Found: " + t_filename),
filename(t_filename)
{ }
file_not_found_error(const file_not_found_error &) = default;
~file_not_found_error() noexcept override = default;
std::string filename;
};
}
@ -673,9 +676,6 @@ namespace chaiscript
/// Special type for returned values
struct Return_Value {
Boxed_Value retval;
explicit Return_Value(Boxed_Value &&t_return_value) : retval(std::move(t_return_value)) { }
};

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
@ -54,8 +54,8 @@
#include "../dispatchkit/exception_specification.hpp"
namespace chaiscript
{
/// Namespace alias to provide cleaner and more explicit syntax to users.
{
/// Namespace alias to provide cleaner and more explicit syntax to users.
using Namespace = dispatch::Dynamic_Object;
namespace detail
@ -200,11 +200,32 @@ namespace chaiscript
m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global");
m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global");
// why this unused parameter to Namesapce?
m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace([](Namespace& ) {}, t_namespace_name); import(t_namespace_name); }), "namespace");
// why this unused parameter to Namespace?
m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace([](Namespace& /*space*/) {}, t_namespace_name); import(t_namespace_name); }), "namespace");
m_engine.add(fun([this](const std::string& t_namespace_name) { import(t_namespace_name); }), "import");
}
/// Skip BOM at the beginning of file
static bool skip_bom(std::ifstream &infile) {
size_t bytes_needed = 3;
char buffer[3];
memset(buffer, '\0', bytes_needed);
infile.read(buffer, static_cast<std::streamsize>(bytes_needed));
if ((buffer[0] == '\xef')
&& (buffer[1] == '\xbb')
&& (buffer[2] == '\xbf')) {
infile.seekg(3);
return true;
}
infile.seekg(0);
return false;
}
/// Helper function for loading a file
static std::string load_file(const std::string &t_filename) {
@ -214,17 +235,22 @@ namespace chaiscript
throw chaiscript::exception::file_not_found_error(t_filename);
}
const auto size = infile.tellg();
auto size = infile.tellg();
infile.seekg(0, std::ios::beg);
assert(size >= 0);
if (skip_bom(infile)) {
size-=3; // decrement the BOM size from file size, otherwise we'll get parsing errors
assert(size >= 0); //and check if there's more text
}
if (size == std::streampos(0))
{
return std::string();
} else {
std::vector<char> v(static_cast<size_t>(size));
infile.read(&v[0], size);
infile.read(&v[0], static_cast<std::streamsize>(size));
return std::string(v.begin(), v.end());
}
}
@ -369,8 +395,8 @@ namespace chaiscript
{
for (const auto &path : m_use_paths)
{
const auto appendedpath = path + t_filename;
try {
const auto appendedpath = path + t_filename;
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l2(m_mutex);
@ -386,7 +412,11 @@ namespace chaiscript
}
return retval; // return, we loaded it, or it was already loaded
} catch (const exception::file_not_found_error &) {
} catch (const exception::file_not_found_error &e) {
if (e.filename != appendedpath) {
// a nested file include failed
throw;
}
// failed to load, try the next path
}
}
@ -711,39 +741,39 @@ namespace chaiscript
return m_engine.boxed_cast<T>(eval_file(t_filename, t_handler));
}
/// \brief Imports a namespace object into the global scope of this ChaiScript instance.
/// \param[in] t_namespace_name Name of the namespace to import.
/// \throw std::runtime_error In the case that the namespace name was never registered.
void import(const std::string& t_namespace_name)
{
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
if (m_engine.get_scripting_objects().count(t_namespace_name)) {
throw std::runtime_error("Namespace: " + t_namespace_name + " was already defined");
}
else if (m_namespace_generators.count(t_namespace_name)) {
m_engine.add_global(var(std::ref(m_namespace_generators[t_namespace_name]())), t_namespace_name);
}
else {
throw std::runtime_error("No registered namespace: " + t_namespace_name);
}
}
/// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never used.
/// \param[in] t_namespace_generator Namespace generator function.
/// \param[in] t_namespace_name Name of the Namespace function being registered.
/// \throw std::runtime_error In the case that the namespace name was already registered.
void register_namespace(const std::function<void(Namespace&)>& t_namespace_generator, const std::string& t_namespace_name)
{
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
if (!m_namespace_generators.count(t_namespace_name)) {
// contain the namespace object memory within the m_namespace_generators map
m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace& { t_namespace_generator(space); return space; }));
}
else {
throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered.");
}
/// \brief Imports a namespace object into the global scope of this ChaiScript instance.
/// \param[in] t_namespace_name Name of the namespace to import.
/// \throw std::runtime_error In the case that the namespace name was never registered.
void import(const std::string& t_namespace_name)
{
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
if (m_engine.get_scripting_objects().count(t_namespace_name)) {
throw std::runtime_error("Namespace: " + t_namespace_name + " was already defined");
}
else if (m_namespace_generators.count(t_namespace_name)) {
m_engine.add_global(var(std::ref(m_namespace_generators[t_namespace_name]())), t_namespace_name);
}
else {
throw std::runtime_error("No registered namespace: " + t_namespace_name);
}
}
/// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never used.
/// \param[in] t_namespace_generator Namespace generator function.
/// \param[in] t_namespace_name Name of the Namespace function being registered.
/// \throw std::runtime_error In the case that the namespace name was already registered.
void register_namespace(const std::function<void(Namespace&)>& t_namespace_generator, const std::string& t_namespace_name)
{
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
if (!m_namespace_generators.count(t_namespace_name)) {
// contain the namespace object memory within the m_namespace_generators map
m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace& { t_namespace_generator(space); return space; }));
}
else {
throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered.");
}
}
};

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
@ -99,7 +99,7 @@ namespace chaiscript
} else if (incoming.get_type_info().bare_equal_type_info(typeid(std::string))) {
return Boxed_Value(*static_cast<const std::string *>(incoming.get_const_ptr()));
} else {
std::array params{std::move(incoming)};
std::array<Boxed_Value, 1> params{std::move(incoming)};
return t_ss->call_function("clone", t_loc, Function_Params{params}, t_ss.conversions());
}
} else {
@ -203,7 +203,7 @@ namespace chaiscript
}
} else {
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
std::array params{t_lhs, m_rhs};
std::array<Boxed_Value, 2> params{t_lhs, m_rhs};
fpp.save_params(Function_Params{params});
return t_ss->call_function(t_oper_string, m_loc, Function_Params{params}, t_ss.conversions());
}
@ -250,7 +250,7 @@ namespace chaiscript
}
} else {
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
std::array params{t_lhs, t_rhs};
std::array<Boxed_Value, 2> params{t_lhs, t_rhs};
fpp.save_params(Function_Params(params));
return t_ss->call_function(t_oper_string, m_loc, Function_Params(params), t_ss.conversions());
}
@ -331,6 +331,7 @@ namespace chaiscript
Boxed_Value fn(this->children[0]->eval(t_ss));
using ConstFunctionTypePtr = const dispatch::Proxy_Function_Base *;
try {
return (*t_ss->boxed_cast<const dispatch::Proxy_Function_Base *>(fn))(Function_Params{params}, t_ss.conversions());
}
@ -339,7 +340,8 @@ namespace chaiscript
}
catch(const exception::bad_boxed_cast &){
try {
Const_Proxy_Function f = t_ss->boxed_cast<const Const_Proxy_Function &>(fn);
using ConstFunctionTypeRef = const Const_Proxy_Function &;
Const_Proxy_Function f = t_ss->boxed_cast<ConstFunctionTypeRef>(fn);
// handle the case where there is only 1 function to try to call and dispatch fails on it
throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, make_vector(f), false, *t_ss);
} catch (const exception::bad_boxed_cast &) {
@ -455,10 +457,16 @@ namespace chaiscript
// for the RHS
auto rhs = this->children[1]->eval(t_ss);
auto lhs = this->children[0]->eval(t_ss);
std::array p{std::move(lhs), std::move(rhs)};
std::array<Boxed_Value, 2> p{std::move(lhs), std::move(rhs)};
return p;
}();
if (params[0].is_return_value()) {
throw exception::eval_error("Error, cannot assign to temporary value.");
} else if (params[0].is_const()) {
throw exception::eval_error("Error, cannot assign to constant value.");
}
if (m_oper != Operators::Opers::invalid && params[0].get_type_info().is_arithmetic() &&
params[1].get_type_info().is_arithmetic())
@ -466,13 +474,9 @@ namespace chaiscript
try {
return Boxed_Number::do_oper(m_oper, params[0], params[1]);
} catch (const std::exception &) {
throw exception::eval_error("Error with unsupported arithmetic assignment operation");
throw exception::eval_error("Error with unsupported arithmetic assignment operation.");
}
} else if (m_oper == Operators::Opers::assign) {
if (params[0].is_return_value()) {
throw exception::eval_error("Error, cannot assign to temporary value.");
}
try {
if (params[0].is_undef()) {
@ -599,7 +603,7 @@ namespace chaiscript
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
std::array params{this->children[0]->eval(t_ss), this->children[1]->eval(t_ss)};
std::array<Boxed_Value, 2> params{this->children[0]->eval(t_ss), this->children[1]->eval(t_ss)};
try {
fpp.save_params(Function_Params{params});
@ -657,7 +661,7 @@ namespace chaiscript
if (this->children[1]->identifier == AST_Node_Type::Array_Call) {
try {
std::array p{retval, this->children[1]->children[1]->eval(t_ss)};
std::array<Boxed_Value, 2> p{retval, this->children[1]->children[1]->eval(t_ss)};
retval = t_ss->call_function("[]", m_array_loc, Function_Params{p}, t_ss.conversions());
}
catch(const exception::dispatch_error &e){
@ -771,6 +775,8 @@ namespace chaiscript
std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
std::make_move_iterator(std::prev(t_children.end(), has_guard(t_children, 1)?2:1)))
),
// This apparent use after move is safe because we are only moving out the specific elements we need
// on each operation.
m_body_node(get_body_node(std::move(t_children))),
m_guard_node(get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2))
@ -933,10 +939,16 @@ namespace chaiscript
const auto do_loop = [&loop_var_name, &t_ss, this](const auto &ranged_thing){
try {
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
Boxed_Value &obj = t_ss.add_get_object(loop_var_name, void_var());
for (auto loop_var : ranged_thing) {
obj = Boxed_Value(std::move(loop_var));
for (auto &&loop_var : ranged_thing) {
// This scope push and pop might not be the best thing for perf
// but we know it's 100% correct
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
/// to-do make this if-constexpr with C++17 branch
if (!std::is_same<std::decay_t<decltype(loop_var)>, Boxed_Value>::value) {
t_ss.add_get_object(loop_var_name, Boxed_Value(std::ref(loop_var)));
} else {
t_ss.add_get_object(loop_var_name, Boxed_Value(loop_var));
}
try {
this->children[2]->eval(t_ss);
} catch (detail::Continue_Loop &) {
@ -960,10 +972,9 @@ namespace chaiscript
try {
const auto range_obj = call_function(range_funcs, range_expression_result);
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
Boxed_Value &obj = t_ss.add_get_object(loop_var_name, void_var());
while (!boxed_cast<bool>(call_function(empty_funcs, range_obj))) {
obj = call_function(front_funcs, range_obj);
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
t_ss.add_get_object(loop_var_name, call_function(front_funcs, range_obj));
try {
this->children[2]->eval(t_ss);
} catch (detail::Continue_Loop &) {
@ -1039,7 +1050,7 @@ namespace chaiscript
if (this->children[currentCase]->identifier == AST_Node_Type::Case) {
//This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here.
try {
std::array p{match_value, this->children[currentCase]->children[0]->eval(t_ss)};
std::array<Boxed_Value, 2> p{match_value, this->children[currentCase]->children[0]->eval(t_ss)};
if (hasMatched || boxed_cast<bool>(t_ss->call_function("==", m_loc, Function_Params{p}, t_ss.conversions()))) {
this->children[currentCase]->eval(t_ss);
hasMatched = true;
@ -1154,10 +1165,10 @@ namespace chaiscript
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
if (!this->children.empty()) {
throw detail::Return_Value(this->children[0]->eval(t_ss));
throw detail::Return_Value{this->children[0]->eval(t_ss)};
}
else {
throw detail::Return_Value(void_var());
throw detail::Return_Value{void_var()};
}
}
};
@ -1214,6 +1225,10 @@ namespace chaiscript
// short circuit arithmetic operations
if (m_oper != Operators::Opers::invalid && m_oper != Operators::Opers::bitwise_and && bv.get_type_info().is_arithmetic())
{
if ((m_oper == Operators::Opers::pre_increment || m_oper == Operators::Opers::pre_decrement) && bv.is_const())
{
throw exception::eval_error("Error with prefix operator evaluation: cannot modify constant value.");
}
return Boxed_Number::do_oper(m_oper, bv);
} else {
chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
@ -1283,7 +1298,7 @@ namespace chaiscript
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
try {
std::array params{
std::array<Boxed_Value, 2> params{
this->children[0]->children[0]->children[0]->eval(t_ss),
this->children[0]->children[0]->children[1]->eval(t_ss)
};
@ -1408,7 +1423,7 @@ namespace chaiscript
Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc),
std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
std::make_move_iterator(std::prev(t_children.end(), Def_AST_Node<T>::has_guard(t_children, 1)?2:1)))
),
m_body_node(Def_AST_Node<T>::get_body_node(std::move(t_children))),

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_OPTIMIZER_HPP_
@ -356,7 +356,7 @@ namespace chaiscript {
const auto make_constant = [&node, &fun_name](auto val){
const auto match = fun_name + "(" + node->children[1]->children[0]->text + ")";
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(val));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, const_var(val));
};
if (fun_name == "double") {

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
@ -118,12 +118,34 @@ namespace chaiscript
}
template<typename Tracer, typename Optimizer>
template<typename Tracer, typename Optimizer, std::size_t Parse_Depth=512>
class ChaiScript_Parser final : public ChaiScript_Parser_Base {
void *get_tracer_ptr() noexcept override {
return &m_tracer;
}
std::size_t m_current_parse_depth = 0;
struct Depth_Counter
{
static const auto max_depth = Parse_Depth;
Depth_Counter(ChaiScript_Parser *t_parser) : parser(t_parser)
{
++parser->m_current_parse_depth;
if (parser->m_current_parse_depth > max_depth) {
throw exception::eval_error("Maximum parse depth exceeded",
File_Position(parser->m_position.line, parser->m_position.col), *(parser->m_filename));
}
}
~Depth_Counter() noexcept
{
--parser->m_current_parse_depth;
}
ChaiScript_Parser *parser;
};
template<typename Array2D, typename First, typename Second>
constexpr static void set_alphabet(Array2D &array, const First first, const Second second) noexcept
{
@ -552,10 +574,14 @@ namespace chaiscript
/// Skips ChaiScript whitespace, which means space and tab, but not cr/lf
/// jespada: Modified SkipWS to skip optionally CR ('\n') and/or LF+CR ("\r\n")
/// AlekMosingiewicz: Added exception when illegal character detected
bool SkipWS(bool skip_cr=false) {
bool retval = false;
while (m_position.has_more()) {
if(static_cast<unsigned char>(*m_position) > 0x7e) {
throw exception::eval_error("Illegal character", File_Position(m_position.line, m_position.col), *m_filename);
}
auto end_line = (*m_position != 0) && ((*m_position == '\n') || (*m_position == '\r' && *(m_position+1) == '\n'));
if ( char_in_alphabet(*m_position,detail::white_alphabet) || (skip_cr && end_line)) {
@ -766,6 +792,7 @@ namespace chaiscript
#ifdef CHAISCRIPT_CLANG
#pragma GCC diagnostic ignored "-Wtautological-compare"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
#endif
@ -781,7 +808,10 @@ namespace chaiscript
return const_var(static_cast<unsigned int>(u));
} else if (!unsigned_ && !longlong_ && u >= std::numeric_limits<long>::min() && u <= std::numeric_limits<long>::max()) {
return const_var(static_cast<long>(u));
} else if ((unsigned_ || base != 10) && !longlong_ && u >= std::numeric_limits<unsigned long>::min() && u <= std::numeric_limits<unsigned long>::max()) {
} else if ((unsigned_ || base != 10) && !longlong_
&& u >= std::numeric_limits<unsigned long>::min()
&& u <= std::numeric_limits<unsigned long>::max()) {
return const_var(static_cast<unsigned long>(u));
} else if (!unsigned_ && u >= std::numeric_limits<long long>::min() && u <= std::numeric_limits<long long>::max()) {
return const_var(static_cast<long long>(u));
@ -948,7 +978,7 @@ namespace chaiscript
} break;
case utility::hash("__FUNC__"): {
std::string fun_name = "NOT_IN_FUNCTION";
for (size_t idx = m_match_stack.size() - 1; idx > 0; --idx)
for (size_t idx = m_match_stack.empty() ? 0 : m_match_stack.size() - 1; idx > 0; --idx)
{
if (m_match_stack[idx-1]->identifier == AST_Node_Type::Id
&& m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) {
@ -961,7 +991,7 @@ namespace chaiscript
} break;
case utility::hash("__CLASS__"): {
std::string fun_name = "NOT_IN_CLASS";
for (size_t idx = m_match_stack.size() - 1; idx > 1; --idx)
for (size_t idx = m_match_stack.empty() ? 0 : m_match_stack.size() - 1; idx > 1; --idx)
{
if (m_match_stack[idx-2]->identifier == AST_Node_Type::Id
&& m_match_stack[idx-1]->identifier == AST_Node_Type::Id
@ -1071,7 +1101,7 @@ namespace chaiscript
bool saw_interpolation_marker = false;
bool is_octal = false;
bool is_hex = false;
bool is_unicode = false;
std::size_t unicode_size = 0;
const bool interpolation_allowed;
string_type octal_matches;
@ -1095,12 +1125,12 @@ namespace chaiscript
process_hex();
}
if (is_unicode) {
if (unicode_size > 0) {
process_unicode();
}
} catch (const std::invalid_argument &) {
// escape sequence was invalid somehow, we'll pick this
// up in the next part of parsing
} catch (const exception::eval_error &) {
// Something happened with parsing, we'll catch it later?
}
}
@ -1130,13 +1160,43 @@ namespace chaiscript
void process_unicode()
{
if (!hex_matches.empty()) {
auto val = stoll(hex_matches, nullptr, 16);
hex_matches.clear();
match += detail::Char_Parser_Helper<string_type>::str_from_ll(val);
}
const auto ch = static_cast<uint32_t>(std::stoi(hex_matches, nullptr, 16));
const auto match_size = hex_matches.size();
hex_matches.clear();
is_escaped = false;
is_unicode = 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) {
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?
throw exception::eval_error("Invalid 32 bit universal character");
}
}
void parse(const char_type t_char, const int line, const int col, const std::string &filename) {
@ -1172,16 +1232,16 @@ namespace chaiscript
} else {
process_hex();
}
} else if (is_unicode) {
} else if (unicode_size > 0) {
if (is_hex_char) {
hex_matches.push_back(t_char);
if(hex_matches.size() == 4) {
// Format is specified to be 'slash'uABCD
// on collecting from A to D do parsing
process_unicode();
}
return;
if(hex_matches.size() == unicode_size) {
// Format is specified to be 'slash'uABCD
// on collecting from A to D do parsing
process_unicode();
}
return;
} else {
// Not a unicode anymore, try parsing any way
// May be someone used 'slash'uAA only
@ -1204,7 +1264,9 @@ namespace chaiscript
} else if (t_char == 'x') {
is_hex = true;
} else if (t_char == 'u') {
is_unicode = true;
unicode_size = 4;
} else if (t_char == 'U') {
unicode_size = 8;
} else {
switch (t_char) {
case ('\'') : match.push_back('\''); break;
@ -1235,6 +1297,7 @@ namespace chaiscript
/// Reads (and potentially captures) a quoted string from input. Translates escaped sequences.
bool Quoted_String() {
Depth_Counter dc{this};
SkipWS();
const auto start = m_position;
@ -1350,6 +1413,7 @@ namespace chaiscript
/// Reads (and potentially captures) a char group from input. Translates escaped sequences.
bool Single_Quoted_String() {
Depth_Counter dc{this};
SkipWS();
const auto start = m_position;
@ -1389,6 +1453,7 @@ namespace chaiscript
/// Reads (and potentially captures) a char from input if it matches the parameter
bool Char(const char t_c) {
Depth_Counter dc{this};
SkipWS();
return Char_(t_c);
}
@ -1413,6 +1478,7 @@ namespace chaiscript
/// Reads (and potentially captures) a string from input if it matches the parameter
bool Keyword(const utility::Static_String &t_s) {
Depth_Counter dc{this};
SkipWS();
const auto start = m_position;
bool retval = Keyword_(t_s);
@ -1431,6 +1497,7 @@ namespace chaiscript
/// 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) {
Depth_Counter dc{this};
SkipWS();
const auto start = m_position;
bool retval = Symbol_(t_s);
@ -1465,6 +1532,7 @@ namespace chaiscript
/// Reads until the end of the current statement
bool Eos() {
Depth_Counter dc{this};
SkipWS();
return Eol_(true);
@ -1472,6 +1540,7 @@ namespace chaiscript
/// Reads (and potentially captures) an end-of-line group from input
bool Eol() {
Depth_Counter dc{this};
SkipWS();
return Eol_();
@ -1479,6 +1548,7 @@ namespace chaiscript
/// Reads a comma-separated list of values from input. Id's only, no types allowed
bool Id_Arg_List() {
Depth_Counter dc{this};
SkipWS(true);
bool retval = false;
@ -1504,6 +1574,7 @@ namespace chaiscript
/// Reads a comma-separated list of values from input, for function declarations
bool Decl_Arg_List() {
Depth_Counter dc{this};
SkipWS(true);
bool retval = false;
@ -1530,6 +1601,7 @@ namespace chaiscript
/// Reads a comma-separated list of values from input
bool Arg_List() {
Depth_Counter dc{this};
SkipWS(true);
bool retval = false;
@ -1555,6 +1627,7 @@ namespace chaiscript
/// Reads possible special container values, including ranges and map_pairs
bool Container_Arg_List() {
Depth_Counter dc{this};
bool retval = false;
SkipWS(true);
@ -1592,6 +1665,7 @@ namespace chaiscript
/// Reads a lambda (anonymous function) from input
bool Lambda() {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -1633,6 +1707,7 @@ namespace chaiscript
/// Reads a function definition from input
bool Def(const bool t_class_context = false, const std::string &t_class_name = "") {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -1692,6 +1767,7 @@ namespace chaiscript
/// Reads a function definition from input
bool Try() {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -1746,6 +1822,7 @@ namespace chaiscript
/// Reads an if/else if/else block from input
bool If() {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -1811,6 +1888,7 @@ namespace chaiscript
/// Reads a class block from input
bool Class(const bool t_class_allowed) {
Depth_Counter dc{this};
bool retval = false;
size_t prev_stack_top = m_match_stack.size();
@ -1843,6 +1921,7 @@ namespace chaiscript
/// Reads a while block from input
bool While() {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -1872,6 +1951,7 @@ namespace chaiscript
/// Reads the ranged `for` conditions from input
bool Range_Expression() {
Depth_Counter dc{this};
// the first element will have already been captured by the For_Guards() call that preceeds it
return Char(':') && Equation();
}
@ -1879,6 +1959,7 @@ namespace chaiscript
/// Reads the C-style `for` conditions from input
bool For_Guards() {
Depth_Counter dc{this};
if (!(Equation() && Eol()))
{
if (!Eol())
@ -1910,6 +1991,7 @@ namespace chaiscript
/// Reads a for block from input
bool For() {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -1953,6 +2035,7 @@ namespace chaiscript
/// Reads a case block from input
bool Case() {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -1993,6 +2076,7 @@ namespace chaiscript
/// Reads a switch statement from input
bool Switch() {
Depth_Counter dc{this};
const auto prev_stack_top = m_match_stack.size();
if (Keyword("switch")) {
@ -2036,6 +2120,7 @@ namespace chaiscript
/// Reads a curly-brace C-style class block from input
bool Class_Block(const std::string &t_class_name) {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -2060,6 +2145,7 @@ namespace chaiscript
/// Reads a curly-brace C-style block from input
bool Block() {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -2084,6 +2170,7 @@ namespace chaiscript
/// Reads a return statement from input
bool Return() {
Depth_Counter dc{this};
const auto prev_stack_top = m_match_stack.size();
if (Keyword("return")) {
@ -2097,6 +2184,7 @@ namespace chaiscript
/// Reads a break statement from input
bool Break() {
Depth_Counter dc{this};
const auto prev_stack_top = m_match_stack.size();
if (Keyword("break")) {
@ -2109,6 +2197,7 @@ namespace chaiscript
/// Reads a continue statement from input
bool Continue() {
Depth_Counter dc{this};
const auto prev_stack_top = m_match_stack.size();
if (Keyword("continue")) {
@ -2121,6 +2210,7 @@ namespace chaiscript
/// Reads a dot expression(member access), then proceeds to check if it's a function or array call
bool Dot_Fun_Array() {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -2191,6 +2281,7 @@ namespace chaiscript
/// Reads a variable declaration from input
bool Var_Decl(const bool t_class_context = false, const std::string &t_class_name = "") {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -2246,6 +2337,7 @@ namespace chaiscript
/// Reads an expression surrounded by parentheses from input
bool Paren_Expression() {
Depth_Counter dc{this};
if (Char('(')) {
if (!Operator()) {
throw exception::eval_error("Incomplete expression", File_Position(m_position.line, m_position.col), *m_filename);
@ -2261,6 +2353,7 @@ namespace chaiscript
/// Reads, and identifies, a short-form container initialization from input
bool Inline_Container() {
Depth_Counter dc{this};
const auto prev_stack_top = m_match_stack.size();
if (Char('[')) {
@ -2292,6 +2385,7 @@ namespace chaiscript
/// Parses a variable specified with a & aka reference
bool Reference() {
Depth_Counter dc{this};
const auto prev_stack_top = m_match_stack.size();
if (Symbol("&")) {
@ -2308,6 +2402,7 @@ namespace chaiscript
/// Reads a unary prefixed expression from input
bool Prefix() {
Depth_Counter dc{this};
const auto prev_stack_top = m_match_stack.size();
using SS = utility::Static_String;
const std::array<utility::Static_String, 6> prefix_opers{{
@ -2338,6 +2433,7 @@ namespace chaiscript
/// Parses any of a group of 'value' style ast_node groups from input
bool Value() {
Depth_Counter dc{this};
return Var_Decl() || Dot_Fun_Array() || Prefix();
}
@ -2354,7 +2450,9 @@ namespace chaiscript
);
}
bool Operator(const size_t t_precedence = 0) {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -2419,6 +2517,7 @@ namespace chaiscript
/// Reads a pair of values used to create a map initialization from input
bool Map_Pair() {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -2446,6 +2545,7 @@ namespace chaiscript
/// Reads a pair of values used to create a range initialization from input
bool Value_Range() {
Depth_Counter dc{this};
bool retval = false;
const auto prev_stack_top = m_match_stack.size();
@ -2473,6 +2573,7 @@ namespace chaiscript
/// Parses a string of binary equation operators
bool Equation() {
Depth_Counter dc{this};
const auto prev_stack_top = m_match_stack.size();
using SS = utility::Static_String;
@ -2498,6 +2599,7 @@ namespace chaiscript
/// Parses statements allowed inside of a class block
bool Class_Statements(const std::string &t_class_name) {
Depth_Counter dc{this};
bool retval = false;
bool has_more = true;
@ -2526,6 +2628,7 @@ namespace chaiscript
/// Top level parser, starts parsing of all known parses
bool Statements(const bool t_class_allowed = false) {
Depth_Counter dc{this};
bool retval = false;
bool has_more = true;

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_POSIX_HPP_

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// and 2009-2017, Jason Turner (jason@emptycrate.com)
// and 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_PRELUDE_HPP_

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_TRACER_HPP_

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_UNKNOWN_HPP_

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_WINDOWS_HPP_

View File

@ -0,0 +1,50 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_UTILITY_FNV1A_HPP_
#define CHAISCRIPT_UTILITY_FNV1A_HPP_
#include <cstdint>
#include "../chaiscript_defines.hpp"
namespace chaiscript
{
namespace utility
{
static constexpr std::uint32_t fnv1a_32(const char *s, std::uint32_t h = 0x811c9dc5) {
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
#ifdef CHAISCRIPT_MSVC
#pragma warning(push)
#pragma warning(disable : 4307)
#endif
return (*s == 0) ? h : fnv1a_32(s+1, ((h ^ (*s)) * 0x01000193));
#ifdef CHAISCRIPT_MSVC
#pragma warning(pop)
#endif
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
}
}
#endif

View File

@ -50,7 +50,7 @@ class JSON
private:
using Data = std::variant<std::nullptr_t, chaiscript::utility::QuickFlatMap<std::string, JSON>, std::vector<JSON>, std::string, double, int64_t, bool>;
using Data = std::variant<std::nullptr_t, chaiscript::utility::QuickFlatMap<std::string, JSON>, std::vector<JSON>, std::string, double, std::int64_t, bool>;
struct Internal
{
@ -66,7 +66,7 @@ class JSON
case Class::Array: return std::vector<JSON>{};
case Class::String: return std::string{};
case Class::Floating: return double{};
case Class::Integral: return int64_t{};
case Class::Integral: return std::int64_t{};
case Class::Boolean: return bool{};
}
throw std::runtime_error("unknown type");
@ -191,7 +191,7 @@ class JSON
explicit JSON( T b, typename enable_if<is_same<T,bool>::value>::type* = nullptr ) noexcept : internal( static_cast<bool>(b) ) {}
template <typename T>
explicit JSON( T i, typename enable_if<is_integral<T>::value && !is_same<T,bool>::value>::type* = nullptr ) noexcept : internal( static_cast<int64_t>(i) ) {}
explicit JSON( T i, typename enable_if<is_integral<T>::value && !is_same<T,bool>::value>::type* = nullptr ) noexcept : internal( static_cast<std::int64_t>(i) ) {}
template <typename T>
explicit JSON( T f, typename enable_if<is_floating_point<T>::value>::type* = nullptr ) noexcept : internal( static_cast<double>(f) ) {}
@ -280,10 +280,10 @@ class JSON
[](){ return double{}; }
);
}
int64_t to_int() const noexcept {
std::int64_t to_int() const noexcept {
return internal.visit_or<Class::Integral>(
[](const auto &o){ return o; },
[](){ return int64_t{}; }
[](){ return std::int64_t{}; }
);
}
bool to_bool() const noexcept {
@ -321,7 +321,7 @@ class JSON
bool skip = true;
for( auto &p : *internal.Map() ) {
if( !skip ) { s += ",\n"; }
s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) );
s += ( pad + "\"" + json_escape(p.first) + "\" : " + p.second.dump( depth + 1, tab ) );
skip = false;
}
s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ;
@ -497,7 +497,7 @@ struct JSONParser {
char c = '\0';
bool isDouble = false;
bool isNegative = false;
int64_t exp = 0;
std::int64_t exp = 0;
bool isExpNegative = false;
if( offset < str.size() && str.at(offset) == '-' ) {
isNegative = true;
@ -535,7 +535,7 @@ struct JSONParser {
break;
}
}
exp = chaiscript::parse_num<int64_t>( exp_str ) * (isExpNegative?-1:1);
exp = chaiscript::parse_num<std::int64_t>( exp_str ) * (isExpNegative?-1:1);
}
else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) {
throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'");
@ -546,9 +546,9 @@ struct JSONParser {
return JSON((isNegative?-1:1) * chaiscript::parse_num<double>( val ) * std::pow( 10, exp ));
} else {
if( !exp_str.empty() ) {
return JSON((isNegative?-1:1) * static_cast<double>(chaiscript::parse_num<int64_t>( val )) * std::pow( 10, exp ));
return JSON((isNegative?-1:1) * static_cast<double>(chaiscript::parse_num<std::int64_t>( val )) * std::pow( 10, exp ));
} else {
return JSON((isNegative?-1:1) * chaiscript::parse_num<int64_t>( val ));
return JSON((isNegative?-1:1) * chaiscript::parse_num<std::int64_t>( val ));
}
}
}

View File

@ -80,7 +80,7 @@ namespace chaiscript
try {
const std::map<std::string, Boxed_Value> m = chaiscript::boxed_cast<const std::map<std::string, Boxed_Value> &>(t_bv);
json::JSON obj;
json::JSON obj(json::JSON::Class::Object);
for (const auto &o : m)
{
obj[o.first] = to_json_object(o.second);
@ -93,7 +93,7 @@ namespace chaiscript
try {
const std::vector<Boxed_Value> v = chaiscript::boxed_cast<const std::vector<Boxed_Value> &>(t_bv);
json::JSON obj;
json::JSON obj(json::JSON::Class::Array);
for (size_t i = 0; i < v.size(); ++i)
{
obj[i] = to_json_object(v[i]);
@ -110,7 +110,7 @@ namespace chaiscript
{
return json::JSON(bn.get_as<double>());
} else {
return json::JSON(bn.get_as<long>());
return json::JSON(bn.get_as<std::int64_t>());
}
} catch (const chaiscript::detail::exception::bad_any_cast &) {
// not a number
@ -132,7 +132,7 @@ namespace chaiscript
try {
const chaiscript::dispatch::Dynamic_Object &o = boxed_cast<const dispatch::Dynamic_Object &>(t_bv);
json::JSON obj;
json::JSON obj(json::JSON::Class::Object);
for (const auto &attr : o.get_attrs())
{
obj[attr.first] = to_json_object(attr.second);
@ -142,6 +142,8 @@ namespace chaiscript
// not a dynamic object
}
if (t_bv.is_null()) return json::JSON(); // a null value
throw std::runtime_error("Unknown object type to convert to JSON");
}

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_UTILITY_STATIC_STRING_HPP_

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,4 +1,6 @@
Copyright 2009-2016 Jason Turner
BSD-3-Clause License
Copyright 2009-2018 Jason Turner
Copyright 2009-2012 Jonathan Turner.
All Rights Reserved.

View File

@ -1,6 +1,19 @@
Notes:
=======
Current Version: 6.0.0
Current Version: 6.1.0
### Changes since 6.0.0
* Add namespacing support #290 @stephenberry
* Add utf parsing support
* cheatsheet.md updates
* `add_class` utility support for scoped enums #306 @StanEpp
* Parser optimizations #300 @niXman
* Various JSON fixes #377 #400 #409 #371 @totalgee @dinghram @arcoRocks
* Various cleanups, bugfixes and warning fixes and minor performance improvements
* Support for C++17 compilers!
* Support for UTF8 BOM #439 @AlekMosingiewicz @MarioLiebisch
### Changes since 5.8.6

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
@ -20,6 +20,7 @@
#include "../static_libs/chaiscript_parser.hpp"
#include "../static_libs/chaiscript_stdlib.hpp"
#include "sha3.h"
#ifdef READLINE_AVAILABLE
#include <readline/readline.h>
@ -323,17 +324,28 @@ def assert_throws(desc, x)
}
})chaiscript");
SHA3 sha3;
std::string sha = sha3(data, size);
std::string input(reinterpret_cast<const char *>(data), size);
try {
chai.eval(std::string(reinterpret_cast<const char *>(data), size));
} catch (const chaiscript::exception::eval_error &ee) {
std::cout << ee.pretty_print();
std::cout << '\n';
} catch (const chaiscript::Boxed_Value &e) {
std::cout << "Unhandled exception thrown of type " << e.get_type_info().name() << '\n';
std::ofstream ofs("VALID/" + sha);
ofs << input;
} catch (const chaiscript::exception::eval_error &) {
std::ofstream ofs("EVAL_ERROR/" + sha);
ofs << input;
} catch (const chaiscript::Boxed_Value &) {
std::ofstream ofs("BOXED_VALUE/" + sha);
ofs << input;
} catch (const chaiscript::exception::load_module_error &e) {
std::cout << "Unhandled module load error\n" << e.what() << '\n';
} catch (const std::exception &e) {
std::cout << "unhandled unknown exception: " << e.what() << '\n';
} catch (const std::exception &) {
std::ofstream ofs("STD_EXCEPTION/" + sha);
ofs << input;
} catch (...) {
std::ofstream ofs("UNKOWN_EXCEPTION/" + sha);
ofs << input;
}
return 0;

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
@ -164,15 +164,15 @@ void help(int n) {
}
}
bool throws_exception(const std::function<void ()> &f)
std::string throws_exception(const std::function<void ()> &f)
{
try {
f();
} catch (...) {
return true;
} catch (const std::exception &e) {
return e.what();
}
return false;
return "";
}
chaiscript::exception::eval_error get_eval_error(const std::function<void ()> &f)

View File

@ -4,6 +4,11 @@
// and Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// NOT TO BE USED AS A SOURCE OF BEST PRACTICES
// FOR CHAISCRIPT
#include <iostream>
#include <ctime>
@ -156,7 +161,7 @@ int main(int /*argc*/, char * /*argv*/[]) {
//Ability to create our own container types when needed. std::vector and std::map are
//mostly supported currently
chai.add(bootstrap::standard_library::vector_type<std::vector<int> >("IntVector"));
//chai.add(bootstrap::standard_library::vector_type<std::vector<int> >("IntVector"));
// Test ability to register a function that excepts a shared_ptr version of a type

289
src/sha3.cpp Normal file
View File

@ -0,0 +1,289 @@
// //////////////////////////////////////////////////////////
// sha3.cpp
// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#include "sha3.h"
// big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN
#ifndef _MSC_VER
#include <endian.h>
#endif
/// same as reset()
SHA3::SHA3(Bits bits)
: m_blockSize(200 - 2 * (bits / 8)),
m_bits(bits)
{
reset();
}
/// restart
void SHA3::reset()
{
for (size_t i = 0; i < StateSize; i++)
m_hash[i] = 0;
m_numBytes = 0;
m_bufferSize = 0;
}
/// constants and local helper functions
namespace
{
const unsigned int Rounds = 24;
const uint64_t XorMasks[Rounds] =
{
0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL,
0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL,
0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL,
0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL,
0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL,
0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL,
0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL,
0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL
};
/// rotate left and wrap around to the right
inline uint64_t rotateLeft(uint64_t x, uint8_t numBits)
{
return (x << numBits) | (x >> (64 - numBits));
}
/// convert litte vs big endian
inline uint64_t swap(uint64_t x)
{
#if defined(__GNUC__) || defined(__clang__)
return __builtin_bswap64(x);
#endif
#ifdef _MSC_VER
return _byteswap_uint64(x);
#endif
return (x >> 56) |
((x >> 40) & 0x000000000000FF00ULL) |
((x >> 24) & 0x0000000000FF0000ULL) |
((x >> 8) & 0x00000000FF000000ULL) |
((x << 8) & 0x000000FF00000000ULL) |
((x << 24) & 0x0000FF0000000000ULL) |
((x << 40) & 0x00FF000000000000ULL) |
(x << 56);
}
/// return x % 5 for 0 <= x <= 9
unsigned int mod5(unsigned int x)
{
if (x < 5)
return x;
return x - 5;
}
}
/// process a full block
void SHA3::processBlock(const void* data)
{
#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
#define LITTLEENDIAN(x) swap(x)
#else
#define LITTLEENDIAN(x) (x)
#endif
const uint64_t* data64 = (const uint64_t*) data;
// mix data into state
for (unsigned int i = 0; i < m_blockSize / 8; i++)
m_hash[i] ^= LITTLEENDIAN(data64[i]);
// re-compute state
for (unsigned int round = 0; round < Rounds; round++)
{
// Theta
uint64_t coefficients[5];
for (unsigned int i = 0; i < 5; i++)
coefficients[i] = m_hash[i] ^ m_hash[i + 5] ^ m_hash[i + 10] ^ m_hash[i + 15] ^ m_hash[i + 20];
for (unsigned int i = 0; i < 5; i++)
{
uint64_t one = coefficients[mod5(i + 4)] ^ rotateLeft(coefficients[mod5(i + 1)], 1);
m_hash[i ] ^= one;
m_hash[i + 5] ^= one;
m_hash[i + 10] ^= one;
m_hash[i + 15] ^= one;
m_hash[i + 20] ^= one;
}
// temporary
uint64_t one;
// Rho Pi
uint64_t last = m_hash[1];
one = m_hash[10]; m_hash[10] = rotateLeft(last, 1); last = one;
one = m_hash[ 7]; m_hash[ 7] = rotateLeft(last, 3); last = one;
one = m_hash[11]; m_hash[11] = rotateLeft(last, 6); last = one;
one = m_hash[17]; m_hash[17] = rotateLeft(last, 10); last = one;
one = m_hash[18]; m_hash[18] = rotateLeft(last, 15); last = one;
one = m_hash[ 3]; m_hash[ 3] = rotateLeft(last, 21); last = one;
one = m_hash[ 5]; m_hash[ 5] = rotateLeft(last, 28); last = one;
one = m_hash[16]; m_hash[16] = rotateLeft(last, 36); last = one;
one = m_hash[ 8]; m_hash[ 8] = rotateLeft(last, 45); last = one;
one = m_hash[21]; m_hash[21] = rotateLeft(last, 55); last = one;
one = m_hash[24]; m_hash[24] = rotateLeft(last, 2); last = one;
one = m_hash[ 4]; m_hash[ 4] = rotateLeft(last, 14); last = one;
one = m_hash[15]; m_hash[15] = rotateLeft(last, 27); last = one;
one = m_hash[23]; m_hash[23] = rotateLeft(last, 41); last = one;
one = m_hash[19]; m_hash[19] = rotateLeft(last, 56); last = one;
one = m_hash[13]; m_hash[13] = rotateLeft(last, 8); last = one;
one = m_hash[12]; m_hash[12] = rotateLeft(last, 25); last = one;
one = m_hash[ 2]; m_hash[ 2] = rotateLeft(last, 43); last = one;
one = m_hash[20]; m_hash[20] = rotateLeft(last, 62); last = one;
one = m_hash[14]; m_hash[14] = rotateLeft(last, 18); last = one;
one = m_hash[22]; m_hash[22] = rotateLeft(last, 39); last = one;
one = m_hash[ 9]; m_hash[ 9] = rotateLeft(last, 61); last = one;
one = m_hash[ 6]; m_hash[ 6] = rotateLeft(last, 20); last = one;
m_hash[ 1] = rotateLeft(last, 44);
// Chi
for (unsigned int j = 0; j < 25; j += 5)
{
// temporaries
uint64_t one = m_hash[j];
uint64_t two = m_hash[j + 1];
m_hash[j] ^= m_hash[j + 2] & ~two;
m_hash[j + 1] ^= m_hash[j + 3] & ~m_hash[j + 2];
m_hash[j + 2] ^= m_hash[j + 4] & ~m_hash[j + 3];
m_hash[j + 3] ^= one & ~m_hash[j + 4];
m_hash[j + 4] ^= two & ~one;
}
// Iota
m_hash[0] ^= XorMasks[round];
}
}
/// add arbitrary number of bytes
void SHA3::add(const void* data, size_t numBytes)
{
const uint8_t* current = (const uint8_t*) data;
// copy data to buffer
if (m_bufferSize > 0)
{
while (numBytes > 0 && m_bufferSize < m_blockSize)
{
m_buffer[m_bufferSize++] = *current++;
numBytes--;
}
}
// full buffer
if (m_bufferSize == m_blockSize)
{
processBlock((void*)m_buffer);
m_numBytes += m_blockSize;
m_bufferSize = 0;
}
// no more data ?
if (numBytes == 0)
return;
// process full blocks
while (numBytes >= m_blockSize)
{
processBlock(current);
current += m_blockSize;
m_numBytes += m_blockSize;
numBytes -= m_blockSize;
}
// keep remaining bytes in buffer
while (numBytes > 0)
{
m_buffer[m_bufferSize++] = *current++;
numBytes--;
}
}
/// process everything left in the internal buffer
void SHA3::processBuffer()
{
// add padding
size_t offset = m_bufferSize;
// add a "1" byte
m_buffer[offset++] = 0x06;
// fill with zeros
while (offset < m_blockSize)
m_buffer[offset++] = 0;
// and add a single set bit
m_buffer[offset - 1] |= 0x80;
processBlock(m_buffer);
}
/// return latest hash as 16 hex characters
std::string SHA3::getHash()
{
// process remaining bytes
processBuffer();
// convert hash to string
static const char dec2hex[16 + 1] = "0123456789abcdef";
// number of significant elements in hash (uint64_t)
unsigned int hashLength = m_bits / 64;
std::string result;
result.reserve(m_bits / 4);
for (unsigned int i = 0; i < hashLength; i++)
for (unsigned int j = 0; j < 8; j++) // 64 bits => 8 bytes
{
// convert a byte to hex
unsigned char oneByte = (unsigned char) (m_hash[i] >> (8 * j));
result += dec2hex[oneByte >> 4];
result += dec2hex[oneByte & 15];
}
// SHA3-224's last entry in m_hash provides only 32 bits instead of 64 bits
unsigned int remainder = m_bits - hashLength * 64;
unsigned int processed = 0;
while (processed < remainder)
{
// convert a byte to hex
unsigned char oneByte = (unsigned char) (m_hash[hashLength] >> processed);
result += dec2hex[oneByte >> 4];
result += dec2hex[oneByte & 15];
processed += 8;
}
return result;
}
/// compute SHA3 of a memory block
std::string SHA3::operator()(const void* data, size_t numBytes)
{
reset();
add(data, numBytes);
return getHash();
}
/// compute SHA3 of a string, excluding final zero
std::string SHA3::operator()(const std::string& text)
{
reset();
add(text.c_str(), text.size());
return getHash();
}

81
src/sha3.h Normal file
View File

@ -0,0 +1,81 @@
// //////////////////////////////////////////////////////////
// sha3.h
// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#pragma once
//#include "hash.h"
#include <string>
// define fixed size integer types
#ifdef _MSC_VER
// Windows
typedef unsigned __int8 uint8_t;
typedef unsigned __int64 uint64_t;
#else
// GCC
#include <stdint.h>
#endif
/// compute SHA3 hash
/** Usage:
SHA3 sha3;
std::string myHash = sha3("Hello World"); // std::string
std::string myHash2 = sha3("How are you", 11); // arbitrary data, 11 bytes
// or in a streaming fashion:
SHA3 sha3;
while (more data available)
sha3.add(pointer to fresh data, number of new bytes);
std::string myHash3 = sha3.getHash();
*/
class SHA3 //: public Hash
{
public:
/// algorithm variants
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);
/// compute hash of a string, excluding final zero
std::string operator()(const std::string& text);
/// add arbitrary number of bytes
void add(const void* data, size_t numBytes);
/// return latest hash as hex characters
std::string getHash();
/// restart
void reset();
private:
/// process a full block
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) };
/// 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;
/// valid bytes in m_buffer
size_t m_bufferSize;
/// bytes not processed yet
uint8_t m_buffer[MaxBlockSize];
/// variant
Bits m_bits;
};

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.

View File

@ -1,2 +1,2 @@
assert_throws("Mismatched types in equation, lhs is const.", fun() { 1 = 2 } );
assert_throws("Mismatched types in equation, lhs is const.", fun() { 1 + 2 = 2 } );
assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 1 = 2 } );
assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 1 + 2 = 2 } );

View File

@ -66,10 +66,10 @@ var group = group_guard.get_contained_functions();
assert_equal(true, group[0].has_guard())
assert_equal(false, group[1].has_guard())
assert_throws("Function does not have a guard", fun() { group[0].get_guard(); } );
assert_throws("Function does not have a guard", fun() { without_guard.get_guard(); } );
assert_throws("Function does not have a guard", fun[group]() { group[1].get_guard(); } );
assert_throws("Function does not have a guard", fun[without_guard]() { without_guard.get_guard(); } );
var guard = with_guard.get_guard();
assert_equal(false, guard.has_guard());
assert_throws("Function does not have a guard", fun() { guard.get_guard(); } );
assert_throws("Function does not have a guard", fun[guard]() { guard.get_guard(); } );

View File

@ -1 +1 @@
assert_throws("Illegal const function assignment", fun() { clone = `-` } );
assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { clone = `-` } );

View File

@ -1 +1,2 @@
assert_throws("Invalid function reassignment", fun() { var x = 5; x = `-`; } );
assert_throws("Error: \"Unable to find appropriate'=' operator.\" With parameters: (int, const Function)", fun() { auto x = 5; x = `-`; } );

View File

@ -1,2 +1,8 @@
assert_throws("Parse failure", fun() { eval("[\"hello\":5,\"j\",\"k\"]") } );
try {
eval("[\"hello\":5,\"j\",\"k\"]");
assert_true(false);
} catch (eval_error ee) {
}

View File

@ -13,4 +13,4 @@ assert_equal(0, i -= i)
assert_equal(3, j *= 1.5)
assert_equal(1.5, j /= 2)
assert_equal(2.5, j += 1)
assert_throws("No modulus for float", fun() { k % 2 } );
assert_throws("Error: \"Error with numeric operator calling: %\"", fun[k]() { k % 2 } );

View File

@ -1,2 +1,2 @@
assert_throws("Mismatched types in equation, lhs is const.", fun() { 1 = 2 } );
assert_throws("Mismatched types in equation, lhs is const.", fun() { 1 + 2 = 2 } );
assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 1 = 2 } );
assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 1 + 2 = 2 } );

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4062 4242 4640 4702 6330 28251)
#pragma warning(disable : 4062 4242 4566 4640 4702 6330 28251)
#endif
@ -352,7 +352,29 @@ TEST_CASE("Functor cast")
CHECK(d == 3 * 6);
}
TEST_CASE("Non-ASCII characters in the middle of string")
{
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser());
CHECK_THROWS_AS(chai.eval<std::string>("prin\xeft \"Hello World\""), chaiscript::exception::eval_error);
}
TEST_CASE("Non-ASCII characters in the beginning of string")
{
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser());
CHECK_THROWS_AS(chai.eval<std::string>("\xefprint \"Hello World\""), chaiscript::exception::eval_error);
}
TEST_CASE("Non-ASCII characters in the end of string")
{
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser());
CHECK_THROWS_AS(chai.eval<std::string>("print \"Hello World\"\xef"), chaiscript::exception::eval_error);
}
TEST_CASE("BOM in string")
{
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser());
CHECK_THROWS_AS(chai.eval<std::string>("\xef\xbb\xbfprint \"Hello World\""), chaiscript::exception::eval_error);
}
int set_state_test_myfun()
{
@ -382,7 +404,7 @@ TEST_CASE("Set and restore chai state")
// set state should have reverted the state of the functions and dropped
// the 'myfun'
CHECK_THROWS_AS(chai.eval<int>("myfun()"), chaiscript::exception::eval_error &);
CHECK_THROWS_AS(chai.eval<int>("myfun()"), chaiscript::exception::eval_error);
// set state should not affect the local variables
CHECK(chai.eval<int>("i") == 1);
@ -390,7 +412,7 @@ TEST_CASE("Set and restore chai state")
// After resetting the locals we expect the 'i' to be gone
chai.set_locals(locals);
CHECK_THROWS_AS(chai.eval<int>("i"), chaiscript::exception::eval_error &);
CHECK_THROWS_AS(chai.eval<int>("i"), chaiscript::exception::eval_error);
}
@ -468,8 +490,8 @@ TEST_CASE("Simultaneous ChaiScript tests")
CHECK(chai.eval<int>("do_something(" + std::to_string(i) + ")") == i + 2);
CHECK(chai2.eval<int>("do_something_else(" + std::to_string(i) + ")") == i * 2);
CHECK_THROWS_AS(chai2.eval("do_something(1)"), chaiscript::exception::eval_error &);
CHECK_THROWS_AS(chai2.eval("i"), chaiscript::exception::eval_error &);
CHECK_THROWS_AS(chai2.eval("do_something(1)"), chaiscript::exception::eval_error);
CHECK_THROWS_AS(chai2.eval("i"), chaiscript::exception::eval_error);
CHECK_NOTHROW(chai2.eval("do_something_else(1)"));
}
}
@ -1271,6 +1293,16 @@ TEST_CASE("Test reference member being registered")
CHECK(d == Approx(2.3));
}
TEST_CASE("Test unicode matches C++")
{
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser());
CHECK(u8"\U000000AC" == chai.eval<std::string>(R"("\U000000AC")"));
CHECK("\xF0\x9F\x8D\x8C" == chai.eval<std::string>(R"("\xF0\x9F\x8D\x8C")"));
CHECK(u8"\U0001F34C" == chai.eval<std::string>(R"("\U0001F34C")"));
CHECK(u8"\u2022" == chai.eval<std::string>(R"("\u2022")"));
}
const int add_3(const int &i)
{

View File

@ -10,5 +10,7 @@ var o = MyClass();
assert_true(o.is_explicit());
assert_throws("error", fun[o](){o.x = 2})
assert_throws("Error: \"'x' is not a function.\"", fun[o](){o.x = 2})

View File

@ -0,0 +1,2 @@
eval_file("file_with_bom.inc")
assert_true(alwaysTrue())

View File

@ -40,3 +40,13 @@ assert_equal(res3[2], "member2")
assert_true(__FILE__.find("execution_context.chai") != -1)
assert_equal(eval("__FILE__"), "__EVAL__")
assert_equal(eval("__LINE__"), 1)
assert_equal(eval("__FUNC__"), "NOT_IN_FUNCTION")
assert_equal(eval("__CLASS__"), "NOT_IN_CLASS")

View File

@ -0,0 +1,11 @@
try {
use("failed_deep_include.inc")
assert_true(false);
} catch (e) {
puts("Caught exception while trying to use file");
assert_equal(e.what(), "File Not Found: totally_missing_file.inc");
}

View File

@ -0,0 +1,3 @@
use("totally_missing_file.inc");

View File

@ -0,0 +1,3 @@
def alwaysTrue() {
return true
}

View File

@ -66,10 +66,10 @@ auto group = group_guard.get_contained_functions();
assert_equal(true, group[0].has_guard())
assert_equal(false, group[1].has_guard())
assert_throws("Function does not have a guard", fun() { group[0].get_guard(); } );
assert_throws("Function does not have a guard", fun() { without_guard.get_guard(); } );
assert_throws("Function does not have a guard", fun[group]() { group[1].get_guard(); } );
assert_throws("Function does not have a guard", fun[without_guard]() { without_guard.get_guard(); } );
auto guard = with_guard.get_guard();
assert_equal(false, guard.has_guard());
assert_throws("Function does not have a guard", fun() { guard.get_guard(); } );
assert_throws("Function does not have a guard", fun[guard]() { guard.get_guard(); } );

View File

@ -1,2 +1,2 @@
assert_throws("Function already defined", fun(){ def foo(x) { x + 1 }; def foo(x) { x + 1 } } );
assert_throws("Error: \"Function redefined 'foo'\"", fun(){ def foo(x) { x + 1 }; def foo(x) { x + 1 } } );

View File

@ -1 +1 @@
assert_throws("Illegal const function assignment", fun() { clone = `-` } );
assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { clone = `-` } );

View File

@ -1 +1,4 @@
assert_throws("Invalid function reassignment", fun() { auto x = 5; x = `-`; } );
assert_throws("Error: \"Unable to find appropriate'=' operator.\" With parameters: (int, const Function)", fun() { auto x = 5; x = `-`; } );

19
unittests/json_15.chai Normal file
View File

@ -0,0 +1,19 @@
// Various to_json() tests
assert_equal(to_json(-13570), "-13570")
assert_equal(to_json(0.654321), "0.654321")
assert_equal(to_json("ChaiScript"), "\"ChaiScript\"")
assert_equal(to_json(true), "true")
assert_equal(to_json([1, 2, 3]), "[1, 2, 3]")
assert_equal(to_json(Vector()), "[]") // empty vector
assert_equal(to_json([]), "[]") // empty vector
assert_equal(to_json(Map()), "{\n\n}") // empty map
assert_equal(to_json(Dynamic_Object()), "{\n\n}") // empty object
// Round-trip JSON tests
assert_equal(from_json(to_json([])), [])
assert_equal(from_json(to_json(Map())), Map())
assert_equal(to_json(from_json("null")), "null")
assert_equal(from_json(to_json(["a": 5, "b": "stuff"])), ["a": 5, "b": "stuff"])
auto x = [3.5, true, false, "test", [], Vector(), Map()]
assert_equal(from_json(to_json(x)), x)
assert_equal(from_json(to_json(["aa\\zz":"aa\\zz"])), ["aa\\zz": "aa\\zz"])

View File

@ -1,2 +1,3 @@
assert_equal(from_json("100"), 100)
assert_equal(from_json("-100"), -100)
assert_equal(to_json(4294967295), "4294967295")

View File

@ -1,2 +1,8 @@
assert_throws("Parse failure", fun() { eval("[\"hello\":5,\"j\",\"k\"]") } );
try {
eval("[\"hello\":5,\"j\",\"k\"]");
assert_true(false);
} catch (eval_error ee) {
}

View File

@ -13,4 +13,6 @@ assert_equal(0, i -= i)
assert_equal(3, j *= 1.5)
assert_equal(1.5, j /= 2)
assert_equal(2.5, j += 1)
assert_throws("No modulus for float", fun() { k % 2 } );
assert_throws("Error: \"Error with numeric operator calling: %\"", fun[k]() { k % 2 } );

View File

@ -29,3 +29,13 @@ assert_equal(2, j += 1);
assert_equal(1, --j);
assert_equal(2, ++j);
assert_throws("Error: \"Error with prefix operator evaluation: cannot modify constant value.\"", fun() { ++4; });
assert_throws("Error: \"Error with prefix operator evaluation: cannot modify constant value.\"", fun() { --4; });
assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 5 = 5; });
assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { int(5) = 5; });

View File

@ -0,0 +1,12 @@
def keys(map)
{
auto v = Vector(); // create empty vector
for( i : map )
{
v.push_back(i.first); // append key to the vector
}
v; // return the new vector
}
print(keys(["1a":111, "2b":222]));

View File

@ -6,6 +6,20 @@ try {
print("Caught Error: " + e.what());
}
def get_value()
{
var i = 5;
return i;
}
def get_value_2()
{
return 5;
}
// TODO these should be fixed somehow
//assert_throws("Cannot assign to temporary", fun[](){ get_value() = 3; });
//assert_throws("Cannot assign to temporary", fun[](){ get_value_2() = 3; });
try {

View File

@ -1,5 +1,7 @@
#ifndef CHAISCRIPT_NO_THREADS
#define CHAISCRIPT_NO_THREADS
#endif
/// ChaiScript as a static is unsupported with thread support enabled
///

View File

@ -1,11 +1,4 @@
assert_equal('\u00aa', '\u00AA')
assert_equal('\u00bb', '\uBB')
assert_equal('\ucc', '\u00CC')
assert_equal('\udd', '\uDD')
assert_equal("\u00aa", "\u00AA")
assert_equal("\u00bb", "\xC2\xBB")
assert_equal('\u0ee', '\uEE')
assert_equal('\ue', '\u000E')
assert_equal("\u30\u31\u32", "012")
assert_equal("\u33Test", "3Test")
assert_equal("Test\u0040", "Test@")

View File

@ -1,5 +1,16 @@
assert_equal("\uc39c", "Ü")
assert_equal("U for \uc39cmlauts", "U for Ümlauts")
assert_equal("More \uc39cml\uc3a4\uc3bcts", "More Ümläüts")
assert_equal("\uc39c", "")
assert_equal("U for \u00dcmlauts", "U for Ümlauts")
assert_equal("Thorn \uc3be sign", "Thorn þ sign")
assert_equal("Thorn \u00fe sign", "Thorn þ sign")
assert_equal("Test\u0020Me", "Test Me")
assert_equal("Test\u2022Me", "Test•Me")
assert_equal("\xF0\x9F\x8D\x8C", "🍌")
assert_equal("\U0001F34C", "🍌")
assert_throws("Error: \"Invalid 16 bit universal character\"", fun(){ parse("\"\\uD83C\""); });
assert_throws("Error: \"Incomplete unicode escape sequence\"", fun(){ parse("\"\\uD83 \""); });
assert_equal("\U00024B62", "𤭢")
assert_equal("Test\U0001F534Me", "Test🔴Me")

View File

@ -42,12 +42,14 @@ def assert_not_equal(x, y)
def assert_throws(desc, x)
{
if (throws_exception(x))
auto result = trim(throws_exception(x));
if (result == desc)
{
// Passes
} else {
// Fails
print("assert_throws failure, function did not throw exception: " + to_string(desc));
print("assert_throws failed: got '${result}' expected '${desc}'");
exit(-1);
}
}

View File

@ -1,2 +1,2 @@
assert_throws("Variable already defined", fun() { auto y = 10; auto y = 20; })
assert_throws("Error: \"Variable redefined 'y'\"", fun() { auto y = 10; auto y = 20; })