Merge pull request #441 from ChaiScript/develop

Update for release to 6.1.0
This commit is contained in:
Jason Turner 2018-05-29 11:46:51 -06:00 committed by GitHub
commit 2e77b9d0bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
92 changed files with 13894 additions and 9330 deletions

0
.buckconfig Normal file
View File

98
.clang-format Normal file
View File

@ -0,0 +1,98 @@
AccessModifierOffset: -2
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: false
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyNamespace: true
SplitEmptyRecord: true
BreakAfterJavaFieldAnnotations: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakConstructorInitializersBeforeComma: false
BreakStringLiterals: true
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: true
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeCategories:
- Priority: 2
Regex: ^"(llvm|llvm-c|clang|clang-c)/
- Priority: 3
Regex: ^(<|"(gtest|gmock|isl|json)/)
- Priority: 1
Regex: .*
IncludeIsMainRegex: (Test)?$
IndentCaseLabels: false
IndentWidth: 2
IndentWrappedFunctionNames: true
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
Language: Cpp
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 2
NamespaceIndentation: Inner
ObjCBlockIndentWidth: 7
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: false
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: true
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/buck-out/
/.buckd/
/buckaroo/
.buckconfig.local
BUCKAROO_DEPS

View File

@ -8,6 +8,7 @@ addons:
packages:
- g++-4.9
- g++-5
- g++-6
coverity_scan:
project:
name: "ChaiScript/ChaiScript"
@ -23,14 +24,25 @@ matrix:
sudo: false
env: GCC_VER="4.9"
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"
env: GCC_VER="5" CPPCHECK=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE"
compiler: gcc
- os: linux
sudo: false
env: GCC_VER="6" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE"
compiler: gcc
- os: osx
compiler: clang
osx_image: xcode8
- os: osx
compiler: clang
osx_image: xcode8
env: CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1
env:
global:
@ -40,14 +52,13 @@ env:
before_install:
- if [ "${GCC_VER}" != "" ]; then export CXX="g++-$GCC_VER" CC="gcc-$GCC_VER" GCOV="gcov-$GCC_VER" ; fi
- if [ "${GCC_VER}" == "5" ]; then export CPPCHECK=1 COVERAGE=1 FUZZY_CMD="-D RUN_FUZZY_TESTS:BOOL=TRUE" ; fi
- pip install --user cpp-coveralls
script:
- cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $FUZZY_CMD .
- cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $CMAKE_OPTIONS .
- cmake --build . -- -j2
- ctest
- if [ ${COVERAGE} = 1 ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; fi
- if [ "${BUILD_ONLY}" != "1" ]; then ctest; fi
- if [ "${COVERAGE}" = "1" ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; fi
#after_script:
# - if [ ${CPPCHECK} = 1 ]; then contrib/codeanalysis/runcppcheck.sh ; fi

11
BUCK Normal file
View File

@ -0,0 +1,11 @@
prebuilt_cxx_library(
name = 'chaiscript',
header_only = True,
header_namespace = 'chaiscript',
exported_headers = subdir_glob([
('include/chaiscript', '**/*.hpp'),
]),
visibility = [
'PUBLIC',
],
)

View File

@ -4,16 +4,10 @@ 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)
option(DYNLOAD_ENABLED "Dynamic Loading Support Enabled" TRUE)
option(BUILD_MODULES "Build Extra Modules (stl)" TRUE)
@ -21,6 +15,7 @@ option(BUILD_SAMPLES "Build Samples Folder" FALSE)
option(RUN_FUZZY_TESTS "Run tests generated by AFL" FALSE)
option(USE_STD_MAKE_SHARED "Use std::make_shared instead of chaiscript::make_shared" FALSE)
option(RUN_PERFORMANCE_TESTS "Run Performance Tests" FALSE)
option(BUILD_IN_CPP17_MODE "Build with C++17 flags" FALSE)
mark_as_advanced(USE_STD_MAKE_SHARED)
@ -48,6 +43,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 +99,7 @@ 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_MINOR 0)
set(CPACK_PACKAGE_VERSION_MINOR 1)
set(CPACK_PACKAGE_VERSION_PATCH 0)
set(CPACK_PACKAGE_EXECUTABLES "chai;ChaiScript Eval")
@ -122,6 +119,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)
@ -149,17 +147,31 @@ if(CMAKE_COMPILER_IS_GNUCC)
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
if(GCC_VERSION VERSION_LESS 4.9)
set(CPP11_FLAG "-std=c++1y")
set(CPP14_FLAG "-std=c++1y")
else()
set(CPP11_FLAG "-std=c++14")
if (BUILD_IN_CPP17_MODE)
set(CPP14_FLAG "-std=c++1z")
else()
set(CPP14_FLAG "-std=c++14")
endif()
endif()
else()
set(CPP11_FLAG "-std=c++14")
if (BUILD_IN_CPP17_MODE)
set(CPP14_FLAG "-std=c++1z")
else()
set(CPP14_FLAG "-std=c++14")
endif()
endif()
if(MSVC)
add_definitions(/W4 /w14545 /w34242 /w34254 /w34287 /w44263 /w44265 /w44296 /w44311 /w44826 /we4289 /w14546 /w14547 /w14549 /w14555 /w14619 /w14905 /w14906 /w14928)
if (BUILD_IN_CPP17_MODE)
add_definitions(/std:c++17)
endif()
if (MSVC_VERSION STREQUAL "1800")
# VS2013 doesn't have magic statics
add_definitions(/w44640)
@ -168,7 +180,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
@ -178,10 +190,10 @@ if(MSVC)
# how to workaround or fix the error. So I'm disabling it globally.
add_definitions(/wd4503)
else()
add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP11_FLAG})
add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP14_FLAG})
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command)
add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command -Wno-unused-template)
else()
add_definitions(-Wnoexcept)
endif()
@ -196,12 +208,12 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if(USE_LIBCXX)
add_definitions(-stdlib=libc++)
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG} -stdlib=libc++")
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG} -stdlib=libc++")
else()
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG}")
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}")
endif()
elseif(CMAKE_COMPILER_IS_GNUCC)
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG}")
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}")
endif()
# limitations in MinGW require us to make an optimized build
@ -221,9 +233,15 @@ if(NOT MULTITHREAD_SUPPORT_ENABLED)
add_definitions(-DCHAISCRIPT_NO_THREADS)
endif()
if(NOT DYNLOAD_ENABLED)
add_definitions(-DCHAISCRIPT_NO_DYNLOAD)
endif()
if(CMAKE_HOST_UNIX)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku")
list(APPEND LIBS "dl")
if(DYNLOAD_ENABLED)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku")
list(APPEND LIBS "dl")
endif()
endif()
if(MULTITHREAD_SUPPORT_ENABLED)
@ -286,6 +304,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)
@ -298,32 +317,17 @@ if (RUN_FUZZY_TESTS)
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/unittests")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2016-06-29.tar.bz2
COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2017-07-20.tar.bz2
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests
)
file(GLOB FUZZY_CRASH_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/id*)
list(SORT FUZZY_CRASH_TESTS)
file(GLOB FUZZY_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/MINIMIZED/*)
list(SORT FUZZY_TESTS)
file(GLOB FUZZY_EXCEPTION_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/id*)
list(SORT FUZZY_EXCEPTION_TESTS)
foreach(filename ${FUZZY_CRASH_TESTS})
foreach(filename ${FUZZY_TESTS})
message(STATUS "Adding test ${filename}")
add_test(${filename} chai "-e" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename})
endforeach()
set_property(TEST ${FUZZY_CRASH_TESTS}
PROPERTY ENVIRONMENT
"CHAI_USE_PATH=${CMAKE_BINARY_DIR}/unittests/"
"CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/"
)
foreach(filename ${FUZZY_EXCEPTION_TESTS})
message(STATUS "Adding test ${filename}")
add_test(${filename} chai "--exception" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename})
add_test(fuzz.${filename} chai "-e" "--exception" "--any-exception" ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzz_unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename})
endforeach()
set_property(TEST ${FUZZY_EXCEPTION_TESTS}
@ -425,8 +429,11 @@ 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})
add_test(NAME Static_ChaiScript_Test COMMAND static_chaiscript_test)
add_executable(boxed_cast_test unittests/boxed_cast_test.cpp)
target_link_libraries(boxed_cast_test ${LIBS})
@ -469,6 +476,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
@ -493,5 +509,3 @@ install(FILES "${chaiscript_BINARY_DIR}/lib/pkgconfig/chaiscript.pc"
DESTINATION lib/pkgconfig)
ENDIF()

View File

@ -1,15 +1,17 @@
version: 5.8.x.{build}
os: Visual Studio 2015
version: 6.1.x.{build}
image:
- Visual Studio 2017
environment:
matrix:
- {}
- VS_VERSION: "Visual Studio 14"
- VS_VERSION: "Visual Studio 15"
build_script:
- cmd: >-
mkdir build
cd build
cmake c:\Projects\chaiscript -G "Visual Studio 14"
cmake c:\Projects\chaiscript -G "%VS_VERSION%" -DBUILD_TESTING:BOOL=ON -DBUILD_MODULES:BOOL=ON
cmake --build . --config Debug
test_script:

3
buckaroo.json Normal file
View File

@ -0,0 +1,3 @@
{
"name": "ChaiScript"
}

View File

@ -5,7 +5,7 @@ ChaiScript tries to follow the [Semantic Versioning](http://semver.org/) scheme.
* Major Version Number: API changes / breaking changes
* Minor Version Number: New Features
* Patch Version Number: Minor changes / enhancements
# Initializing ChaiScript
@ -15,6 +15,9 @@ chaiscript::ChaiScript chai; // loads stdlib from loadable module on file system
chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); // compiles in stdlib
```
Note that ChaiScript cannot be used as a global / static object unless it is being compiled with `CHAISCRIPT_NO_THREADS`.
# Adding Things To The Engine
## Adding a Function / Method / Member
@ -34,7 +37,7 @@ chai.add(chaiscript::fun(&Class::method_name, Class_instance_ptr), "method_name"
chai.add(chaiscript::fun(&Class::member_name, Class_instance_ptr), "member_name");
```
### With Overloads
### With Overloads
#### Preferred
@ -66,9 +69,9 @@ chai.add(chaiscript::fun(static_cast<int(Derived::*)>(&Derived::data)), "data");
```
chai.add(
chaiscript::fun<std::string (bool)>(
[](bool type) {
if (type) { return "x"; }
else { return "y"; }
[](bool type) {
if (type) { return "x"; }
else { return "y"; }
}), "function_name");
```
@ -163,6 +166,23 @@ chai.add_global(chaiscript::var(somevar), "somevar"); // global non-const, throw
chai.set_global(chaiscript::var(somevar), "somevar"); // global non-const, overwrites existing object
```
## Adding Namespaces
Namespaces will not be populated until `import` is called.
This saves memory and computing costs if a namespace is not imported into every ChaiScript instance.
```
chai.register_namespace([](chaiscript::Namespace& math) {
math["pi"] = chaiscript::const_var(3.14159);
math["sin"] = chaiscript::var(chaiscript::fun([](const double x) { return sin(x); })); },
"math");
```
Import namespace in ChaiScript
```
import("math")
print(math.pi) // prints 3.14159
```
# Using STL
ChaiScript recognize many types from STL, but you have to add specific instantiation yourself.
@ -350,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
```
@ -476,6 +515,20 @@ o.f = fun(y) { print(this.x + y); }
o.f(10); // prints 13
```
## Namespaces
Namespaces in ChaiScript are Dynamic Objects with global scope
```
namespace("math") // create a new namespace
math.square = fun(x) { x * x } // add a function to the "math" namespace
math.sum_squares = fun(x, y) { math.square(x) + math.square(y) }
print(math.square(4)) // prints 16
print(math.sum_squares(2, 5)) // prints 29
```
### Option Explicit
If you want to disable dynamic parameter definitions, you can `set_explicit`.

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()

1
contrib/vim Normal file
View File

@ -0,0 +1 @@
vim support can be found at https://github.com/ChaiScript/vim-chaiscript

View File

@ -1,7 +0,0 @@
Install ftdetect, indent and syntax subdirectories to:
~/.vim/
See the vim documentation on this:
http://vimdoc.sourceforge.net/htmldoc/syntax.html#mysyntaxfile

View File

@ -1,2 +0,0 @@
au BufRead,BufNewFile *.chai set filetype=chaiscript

View File

@ -1,50 +0,0 @@
" Vim indent file
" Language: ChaiScript
" Maintainer: Jason Turner <lefticus 'at' gmail com>
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
finish
endif
let b:did_indent = 1
setlocal indentexpr=GetChaiScriptIndent()
setlocal autoindent
" Only define the function once.
if exists("*GetChaiScriptIndent")
finish
endif
function! GetChaiScriptIndent()
" Find a non-blank line above the current line.
let lnum = prevnonblank(v:lnum - 1)
" Hit the start of the file, use zero indent.
if lnum == 0
return 0
endif
" Add a 'shiftwidth' after lines that start a block:
" lines containing a {
let ind = indent(lnum)
let flag = 0
let prevline = getline(lnum)
if prevline =~ '^.*{.*'
let ind = ind + &shiftwidth
let flag = 1
endif
" Subtract a 'shiftwidth' after lines containing a { followed by a }
" to keep it balanced
if flag == 1 && prevline =~ '.*{.*}.*'
let ind = ind - &shiftwidth
endif
" Subtract a 'shiftwidth' on lines ending with }
if getline(v:lnum) =~ '^\s*\%(}\)'
let ind = ind - &shiftwidth
endif
return ind
endfunction

View File

@ -1,99 +0,0 @@
" Vim syntax file
" Language: ChaiScript
" Maintainer: Jason Turner <lefticus 'at' gmail com>
" Quit when a (custom) syntax file was already loaded
if exists("b:current_syntax")
finish
end
let s:cpo_save = &cpo
set cpo&vim
syn case match
" syncing method
syn sync fromstart
" Strings
syn region chaiscriptString start=+"+ end=+"+ skip=+\\\\\|\\"+ contains=chaiscriptSpecial,chaiscriptEval,@Spell
" Escape characters
syn match chaiscriptSpecial contained "\\[\\abfnrtv\'\"]\|\\\d\{,3}"
" String evals
syn region chaiscriptEval contained start="${" end="}"
" integer number
syn match chaiscriptNumber "\<\d\+\>"
" floating point number, with dot, optional exponent
syn match chaiscriptFloat "\<\d\+\.\d*\%(e[-+]\=\d\+\)\=\>"
" floating point number, starting with a dot, optional exponent
syn match chaiscriptFloat "\.\d\+\%(e[-+]\=\d\+\)\=\>"
" floating point number, without dot, with exponent
syn match chaiscriptFloat "\<\d\+e[-+]\=\d\+\>"
" Hex strings
syn match chaiscriptNumber "\<0x\x\+\>"
" Binary strings
syn match chaiscriptNumber "\<0b[01]\+\>"
" Various language features
syn keyword chaiscriptCond if else
syn keyword chaiscriptRepeat while for do
syn keyword chaiscriptStatement break continue return switch case default
syn keyword chaiscriptExceptions try catch throw
"Keyword
syn keyword chaiscriptKeyword def true false attr
"Built in types
syn keyword chaiscriptType fun var auto
"Built in funcs, keep it simple
syn keyword chaiscriptFunc eval throw
"Let's treat all backtick operator function lookups as built in too
syn region chaiscriptFunc matchgroup=chaiscriptFunc start="`" end="`"
" Account for the "[1..10]" syntax, treating it as an operator
" Intentionally leaving out all of the normal, well known operators
syn match chaiscriptOperator "\.\."
" Guard seperator as an operator
syn match chaiscriptOperator ":"
" Comments
syn match chaiscriptComment "//.*$" contains=@Spell
syn region chaiscriptComment matchgroup=chaiscriptComment start="/\*" end="\*/" contains=@Spell
hi def link chaiscriptExceptions Exception
hi def link chaiscriptKeyword Keyword
hi def link chaiscriptStatement Statement
hi def link chaiscriptRepeat Repeat
hi def link chaiscriptString String
hi def link chaiscriptNumber Number
hi def link chaiscriptFloat Float
hi def link chaiscriptOperator Operator
hi def link chaiscriptConstant Constant
hi def link chaiscriptCond Conditional
hi def link chaiscriptFunction Function
hi def link chaiscriptComment Comment
hi def link chaiscriptTodo Todo
hi def link chaiscriptError Error
hi def link chaiscriptSpecial SpecialChar
hi def link chaiscriptFunc Identifier
hi def link chaiscriptType Type
hi def link chaiscriptEval Special
let b:current_syntax = "chaiscript"
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: nowrap sw=2 sts=2 ts=8 noet

View File

@ -832,7 +832,7 @@ namespace chaiscript
public:
ChaiScript(std::vector<std::string> t_modulepaths = {},
std::vector<std::string> t_usepaths = {},
const std::vector<Options> &t_opts = {})
const std::vector<Options> &t_opts = chaiscript::default_options())
: ChaiScript_Basic(
chaiscript::Std_Lib::library(),
std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default>>(),

View File

@ -76,7 +76,7 @@ static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later req
namespace chaiscript {
static const int version_major = 6;
static const int version_minor = 0;
static const int version_minor = 1;
static const int version_patch = 0;
static const char *compiler_version = CHAISCRIPT_COMPILER_VERSION;
@ -93,6 +93,16 @@ namespace chaiscript {
#endif
}
template<typename B, typename D, typename ...Arg>
inline std::unique_ptr<B> make_unique(Arg && ... arg)
{
#ifdef CHAISCRIPT_USE_STD_MAKE_SHARED
return std::make_unique<D>(std::forward<Arg>(arg)...);
#else
return std::unique_ptr<B>(static_cast<B*>(new D(std::forward<Arg>(arg)...)));
#endif
}
struct Build_Info {
static int version_major()
{
@ -156,48 +166,56 @@ namespace chaiscript {
}
template<typename T>
template<typename T>
auto parse_num(const char *t_str) -> 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;
T t = 0;
T base{};
T decimal_place = 0;
int exponent = 0;
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(; *t_str != '\0'; ++t_str) {
char 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);
for (char c;; ++t_str) {
c = *t_str;
switch (c)
{
case '.':
decimal_place = 10;
break;
case 'e':
case 'E':
exponent = 1;
decimal_place = 0;
base = t;
t = 0;
break;
case '-':
exponent = -1;
break;
case '+':
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (decimal_place < 10) {
t *= 10;
t += static_cast<T>(c - '0');
}
else {
t += static_cast<T>(c - '0') / decimal_place;
decimal_place *= 10;
}
break;
default:
return exponent ? base * std::pow(T(10), t * static_cast<T>(exponent)) : t;
}
}
}
template<typename T>
@ -216,7 +234,11 @@ namespace chaiscript {
static inline std::vector<Options> default_options()
{
#ifdef CHAISCRIPT_NO_DYNLOAD
return {Options::No_Load_Modules, Options::External_Scripts};
#else
return {Options::Load_Modules, Options::External_Scripts};
#endif
}
}
#endif

View File

@ -67,44 +67,44 @@ namespace chaiscript
class Thread_Storage
{
public:
explicit Thread_Storage(void *t_key)
: m_key(t_key)
{
}
Thread_Storage() = default;
Thread_Storage(const Thread_Storage &) = delete;
Thread_Storage(Thread_Storage &&) = delete;
Thread_Storage &operator=(const Thread_Storage &) = delete;
Thread_Storage &operator=(Thread_Storage &&) = delete;
~Thread_Storage()
{
t().erase(m_key);
t().erase(this);
}
inline const T *operator->() const
{
return &(t()[m_key]);
return &(t()[this]);
}
inline const T &operator*() const
{
return t()[m_key];
return t()[this];
}
inline T *operator->()
{
return &(t()[m_key]);
return &(t()[this]);
}
inline T &operator*()
{
return t()[m_key];
return t()[this];
}
void *m_key;
private:
static std::unordered_map<void*, T> &t()
static std::unordered_map<const void*, T> &t()
{
thread_local static std::unordered_map<void *, T> my_t;
thread_local std::unordered_map<const void *, T> my_t;
return my_t;
}
};
@ -144,7 +144,7 @@ namespace chaiscript
class Thread_Storage
{
public:
explicit Thread_Storage(void *)
explicit Thread_Storage()
{
}

View File

@ -23,10 +23,10 @@ namespace chaiscript
void array(const std::string &type, Module& m)
{
typedef typename std::remove_extent<T>::type ReturnType;
const auto extent = std::extent<T>::value;
m.add(user_type<T>(), type);
m.add(fun(
[extent](T& t, size_t index)->ReturnType &{
[](T& t, size_t index)->ReturnType &{
constexpr auto extent = std::extent<T>::value;
if (extent > 0 && index >= extent) {
throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent));
} else {
@ -37,7 +37,8 @@ namespace chaiscript
);
m.add(fun(
[extent](const T &t, size_t index)->const ReturnType &{
[](const T &t, size_t index)->const ReturnType &{
constexpr auto extent = std::extent<T>::value;
if (extent > 0 && index >= extent) {
throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent));
} else {
@ -48,7 +49,8 @@ namespace chaiscript
);
m.add(fun(
[extent](const T &) {
[](const T &) {
constexpr auto extent = std::extent<T>::value;
return extent;
}), "size");
}
@ -144,6 +146,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);
}
@ -305,13 +308,13 @@ namespace chaiscript
static bool has_parse_tree(const chaiscript::Const_Proxy_Function &t_pf)
{
const auto pf = std::dynamic_pointer_cast<const chaiscript::dispatch::Dynamic_Proxy_Function>(t_pf);
return pf && pf->get_parse_tree();
return bool(pf);
}
static chaiscript::AST_NodePtr get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf)
static const chaiscript::AST_Node &get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf)
{
const auto pf = std::dynamic_pointer_cast<const chaiscript::dispatch::Dynamic_Proxy_Function>(t_pf);
if (pf && pf->get_parse_tree())
if (pf)
{
return pf->get_parse_tree();
} else {
@ -463,7 +466,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);
@ -492,7 +495,7 @@ namespace chaiscript
opers_arithmetic_pod(m);
m.add(fun(&Build_Info::version_major), "version_major");
m.add(fun(&Build_Info::version_minor), "version_minor");
m.add(fun(&Build_Info::version_patch), "version_patch");
@ -545,7 +548,7 @@ namespace chaiscript
std::vector<Boxed_Value> retval;
std::transform(t_eval_error.call_stack.begin(), t_eval_error.call_stack.end(),
std::back_inserter(retval),
&chaiscript::var<const std::shared_ptr<const chaiscript::AST_Node> &>);
&chaiscript::var<const chaiscript::AST_Node_Trace &>);
return retval;
}), "call_stack"} }
);
@ -574,7 +577,7 @@ namespace chaiscript
const auto children = t_node.get_children();
std::transform(children.begin(), children.end(),
std::back_inserter(retval),
&chaiscript::var<const std::shared_ptr<chaiscript::AST_Node> &>);
&chaiscript::var<const std::reference_wrapper<chaiscript::AST_Node> &>);
return retval;
}), "children"}
}

View File

@ -335,9 +335,24 @@ namespace chaiscript
template<typename ContainerType>
void back_insertion_sequence_type(const std::string &type, Module& m)
{
typedef typename ContainerType::reference (ContainerType::*backptr)();
m.add(fun(static_cast<backptr>(&ContainerType::back)), "back");
m.add(fun([](ContainerType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.back());
}
}
)
, "back");
m.add(fun([](const ContainerType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.back());
}
}
)
, "back");
typedef void (ContainerType::*push_back)(const typename ContainerType::value_type &);
@ -380,13 +395,29 @@ namespace chaiscript
template<typename ContainerType>
void front_insertion_sequence_type(const std::string &type, Module& m)
{
typedef typename ContainerType::reference (ContainerType::*front_ptr)();
typedef typename ContainerType::const_reference (ContainerType::*const_front_ptr)() const;
typedef void (ContainerType::*push_ptr)(typename ContainerType::const_reference);
typedef void (ContainerType::*pop_ptr)();
m.add(fun(static_cast<front_ptr>(&ContainerType::front)), "front");
m.add(fun(static_cast<const_front_ptr>(&ContainerType::front)), "front");
m.add(fun([](ContainerType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.front());
}
}
)
, "front");
m.add(fun([](const ContainerType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.front());
}
}
)
, "front");
m.add(fun(static_cast<push_ptr>(&ContainerType::push_front)),
[&]()->std::string{
@ -577,11 +608,27 @@ namespace chaiscript
{
m.add(user_type<VectorType>(), type);
typedef typename VectorType::reference (VectorType::*frontptr)();
typedef typename VectorType::const_reference (VectorType::*constfrontptr)() const;
m.add(fun([](VectorType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.front());
}
}
)
, "front");
m.add(fun([](const VectorType &container)->decltype(auto){
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.front());
}
}
)
, "front");
m.add(fun(static_cast<frontptr>(&VectorType::front)), "front");
m.add(fun(static_cast<constfrontptr>(&VectorType::front)), "front");
back_insertion_sequence_type<VectorType>(type, m);
@ -659,6 +706,8 @@ namespace chaiscript
m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_of(f, pos); } ), "find_last_of");
m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_not_of(f, pos); } ), "find_last_not_of");
m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_not_of(f, pos); } ), "find_first_not_of");
m.add(fun([](String *s, typename String::value_type c) -> decltype(auto) { return (*s += c); } ), "+=");
m.add(fun([](String *s) { s->clear(); } ), "clear");
m.add(fun([](const String *s) { return s->empty(); } ), "empty");
@ -686,7 +735,7 @@ namespace chaiscript
m.add(user_type<FutureType>(), type);
m.add(fun([](const FutureType &t) { return t.valid(); }), "valid");
m.add(fun(&FutureType::get), "get");
m.add(fun([](FutureType &t) { return t.get(); }), "get");
m.add(fun(&FutureType::wait), "wait");
}
template<typename FutureType>

View File

@ -521,6 +521,10 @@ namespace chaiscript
validate_boxed_number(bv);
}
static Boxed_Value clone(const Boxed_Value &t_bv) {
return Boxed_Number(t_bv).get_as(t_bv.get_type_info()).bv;
}
static bool is_floating_point(const Boxed_Value &t_bv)
{
const Type_Info &inp_ = t_bv.get_type_info();

View File

@ -452,7 +452,7 @@ namespace chaiscript
};
explicit Dispatch_Engine(chaiscript::parser::ChaiScript_Parser_Base &parser)
: m_stack_holder(this),
: m_stack_holder(),
m_parser(parser)
{
}
@ -731,7 +731,7 @@ namespace chaiscript
}
if (t_throw) {
throw std::range_error("Type Not Known");
throw std::range_error("Type Not Known: " + name);
} else {
return Type_Info();
}
@ -767,8 +767,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);
@ -879,7 +879,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);
@ -963,7 +963,7 @@ namespace chaiscript
const auto funs = get_function(t_name, loc);
if (funs.first != loc) { t_loc = uint_fast32_t(funs.first); }
const auto do_attribute_call =
const auto do_attribute_call =
[this](int l_num_params, const std::vector<Boxed_Value> &l_params, const std::vector<Proxy_Function> &l_funs, const Type_Conversions_State &l_conversions)->Boxed_Value
{
std::vector<Boxed_Value> attr_params{l_params.begin(), l_params.begin() + l_num_params};
@ -992,11 +992,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 {
@ -1056,7 +1056,7 @@ namespace chaiscript
return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector<Boxed_Value>(params.begin()+1, params.end()))}, t_conversions);
}
} catch (const dispatch::option_explicit_set &e) {
throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.second->begin(), funs.second->end()),
throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.second->begin(), funs.second->end()),
e.what());
}
}
@ -1142,25 +1142,17 @@ namespace chaiscript
void dump_system() const
{
std::cout << "Registered Types: \n";
std::vector<std::pair<std::string, Type_Info> > types = get_types();
for (std::vector<std::pair<std::string, Type_Info> >::const_iterator itr = types.begin();
itr != types.end();
++itr)
for (auto const &type: get_types())
{
std::cout << itr->first << ": ";
std::cout << itr->second.bare_name();
std::cout << '\n';
std::cout << type.first << ": " << type.second.bare_name() << '\n';
}
std::cout << '\n';
std::vector<std::pair<std::string, Proxy_Function > > funcs = get_functions();
std::cout << '\n';
std::cout << "Functions: \n";
for (std::vector<std::pair<std::string, Proxy_Function > >::const_iterator itr = funcs.begin();
itr != funcs.end();
++itr)
for (auto const &func: get_functions())
{
dump_function(*itr);
dump_function(func);
}
std::cout << '\n';
}
@ -1306,7 +1298,7 @@ namespace chaiscript
return m_state.m_boxed_functions;
}
std::vector<std::pair<std::string, Boxed_Value>> &get_boxed_functions_int()
std::vector<std::pair<std::string, Boxed_Value>> &get_boxed_functions_int()
{
return m_state.m_boxed_functions;
}
@ -1316,7 +1308,7 @@ namespace chaiscript
return m_state.m_function_objects;
}
std::vector<std::pair<std::string, Proxy_Function>> &get_function_objects_int()
std::vector<std::pair<std::string, Proxy_Function>> &get_function_objects_int()
{
return m_state.m_function_objects;
}
@ -1326,7 +1318,7 @@ namespace chaiscript
return m_state.m_functions;
}
std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> &get_functions_int()
std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> &get_functions_int()
{
return m_state.m_functions;
}
@ -1434,7 +1426,7 @@ namespace chaiscript
template<typename Container, typename Key>
static typename Container::iterator find_keyed_value(Container &t_c, const Key &t_key)
{
return std::find_if(t_c.begin(), t_c.end(),
return std::find_if(t_c.begin(), t_c.end(),
[&t_key](const typename Container::value_type &o) {
return o.first == t_key;
});

View File

@ -181,9 +181,9 @@ namespace chaiscript
template<typename Ret>
struct Handle_Return<const Ret>
{
static Boxed_Value handle(const Ret &r)
static Boxed_Value handle(Ret r)
{
return Boxed_Value(std::cref(r));
return Boxed_Value(std::move(r));
}
};

View File

@ -41,7 +41,7 @@ namespace chaiscript
class Boxed_Number;
struct AST_Node;
typedef std::shared_ptr<AST_Node> AST_NodePtr;
typedef std::unique_ptr<AST_Node> AST_NodePtr;
namespace dispatch
{
@ -346,14 +346,15 @@ namespace chaiscript
{
public:
Dynamic_Proxy_Function(
int t_arity=-1,
AST_NodePtr t_parsenode = AST_NodePtr(),
const int t_arity,
std::shared_ptr<AST_Node> t_parsenode,
Param_Types t_param_types = Param_Types(),
Proxy_Function t_guard = Proxy_Function())
: Proxy_Function_Base(build_param_type_list(t_param_types), t_arity),
m_param_types(std::move(t_param_types)),
m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode))
{
// assert(t_parsenode);
}
@ -379,9 +380,17 @@ namespace chaiscript
return m_guard;
}
AST_NodePtr get_parse_tree() const
bool has_parse_tree() const {
return static_cast<bool>(m_parsenode);
}
const AST_Node &get_parse_tree() const
{
return m_parsenode;
if (m_parsenode) {
return *m_parsenode;
} else {
throw std::runtime_error("Dynamic_Proxy_Function does not have parse_tree");
}
}
@ -445,7 +454,7 @@ namespace chaiscript
private:
Proxy_Function m_guard;
AST_NodePtr m_parsenode;
std::shared_ptr<AST_Node> m_parsenode;
};
@ -457,7 +466,7 @@ namespace chaiscript
Dynamic_Proxy_Function_Impl(
Callable t_f,
int t_arity=-1,
AST_NodePtr t_parsenode = AST_NodePtr(),
std::shared_ptr<AST_Node> t_parsenode = AST_NodePtr(),
Param_Types t_param_types = Param_Types(),
Proxy_Function t_guard = Proxy_Function())
: Dynamic_Proxy_Function(

View File

@ -48,6 +48,7 @@ namespace chaiscript
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Signature, T>>(t));
}
template<typename Ret, typename ... Param>
Proxy_Function fun(Ret (*func)(Param...))
{
@ -77,13 +78,45 @@ namespace chaiscript
}
template<typename T, typename Class /*, typename = typename std::enable_if<std::is_member_object_pointer<T>::value>::type*/>
Proxy_Function fun(T Class::* m /*, typename std::enable_if<std::is_member_object_pointer<T>::value>::type* = 0*/ )
{
return Proxy_Function(chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Attribute_Access<T, Class>>(m));
}
// only compile this bit if noexcept is part of the type system
//
#if (defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510) || (defined(_NOEXCEPT_TYPES_SUPPORTED) && _MSC_VER >= 1912)
template<typename Ret, typename ... Param>
Proxy_Function fun(Ret (*func)(Param...) noexcept)
{
auto fun_call = dispatch::detail::Fun_Caller<Ret, Param...>(func);
return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (Param...), decltype(fun_call)>>(fun_call));
}
template<typename Ret, typename Class, typename ... Param>
Proxy_Function fun(Ret (Class::*t_func)(Param...) const noexcept)
{
auto call = dispatch::detail::Const_Caller<Ret, Class, Param...>(t_func);
return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (const Class &, Param...), decltype(call)>>(call));
}
template<typename Ret, typename Class, typename ... Param>
Proxy_Function fun(Ret (Class::*t_func)(Param...) noexcept)
{
auto call = dispatch::detail::Caller<Ret, Class, Param...>(t_func);
return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (Class &, Param...), decltype(call)>>(call));
}
#endif

View File

@ -338,9 +338,7 @@ namespace chaiscript
: m_mutex(),
m_conversions(),
m_convertableTypes(),
m_num_types(0),
m_thread_cache(this),
m_conversion_saves(this)
m_num_types(0)
{
}

View File

@ -46,9 +46,9 @@ namespace chaiscript
constexpr Type_Info() = 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

@ -62,7 +62,7 @@ namespace chaiscript
/// Types of AST nodes available to the parser and eval
enum class AST_Node_Type { Id, Fun_Call, Unused_Return_Fun_Call, Arg_List, Equation, Var_Decl,
enum class AST_Node_Type { Id, Fun_Call, Unused_Return_Fun_Call, Arg_List, Equation, Var_Decl, Assign_Decl,
Array_Call, Dot_Access,
Lambda, Block, Scopeless_Block, Def, While, If, For, Ranged_For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range,
Inline_Range, Try, Catch, Finally, Method, Attr_Decl,
@ -77,7 +77,7 @@ namespace chaiscript
{
/// Helper lookup to get the name of each node type
inline const char *ast_node_type_to_string(AST_Node_Type ast_node_type) {
static const char * const ast_node_types[] = { "Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl",
static const char * const ast_node_types[] = { "Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", "Assign_Decl",
"Array_Call", "Dot_Access",
"Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range",
"Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl",
@ -124,8 +124,10 @@ namespace chaiscript
/// \brief Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree
typedef std::shared_ptr<AST_Node> AST_NodePtr;
typedef std::shared_ptr<const AST_Node> AST_NodePtr_Const;
typedef std::unique_ptr<AST_Node> AST_NodePtr;
typedef std::unique_ptr<const AST_Node> AST_NodePtr_Const;
struct AST_Node_Trace;
/// \brief Classes which may be thrown during error cases when ChaiScript is executing.
@ -168,7 +170,7 @@ namespace chaiscript
File_Position start_position;
std::string filename;
std::string detail;
std::vector<AST_NodePtr_Const> call_stack;
std::vector<AST_Node_Trace> call_stack;
eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname,
const std::vector<Boxed_Value> &t_parameters, const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
@ -228,26 +230,26 @@ namespace chaiscript
template<typename T>
static AST_Node_Type id(const T& t)
{
return t->identifier;
return t.identifier;
}
template<typename T>
static std::string pretty(const T& t)
{
return t->pretty_print();
return t.pretty_print();
}
template<typename T>
static const std::string &fname(const T& t)
{
return t->filename();
return t.filename();
}
template<typename T>
static std::string startpos(const T& t)
{
std::ostringstream oss;
oss << t->start().line << ", " << t->start().column;
oss << t.start().line << ", " << t.start().column;
return oss.str();
}
@ -260,6 +262,7 @@ namespace chaiscript
bool t_dot_notation,
const chaiscript::detail::Dispatch_Engine &t_ss)
{
assert(t_func);
int arity = t_func->get_arity();
std::vector<Type_Info> types = t_func->get_param_types();
@ -308,20 +311,20 @@ namespace chaiscript
std::shared_ptr<const dispatch::Dynamic_Proxy_Function> dynfun
= std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_func);
if (dynfun)
if (dynfun && dynfun->has_parse_tree())
{
Proxy_Function f = dynfun->get_guard();
if (f)
{
auto dynfunguard = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(f);
if (dynfunguard)
if (dynfunguard && dynfunguard->has_parse_tree())
{
retval += " : " + format_guard(dynfunguard->get_parse_tree());
}
}
retval += "\n Defined at " + format_location(dynfun->get_parse_tree());
retval += "\n Defined at " + format_location(dynfun->get_parse_tree());
}
return retval;
@ -330,20 +333,15 @@ namespace chaiscript
template<typename T>
static std::string format_guard(const T &t)
{
return t->pretty_print();
return t.pretty_print();
}
template<typename T>
static std::string format_location(const T &t)
{
if (t) {
std::ostringstream oss;
oss << "(" << t->filename() << " " << t->start().line << ", " << t->start().column << ")";
return oss.str();
} else {
return "(internal)";
}
std::ostringstream oss;
oss << "(" << t.filename() << " " << t.start().line << ", " << t.start().column << ")";
return oss.str();
}
static std::string format_detail(const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
@ -353,6 +351,7 @@ namespace chaiscript
std::stringstream ss;
if (t_functions.size() == 1)
{
assert(t_functions[0]);
ss << " Expected: " << format_types(t_functions[0], t_dot_notation, t_ss) << '\n';
} else {
ss << " " << t_functions.size() << " overloads available:\n";
@ -481,18 +480,21 @@ 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) noexcept
: 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;
};
}
/// \brief Struct that doubles as both a parser ast_node and an AST node.
struct AST_Node : std::enable_shared_from_this<AST_Node> {
struct AST_Node {
public:
const AST_Node_Type identifier;
const std::string text;
@ -516,14 +518,14 @@ namespace chaiscript
oss << text;
for (auto & elem : this->get_children()) {
oss << elem->pretty_print() << ' ';
for (auto & elem : get_children()) {
oss << elem.get().pretty_print() << ' ';
}
return oss.str();
}
virtual std::vector<AST_NodePtr> get_children() const = 0;
virtual std::vector<std::reference_wrapper<AST_Node>> get_children() const = 0;
virtual Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const = 0;
@ -534,8 +536,8 @@ namespace chaiscript
oss << t_prepend << "(" << ast_node_type_to_string(this->identifier) << ") "
<< this->text << " : " << this->location.start.line << ", " << this->location.start.column << '\n';
for (auto & elem : this->get_children()) {
oss << elem->to_string(t_prepend + " ");
for (auto & elem : get_children()) {
oss << elem.get().to_string(t_prepend + " ");
}
return oss.str();
}
@ -568,12 +570,60 @@ namespace chaiscript
};
struct AST_Node_Trace
{
const AST_Node_Type identifier;
const std::string text;
Parse_Location location;
const std::string &filename() const {
return *location.filename;
}
const File_Position &start() const {
return location.start;
}
const File_Position &end() const {
return location.end;
}
std::string pretty_print() const
{
std::ostringstream oss;
oss << text;
for (const auto & elem : children) {
oss << elem.pretty_print() << ' ';
}
return oss.str();
}
std::vector<AST_Node_Trace> get_children(const AST_Node &node)
{
const auto node_children = node.get_children();
return std::vector<AST_Node_Trace>(node_children.begin(), node_children.end());
}
AST_Node_Trace(const AST_Node &node)
: identifier(node.identifier), text(node.text),
location(node.location), children(get_children(node))
{
}
std::vector<AST_Node_Trace> children;
};
namespace parser {
class ChaiScript_Parser_Base
{
public:
virtual AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) = 0;
virtual void debug_print(AST_NodePtr t, std::string prepend = "") const = 0;
virtual void debug_print(const AST_Node &t, std::string prepend = "") const = 0;
virtual void *get_tracer_ptr() = 0;
virtual ~ChaiScript_Parser_Base() = default;
ChaiScript_Parser_Base() = default;
@ -600,8 +650,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

@ -36,12 +36,13 @@
#include <unistd.h>
#endif
#if defined(_POSIX_VERSION) && !defined(__CYGWIN__)
#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__)
#include <dlfcn.h>
#endif
#ifdef CHAISCRIPT_WINDOWS
#if defined(CHAISCRIPT_NO_DYNLOAD)
#include "chaiscript_unknown.hpp"
#elif defined(CHAISCRIPT_WINDOWS)
#include "chaiscript_windows.hpp"
#elif _POSIX_VERSION
#include "chaiscript_posix.hpp"
@ -54,6 +55,8 @@
namespace chaiscript
{
/// Namespace alias to provide cleaner and more explicit syntax to users.
using Namespace = dispatch::Dynamic_Object;
namespace detail
{
@ -78,6 +81,8 @@ namespace chaiscript
chaiscript::detail::Dispatch_Engine m_engine;
std::map<std::string, std::function<Namespace&()>> m_namespace_generators;
/// Evaluates the given string in by parsing it and running the results through the evaluator
Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false)
{
@ -185,7 +190,7 @@ namespace chaiscript
}
m_engine.add(fun([this](const std::string &t_str){ return internal_eval(t_str); }), "eval");
m_engine.add(fun([this](const AST_NodePtr &t_ast){ return eval(t_ast); }), "eval");
m_engine.add(fun([this](const AST_Node &t_ast){ return eval(t_ast); }), "eval");
m_engine.add(fun([this](const std::string &t_str, const bool t_dump){ return parse(t_str, t_dump); }), "parse");
m_engine.add(fun([this](const std::string &t_str){ return parse(t_str); }), "parse");
@ -194,8 +199,32 @@ namespace chaiscript
m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global_const(t_bv, t_name); }), "add_global_const");
m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global");
m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global");
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) {
@ -205,17 +234,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());
}
}
@ -242,7 +276,7 @@ namespace chaiscript
m_parser(std::move(parser)),
m_engine(*m_parser)
{
#if defined(_POSIX_VERSION) && !defined(__CYGWIN__)
#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__)
// If on Unix, add the path of the current executable to the module search path
// as windows would do
@ -278,6 +312,7 @@ namespace chaiscript
build_eval_system(t_lib, t_opts);
}
#ifndef CHAISCRIPT_NO_DYNLOAD
/// \brief Constructor for ChaiScript.
///
/// This version of the ChaiScript constructor attempts to find the stdlib module to load
@ -307,16 +342,22 @@ namespace chaiscript
throw;
}
}
#else // CHAISCRIPT_NO_DYNLOAD
explicit ChaiScript_Basic(std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
std::vector<std::string> t_module_paths = {},
std::vector<std::string> t_use_paths = {},
const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options()) = delete;
#endif
parser::ChaiScript_Parser_Base &get_parser()
{
return *m_parser;
}
const Boxed_Value eval(const AST_NodePtr &t_ast)
const Boxed_Value eval(const AST_Node &t_ast)
{
try {
return t_ast->eval(chaiscript::detail::Dispatch_State(m_engine));
return t_ast.eval(chaiscript::detail::Dispatch_State(m_engine));
} catch (const exception::eval_error &t_ee) {
throw Boxed_Value(t_ee);
}
@ -324,9 +365,9 @@ namespace chaiscript
AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false)
{
const auto ast = m_parser->parse(t_input, "PARSE");
auto ast = m_parser->parse(t_input, "PARSE");
if (t_debug_print) {
m_parser->debug_print(ast);
m_parser->debug_print(*ast);
}
return ast;
}
@ -353,8 +394,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);
@ -370,7 +411,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
}
}
@ -548,6 +593,10 @@ namespace chaiscript
/// \throw chaiscript::exception::load_module_error In the event that no matching module can be found.
std::string load_module(const std::string &t_module_name)
{
#ifdef CHAISCRIPT_NO_DYNLOAD
(void)t_module_name; // -Wunused-parameter
throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)");
#else
std::vector<exception::load_module_error> errors;
std::string version_stripped_name = t_module_name;
size_t version_pos = version_stripped_name.find("-" + Build_Info::version());
@ -581,6 +630,7 @@ namespace chaiscript
}
throw chaiscript::exception::load_module_error(t_module_name, errors);
#endif
}
/// \brief Load a binary module from a dynamic library. Works on platforms that support
@ -690,6 +740,41 @@ namespace chaiscript
T eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) {
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.");
}
}
};
}

View File

@ -47,13 +47,13 @@ namespace chaiscript
{
template<typename T> struct AST_Node_Impl;
template<typename T> using AST_Node_Impl_Ptr = typename std::shared_ptr<AST_Node_Impl<T>>;
template<typename T> using AST_Node_Impl_Ptr = typename std::unique_ptr<AST_Node_Impl<T>>;
namespace detail
{
/// Helper function that will set up the scope around a function call, including handling the named function parameters
template<typename T>
static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl_Ptr<T> &t_node, const std::vector<std::string> &t_param_names, const std::vector<Boxed_Value> &t_vals, const std::map<std::string, Boxed_Value> *t_locals=nullptr, bool has_this_capture = false) {
static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl<T> &t_node, const std::vector<std::string> &t_param_names, const std::vector<Boxed_Value> &t_vals, const std::map<std::string, Boxed_Value> *t_locals=nullptr, bool has_this_capture = false) {
chaiscript::detail::Dispatch_State state(t_ss);
const Boxed_Value *thisobj = [&]() -> const Boxed_Value *{
@ -83,11 +83,28 @@ namespace chaiscript
}
try {
return t_node->eval(state);
return t_node.eval(state);
} catch (detail::Return_Value &rv) {
return std::move(rv.retval);
}
}
inline Boxed_Value clone_if_necessary(Boxed_Value incoming, std::atomic_uint_fast32_t &t_loc, const chaiscript::detail::Dispatch_State &t_ss)
{
if (!incoming.is_return_value())
{
if (incoming.get_type_info().is_arithmetic()) {
return Boxed_Number::clone(incoming);
} else if (incoming.get_type_info().bare_equal_type_info(typeid(bool))) {
return Boxed_Value(*static_cast<const bool*>(incoming.get_const_ptr()));
} else {
return t_ss->call_function("clone", t_loc, {incoming}, t_ss.conversions());
}
} else {
incoming.reset_return_value();
return incoming;
}
}
}
template<typename T>
@ -106,8 +123,14 @@ namespace chaiscript
}
std::vector<AST_NodePtr> get_children() const final {
return {children.begin(), children.end()};
std::vector<std::reference_wrapper<AST_Node>> get_children() const final {
std::vector<std::reference_wrapper<AST_Node>> retval;
retval.reserve(children.size());
for (auto &&child : children) {
retval.emplace_back(*child);
}
return retval;
}
Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const final
@ -116,7 +139,7 @@ namespace chaiscript
T::trace(t_e, this);
return eval_internal(t_e);
} catch (exception::eval_error &ee) {
ee.call_stack.push_back(shared_from_this());
ee.call_stack.push_back(*this);
throw;
}
}
@ -282,7 +305,9 @@ namespace chaiscript
template<typename T>
struct Fun_Call_AST_Node : AST_Node_Impl<T> {
Fun_Call_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::Fun_Call, std::move(t_loc), std::move(t_children)) { }
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) {
assert(!this->children.empty());
}
template<bool Save_Params>
Boxed_Value do_eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const
@ -302,15 +327,17 @@ 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))(params, t_ss.conversions());
return (*t_ss->boxed_cast<ConstFunctionTypePtr>(fn))(params, t_ss.conversions());
}
catch(const exception::dispatch_error &e){
throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'", e.parameters, e.functions, false, *t_ss);
}
catch(const exception::bad_boxed_cast &){
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, {f}, false, *t_ss);
} catch (const exception::bad_boxed_cast &) {
@ -364,44 +391,44 @@ namespace chaiscript
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { }
static std::string get_arg_name(const AST_Node_Impl_Ptr<T> &t_node) {
if (t_node->children.empty())
static std::string get_arg_name(const AST_Node_Impl<T> &t_node) {
if (t_node.children.empty())
{
return t_node->text;
} else if (t_node->children.size() == 1) {
return t_node->children[0]->text;
return t_node.text;
} else if (t_node.children.size() == 1) {
return t_node.children[0]->text;
} else {
return t_node->children[1]->text;
return t_node.children[1]->text;
}
}
static std::vector<std::string> get_arg_names(const AST_Node_Impl_Ptr<T> &t_node) {
static std::vector<std::string> get_arg_names(const AST_Node_Impl<T> &t_node) {
std::vector<std::string> retval;
for (const auto &node : t_node->children)
for (const auto &node : t_node.children)
{
retval.push_back(get_arg_name(node));
retval.push_back(get_arg_name(*node));
}
return retval;
}
static std::pair<std::string, Type_Info> get_arg_type(const AST_Node_Impl_Ptr<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss)
static std::pair<std::string, Type_Info> get_arg_type(const AST_Node_Impl<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss)
{
if (t_node->children.size() < 2)
if (t_node.children.size() < 2)
{
return {};
} else {
return {t_node->children[0]->text, t_ss->get_type(t_node->children[0]->text, false)};
return {t_node.children[0]->text, t_ss->get_type(t_node.children[0]->text, false)};
}
}
static dispatch::Param_Types get_arg_types(const AST_Node_Impl_Ptr<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) {
static dispatch::Param_Types get_arg_types(const AST_Node_Impl<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) {
std::vector<std::pair<std::string, Type_Info>> retval;
for (const auto &child : t_node->children)
for (const auto &child : t_node.children)
{
retval.push_back(get_arg_type(child, t_ss));
retval.push_back(get_arg_type(*child, t_ss));
}
return dispatch::Param_Types(std::move(retval));
@ -421,19 +448,22 @@ namespace chaiscript
Boxed_Value rhs = this->children[1]->eval(t_ss);
Boxed_Value lhs = this->children[0]->eval(t_ss);
if (lhs.is_return_value()) {
throw exception::eval_error("Error, cannot assign to temporary value.");
} else if (lhs.is_const()) {
throw exception::eval_error("Error, cannot assign to constant value.");
}
if (m_oper != Operators::Opers::invalid && lhs.get_type_info().is_arithmetic() &&
rhs.get_type_info().is_arithmetic())
{
try {
return Boxed_Number::do_oper(m_oper, lhs, rhs);
} 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 (lhs.is_return_value()) {
throw exception::eval_error("Error, cannot assign to temporary value.");
}
try {
if (lhs.is_undef()) {
@ -443,7 +473,7 @@ namespace chaiscript
&& this->children[0]->children[0]->identifier == AST_Node_Type::Reference)
)
)
{
/// \todo This does not handle the case of an unassigned reference variable
/// being assigned outside of its declaration
@ -451,11 +481,7 @@ namespace chaiscript
lhs.reset_return_value();
return rhs;
} else {
if (!rhs.is_return_value())
{
rhs = t_ss->call_function("clone", m_clone_loc, {rhs}, t_ss.conversions());
}
rhs.reset_return_value();
rhs = detail::clone_if_necessary(std::move(rhs), m_clone_loc, t_ss);
}
}
@ -534,6 +560,27 @@ namespace chaiscript
}
};
template<typename T>
struct Assign_Decl_AST_Node final : AST_Node_Impl<T> {
Assign_Decl_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::Assign_Decl, std::move(t_loc), std::move(t_children)) { }
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
const std::string &idname = this->children[0]->text;
try {
Boxed_Value bv(detail::clone_if_necessary(this->children[1]->eval(t_ss), m_loc, t_ss));
bv.reset_return_value();
t_ss.add_object(idname, bv);
return bv;
} catch (const exception::name_conflict_error &e) {
throw exception::eval_error("Variable redefined '" + e.name() + "'");
}
}
private:
mutable std::atomic_uint_fast32_t m_loc = {0};
};
template<typename T>
struct Array_Call_AST_Node final : AST_Node_Impl<T> {
@ -621,9 +668,15 @@ namespace chaiscript
template<typename T>
struct Lambda_AST_Node final : AST_Node_Impl<T> {
Lambda_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>(t_ast_node_text, AST_Node_Type::Lambda, std::move(t_loc), std::move(t_children)),
m_param_names(Arg_List_AST_Node<T>::get_arg_names(this->children[1])),
m_this_capture(has_this_capture(this->children[0]->children))
AST_Node_Impl<T>(t_ast_node_text,
AST_Node_Type::Lambda,
std::move(t_loc),
std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()),
std::make_move_iterator(std::prev(t_children.end())))
),
m_param_names(Arg_List_AST_Node<T>::get_arg_names(*this->children[1])),
m_this_capture(has_this_capture(this->children[0]->children)),
m_lambda_node(std::move(t_children.back()))
{ }
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
@ -637,18 +690,18 @@ namespace chaiscript
}();
const auto numparams = this->children[1]->children.size();
const auto param_types = Arg_List_AST_Node<T>::get_arg_types(this->children[1], t_ss);
const auto param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[1], t_ss);
const auto &lambda_node = this->children.back();
std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
return Boxed_Value(
dispatch::make_dynamic_proxy_function(
[engine, lambda_node, param_names = this->m_param_names, captures, this_capture = this->m_this_capture](const std::vector<Boxed_Value> &t_params)
[engine, lambda_node = this->m_lambda_node, param_names = this->m_param_names, captures,
this_capture = this->m_this_capture] (const std::vector<Boxed_Value> &t_params)
{
return detail::eval_function(engine, lambda_node, param_names, t_params, &captures, this_capture);
return detail::eval_function(engine, *lambda_node, param_names, t_params, &captures, this_capture);
},
static_cast<int>(numparams), lambda_node, param_types
static_cast<int>(numparams), m_lambda_node, param_types
)
);
}
@ -664,7 +717,7 @@ namespace chaiscript
private:
const std::vector<std::string> m_param_names;
const bool m_this_capture = false;
const std::shared_ptr<AST_Node_Impl<T>> m_lambda_node;
};
template<typename T>
@ -699,55 +752,83 @@ namespace chaiscript
template<typename T>
struct Def_AST_Node final : AST_Node_Impl<T> {
std::shared_ptr<AST_Node_Impl<T>> m_body_node;
std::shared_ptr<AST_Node_Impl<T>> m_guard_node;
Def_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::Def, std::move(t_loc), std::move(t_children)) { }
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc),
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))
{ }
static std::shared_ptr<AST_Node_Impl<T>> get_guard_node(std::vector<AST_Node_Impl_Ptr<T>> &&vec, bool has_guard)
{
if (has_guard) {
return std::move(*std::prev(vec.end(), 2));
} else {
return {};
}
}
static std::shared_ptr<AST_Node_Impl<T>> get_body_node(std::vector<AST_Node_Impl_Ptr<T>> &&vec)
{
return std::move(vec.back());
}
static bool has_guard(const std::vector<AST_Node_Impl_Ptr<T>> &t_children, const std::size_t offset)
{
if ((t_children.size() > 2 + offset) && (t_children[1+offset]->identifier == AST_Node_Type::Arg_List)) {
if (t_children.size() > 3 + offset) {
return true;
}
}
else {
if (t_children.size() > 2 + offset) {
return true;
}
}
return false;
}
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
std::vector<std::string> t_param_names;
size_t numparams = 0;
AST_Node_Impl_Ptr<T> guardnode;
dispatch::Param_Types param_types;
if ((this->children.size() > 2) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) {
if ((this->children.size() > 1) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) {
numparams = this->children[1]->children.size();
t_param_names = Arg_List_AST_Node<T>::get_arg_names(this->children[1]);
param_types = Arg_List_AST_Node<T>::get_arg_types(this->children[1], t_ss);
if (this->children.size() > 3) {
guardnode = this->children[2];
}
}
else {
//no parameters
numparams = 0;
if (this->children.size() > 2) {
guardnode = this->children[1];
}
t_param_names = Arg_List_AST_Node<T>::get_arg_names(*this->children[1]);
param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[1], t_ss);
}
std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
std::shared_ptr<dispatch::Proxy_Function_Base> guard;
if (guardnode) {
if (m_guard_node) {
guard = dispatch::make_dynamic_proxy_function(
[engine, guardnode, t_param_names](const std::vector<Boxed_Value> &t_params)
[engine, guardnode = m_guard_node, t_param_names](const std::vector<Boxed_Value> &t_params)
{
return detail::eval_function(engine, guardnode, t_param_names, t_params);
return detail::eval_function(engine, *guardnode, t_param_names, t_params);
},
static_cast<int>(numparams), guardnode);
static_cast<int>(numparams), m_guard_node);
}
try {
const std::string & l_function_name = this->children[0]->text;
const auto & func_node = this->children.back();
t_ss->add(
dispatch::make_dynamic_proxy_function(
[engine, guardnode, func_node, t_param_names](const std::vector<Boxed_Value> &t_params)
[engine, func_node = m_body_node, t_param_names](const std::vector<Boxed_Value> &t_params)
{
return detail::eval_function(engine, func_node, t_param_names, t_params);
return detail::eval_function(engine, *func_node, t_param_names, t_params);
},
static_cast<int>(numparams), this->children.back(),
static_cast<int>(numparams), m_body_node,
param_types, guard), l_function_name);
} catch (const exception::name_conflict_error &e) {
throw exception::eval_error("Function redefined '" + e.name() + "'");
@ -844,10 +925,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 &) {
@ -871,10 +958,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 &) {
@ -1016,12 +1102,7 @@ namespace chaiscript
if (!this->children.empty()) {
vec.reserve(this->children[0]->children.size());
for (const auto &child : this->children[0]->children) {
auto obj = child->eval(t_ss);
if (!obj.is_return_value()) {
vec.push_back(t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions()));
} else {
vec.push_back(std::move(obj));
}
vec.push_back(detail::clone_if_necessary(child->eval(t_ss), m_loc, t_ss));
}
}
return const_var(std::move(vec));
@ -1046,12 +1127,8 @@ namespace chaiscript
std::map<std::string, Boxed_Value> retval;
for (const auto &child : this->children[0]->children) {
auto obj = child->children[1]->eval(t_ss);
if (!obj.is_return_value()) {
obj = t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions());
}
retval[t_ss->boxed_cast<std::string>(child->children[0]->eval(t_ss))] = std::move(obj);
retval.insert(std::make_pair(t_ss->boxed_cast<std::string>(child->children[0]->eval(t_ss)),
detail::clone_if_necessary(child->children[1]->eval(t_ss), m_loc, t_ss)));
}
return const_var(std::move(retval));
@ -1072,10 +1149,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()};
}
}
};
@ -1132,6 +1209,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);
@ -1230,32 +1311,32 @@ namespace chaiscript
}
for (size_t i = 1; i < end_point; ++i) {
chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss);
AST_Node_Impl_Ptr<T> catch_block = this->children[i];
auto &catch_block = *this->children[i];
if (catch_block->children.size() == 1) {
if (catch_block.children.size() == 1) {
//No variable capture, no guards
retval = catch_block->children[0]->eval(t_ss);
retval = catch_block.children[0]->eval(t_ss);
break;
} else if (catch_block->children.size() == 2 || catch_block->children.size() == 3) {
const auto name = Arg_List_AST_Node<T>::get_arg_name(catch_block->children[0]);
} else if (catch_block.children.size() == 2 || catch_block.children.size() == 3) {
const auto name = Arg_List_AST_Node<T>::get_arg_name(*catch_block.children[0]);
if (dispatch::Param_Types(
std::vector<std::pair<std::string, Type_Info>>{Arg_List_AST_Node<T>::get_arg_type(catch_block->children[0], t_ss)}
std::vector<std::pair<std::string, Type_Info>>{Arg_List_AST_Node<T>::get_arg_type(*catch_block.children[0], t_ss)}
).match(std::vector<Boxed_Value>{t_except}, t_ss.conversions()).first)
{
t_ss.add_object(name, t_except);
if (catch_block->children.size() == 2) {
if (catch_block.children.size() == 2) {
//Variable capture, no guards
retval = catch_block->children[1]->eval(t_ss);
retval = catch_block.children[1]->eval(t_ss);
break;
}
else if (catch_block->children.size() == 3) {
else if (catch_block.children.size() == 3) {
//Variable capture, guards
bool guard = false;
try {
guard = boxed_cast<bool>(catch_block->children[1]->eval(t_ss));
guard = boxed_cast<bool>(catch_block.children[1]->eval(t_ss));
} catch (const exception::bad_boxed_cast &) {
if (this->children.back()->identifier == AST_Node_Type::Finally) {
this->children.back()->children[0]->eval(t_ss);
@ -1263,7 +1344,7 @@ namespace chaiscript
throw exception::eval_error("Guard condition not boolean");
}
if (guard) {
retval = catch_block->children[2]->eval(t_ss);
retval = catch_block.children[2]->eval(t_ss);
break;
}
}
@ -1335,8 +1416,18 @@ namespace chaiscript
template<typename T>
struct Method_AST_Node final : AST_Node_Impl<T> {
std::shared_ptr<AST_Node_Impl<T>> m_body_node;
std::shared_ptr<AST_Node_Impl<T>> m_guard_node;
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::move(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::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))),
m_guard_node(Def_AST_Node<T>::get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2))
{
}
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
@ -1348,39 +1439,27 @@ namespace chaiscript
std::vector<std::string> t_param_names{"this"};
dispatch::Param_Types param_types;
if ((this->children.size() > 3)
if ((this->children.size() > 2)
&& (this->children[2]->identifier == AST_Node_Type::Arg_List)) {
auto args = Arg_List_AST_Node<T>::get_arg_names(this->children[2]);
auto args = Arg_List_AST_Node<T>::get_arg_names(*this->children[2]);
t_param_names.insert(t_param_names.end(), args.begin(), args.end());
param_types = Arg_List_AST_Node<T>::get_arg_types(this->children[2], t_ss);
if (this->children.size() > 4) {
guardnode = this->children[3];
}
}
else {
//no parameters
if (this->children.size() > 3) {
guardnode = this->children[2];
}
param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[2], t_ss);
}
const size_t numparams = t_param_names.size();
std::shared_ptr<dispatch::Proxy_Function_Base> guard;
std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
if (guardnode) {
if (m_guard_node) {
guard = dispatch::make_dynamic_proxy_function(
[engine, t_param_names, guardnode](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, guardnode, t_param_names, t_params);
[engine, t_param_names, guardnode = m_guard_node](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, *guardnode, t_param_names, t_params);
},
static_cast<int>(numparams), guardnode);
static_cast<int>(numparams), m_guard_node);
}
try {
const std::string & function_name = this->children[1]->text;
auto node = this->children.back();
if (function_name == class_name) {
param_types.push_front(class_name, Type_Info());
@ -1388,10 +1467,10 @@ namespace chaiscript
t_ss->add(
std::make_shared<dispatch::detail::Dynamic_Object_Constructor>(class_name,
dispatch::make_dynamic_proxy_function(
[engine, t_param_names, node](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params);
[engine, t_param_names, node = m_body_node](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params);
},
static_cast<int>(numparams), node, param_types, guard
static_cast<int>(numparams), m_body_node, param_types, guard
)
),
function_name);
@ -1402,12 +1481,13 @@ namespace chaiscript
auto type = t_ss->get_type(class_name, false);
param_types.push_front(class_name, type);
t_ss->add(std::make_shared<dispatch::detail::Dynamic_Object_Function>(class_name,
t_ss->add(
std::make_shared<dispatch::detail::Dynamic_Object_Function>(class_name,
dispatch::make_dynamic_proxy_function(
[engine, t_param_names, node](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params);
[engine, t_param_names, node = m_body_node](const std::vector<Boxed_Value> &t_params) {
return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params);
},
static_cast<int>(numparams), node, param_types, guard), type),
static_cast<int>(numparams), m_body_node, param_types, guard), type),
function_name);
}
} catch (const exception::name_conflict_error &e) {

View File

@ -24,17 +24,26 @@ namespace chaiscript {
template<typename Tracer>
auto optimize(eval::AST_Node_Impl_Ptr<Tracer> p) {
(void)std::initializer_list<int>{ (p = static_cast<T&>(*this).optimize(p), 0)... };
(void)std::initializer_list<int>{ (p = static_cast<T&>(*this).optimize(std::move(p)), 0)... };
return p;
}
};
template<typename T>
auto child_at(const eval::AST_Node_Impl_Ptr<T> &node, const size_t offset) {
if (node->children[offset]->identifier == AST_Node_Type::Compiled) {
return dynamic_cast<const eval::Compiled_AST_Node<T>&>(*node->children[offset]).m_original_node;
eval::AST_Node_Impl<T> &child_at(eval::AST_Node_Impl<T> &node, const size_t offset) {
if (node.children[offset]->identifier == AST_Node_Type::Compiled) {
return *(dynamic_cast<eval::Compiled_AST_Node<T> &>(*node.children[offset]).m_original_node);
} else {
return node->children[offset];
return *node.children[offset];
}
}
template<typename T>
const eval::AST_Node_Impl<T> &child_at(const eval::AST_Node_Impl<T> &node, const size_t offset) {
if (node.children[offset]->identifier == AST_Node_Type::Compiled) {
return *(dynamic_cast<const eval::Compiled_AST_Node<T> &>(*node.children[offset]).m_original_node);
} else {
return *node.children[offset];
}
@ -48,24 +57,24 @@ namespace chaiscript {
}
template<typename T>
auto child_count(const eval::AST_Node_Impl_Ptr<T> &node) {
if (node->identifier == AST_Node_Type::Compiled) {
return dynamic_cast<const eval::Compiled_AST_Node<T>&>(*node).m_original_node->children.size();
auto child_count(const eval::AST_Node_Impl<T> &node) {
if (node.identifier == AST_Node_Type::Compiled) {
return dynamic_cast<const eval::Compiled_AST_Node<T>&>(node).m_original_node->children.size();
} else {
return node->children.size();
return node.children.size();
}
}
template<typename T, typename Callable>
auto make_compiled_node(const eval::AST_Node_Impl_Ptr<T> &original_node, std::vector<eval::AST_Node_Impl_Ptr<T>> children, Callable callable)
auto make_compiled_node(eval::AST_Node_Impl_Ptr<T> original_node, std::vector<eval::AST_Node_Impl_Ptr<T>> children, Callable callable)
{
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Compiled_AST_Node<T>>(original_node, std::move(children), std::move(callable));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Compiled_AST_Node<T>>(std::move(original_node), std::move(children), std::move(callable));
}
struct Return {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &p)
auto optimize(eval::AST_Node_Impl_Ptr<T> p)
{
if ( (p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda)
&& !p->children.empty())
@ -75,7 +84,7 @@ namespace chaiscript {
auto &block_last_child = last_child->children.back();
if (block_last_child->identifier == AST_Node_Type::Return) {
if (block_last_child->children.size() == 1) {
last_child->children.back() = block_last_child->children[0];
last_child->children.back() = std::move(block_last_child->children[0]);
}
}
}
@ -86,9 +95,9 @@ namespace chaiscript {
};
template<typename T>
bool contains_var_decl_in_scope(const T &node)
bool contains_var_decl_in_scope(const eval::AST_Node_Impl<T> &node)
{
if (node->identifier == AST_Node_Type::Var_Decl) {
if (node.identifier == AST_Node_Type::Var_Decl || node.identifier == AST_Node_Type::Assign_Decl || node.identifier == AST_Node_Type::Reference) {
return true;
}
@ -96,8 +105,9 @@ namespace chaiscript {
for (size_t i = 0; i < num; ++i) {
const auto &child = child_at(node, i);
if (child->identifier != AST_Node_Type::Block
&& child->identifier != AST_Node_Type::For
if (child.identifier != AST_Node_Type::Block
&& child.identifier != AST_Node_Type::For
&& child.identifier != AST_Node_Type::Ranged_For
&& contains_var_decl_in_scope(child)) {
return true;
}
@ -108,15 +118,16 @@ namespace chaiscript {
struct Block {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if (node->identifier == AST_Node_Type::Block)
{
if (!contains_var_decl_in_scope(node))
if (!contains_var_decl_in_scope(*node))
{
if (node->children.size() == 1) {
return node->children[0];
return std::move(node->children[0]);
} else {
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Scopeless_Block_AST_Node<T>>(node->text, node->location, node->children);
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Scopeless_Block_AST_Node<T>>(node->text, node->location,
std::move(node->children));
}
}
}
@ -127,7 +138,7 @@ namespace chaiscript {
struct Dead_Code {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if (node->identifier == AST_Node_Type::Block)
{
std::vector<size_t> keepers;
@ -135,10 +146,10 @@ namespace chaiscript {
keepers.reserve(num_children);
for (size_t i = 0; i < num_children; ++i) {
auto child = node->children[i];
if ( (child->identifier != AST_Node_Type::Id
&& child->identifier != AST_Node_Type::Constant
&& child->identifier != AST_Node_Type::Noop)
const auto &child = *node->children[i];
if ( (child.identifier != AST_Node_Type::Id
&& child.identifier != AST_Node_Type::Constant
&& child.identifier != AST_Node_Type::Noop)
|| i == num_children - 1) {
keepers.push_back(i);
}
@ -147,12 +158,16 @@ namespace chaiscript {
if (keepers.size() == num_children) {
return node;
} else {
std::vector<eval::AST_Node_Impl_Ptr<T>> new_children;
for (const auto x : keepers)
{
new_children.push_back(node->children[x]);
}
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Block_AST_Node<T>>(node->text, node->location, new_children);
const auto new_children = [&](){
std::vector<eval::AST_Node_Impl_Ptr<T>> retval;
for (const auto x : keepers)
{
retval.push_back(std::move(node->children[x]));
}
return retval;
};
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Block_AST_Node<T>>(node->text, node->location, new_children());
}
} else {
return node;
@ -162,29 +177,30 @@ namespace chaiscript {
struct Unused_Return {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if ((node->identifier == AST_Node_Type::Block
|| node->identifier == AST_Node_Type::Scopeless_Block)
&& !node->children.empty())
{
for (size_t i = 0; i < node->children.size()-1; ++i) {
auto child = node->children[i];
auto child = node->children[i].get();
if (child->identifier == AST_Node_Type::Fun_Call) {
node->children[i] = chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(child->text, child->location, std::move(child->children));
node->children[i] = chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(child->text, child->location,
std::move(child->children));
}
}
} else if ((node->identifier == AST_Node_Type::For
|| node->identifier == AST_Node_Type::While)
&& child_count(node) > 0) {
auto child = child_at(node, child_count(node) - 1);
if (child->identifier == AST_Node_Type::Block
|| child->identifier == AST_Node_Type::Scopeless_Block)
&& child_count(*node) > 0) {
auto &child = child_at(*node, child_count(*node) - 1);
if (child.identifier == AST_Node_Type::Block
|| child.identifier == AST_Node_Type::Scopeless_Block)
{
auto num_sub_children = child_count(child);
for (size_t i = 0; i < num_sub_children; ++i) {
auto sub_child = child_at(child, i);
if (sub_child->identifier == AST_Node_Type::Fun_Call) {
child->children[i] = chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(sub_child->text, sub_child->location, std::move(sub_child->children));
auto &sub_child = child_at(child, i);
if (sub_child.identifier == AST_Node_Type::Fun_Call) {
child.children[i] = chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(sub_child.text, sub_child.location, std::move(sub_child.children));
}
}
}
@ -193,19 +209,40 @@ namespace chaiscript {
}
};
struct Assign_Decl {
template<typename T>
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if ((node->identifier == AST_Node_Type::Equation)
&& node->text == "="
&& node->children.size() == 2
&& node->children[0]->identifier == AST_Node_Type::Var_Decl
)
{
std::vector<eval::AST_Node_Impl_Ptr<T>> new_children;
new_children.push_back(std::move(node->children[0]->children[0]));
new_children.push_back(std::move(node->children[1]));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Assign_Decl_AST_Node<T>>(node->text,
node->location, std::move(new_children) );
}
return node;
}
};
struct If {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if ((node->identifier == AST_Node_Type::If)
&& node->children.size() >= 2
&& node->children[0]->identifier == AST_Node_Type::Constant)
{
const auto condition = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[0])->m_value;
const auto condition = dynamic_cast<eval::Constant_AST_Node<T> *>(node->children[0].get())->m_value;
if (condition.get_type_info().bare_equal_type_info(typeid(bool))) {
if (boxed_cast<bool>(condition)) {
return node->children[1];
return std::move(node->children[1]);
} else if (node->children.size() == 3) {
return node->children[2];
return std::move(node->children[2]);
}
}
}
@ -216,7 +253,7 @@ namespace chaiscript {
struct Partial_Fold {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
// Fold right side
if (node->identifier == AST_Node_Type::Binary
@ -228,9 +265,10 @@ namespace chaiscript {
const auto &oper = node->text;
const auto parsed = Operators::to_operator(oper);
if (parsed != Operators::Opers::invalid) {
const auto rhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[1])->m_value;
const auto rhs = dynamic_cast<eval::Constant_AST_Node<T> *>(node->children[1].get())->m_value;
if (rhs.get_type_info().is_arithmetic()) {
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Fold_Right_Binary_Operator_AST_Node<T>>(node->text, node->location, node->children, rhs);
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Fold_Right_Binary_Operator_AST_Node<T>>(node->text, node->location,
std::move(node->children), rhs);
}
}
} catch (const std::exception &) {
@ -244,7 +282,7 @@ namespace chaiscript {
struct Constant_Fold {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if (node->identifier == AST_Node_Type::Prefix
&& node->children.size() == 1
@ -253,14 +291,14 @@ namespace chaiscript {
try {
const auto &oper = node->text;
const auto parsed = Operators::to_operator(oper, true);
const auto lhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[0])->m_value;
const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> *>(node->children[0].get())->m_value;
const auto match = oper + node->children[0]->text;
if (parsed != Operators::Opers::invalid && parsed != Operators::Opers::bitwise_and && lhs.get_type_info().is_arithmetic()) {
const auto val = Boxed_Number::do_oper(parsed, lhs);
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
} else if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && oper == "!") {
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(!boxed_cast<bool>(lhs)));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(!boxed_cast<bool>(lhs)));
}
} catch (const std::exception &) {
//failure to fold, that's OK
@ -271,8 +309,8 @@ namespace chaiscript {
&& node->children[1]->identifier == AST_Node_Type::Constant)
{
try {
const auto lhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[0])->m_value;
const auto rhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[1])->m_value;
const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[0]).m_value;
const auto rhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]).m_value;
if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && rhs.get_type_info().bare_equal_type_info(typeid(bool))) {
const auto match = node->children[0]->text + " " + node->text + " " + node->children[1]->text;
const auto val = [lhs_val = boxed_cast<bool>(lhs), rhs_val = boxed_cast<bool>(rhs), id = node->identifier] {
@ -280,7 +318,7 @@ namespace chaiscript {
else { return Boxed_Value(lhs_val || rhs_val); }
}();
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
}
} catch (const std::exception &) {
//failure to fold, that's OK
@ -294,12 +332,12 @@ namespace chaiscript {
const auto &oper = node->text;
const auto parsed = Operators::to_operator(oper);
if (parsed != Operators::Opers::invalid) {
const auto lhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[0])->m_value;
const auto rhs = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[1])->m_value;
const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[0]).m_value;
const auto rhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]).m_value;
if (lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) {
const auto val = Boxed_Number::do_oper(parsed, lhs, rhs);
const auto match = node->children[0]->text + " " + oper + " " + node->children[1]->text;
return chaiscript::make_shared<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
}
}
} catch (const std::exception &) {
@ -312,13 +350,13 @@ namespace chaiscript {
&& node->children[1]->children.size() == 1
&& node->children[1]->children[0]->identifier == AST_Node_Type::Constant) {
const auto arg = std::dynamic_pointer_cast<eval::Constant_AST_Node<T>>(node->children[1]->children[0])->m_value;
const auto arg = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]->children[0]).m_value;
if (arg.get_type_info().is_arithmetic()) {
const auto &fun_name = node->children[0]->text;
const auto make_constant = [&node, &fun_name](auto val){
const auto match = fun_name + "(" + node->children[1]->children[0]->text + ")";
return chaiscript::make_shared<eval::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") {
@ -344,35 +382,36 @@ namespace chaiscript {
struct For_Loop {
template<typename T>
auto optimize(const eval::AST_Node_Impl_Ptr<T> &for_node) {
auto optimize(eval::AST_Node_Impl_Ptr<T> for_node) {
if (for_node->identifier != AST_Node_Type::For) {
return for_node;
}
const auto eq_node = child_at(for_node, 0);
const auto binary_node = child_at(for_node, 1);
const auto prefix_node = child_at(for_node, 2);
const auto &eq_node = child_at(*for_node, 0);
const auto &binary_node = child_at(*for_node, 1);
const auto &prefix_node = child_at(*for_node, 2);
if (eq_node->identifier == AST_Node_Type::Equation
if (child_count(*for_node) == 4
&& eq_node.identifier == AST_Node_Type::Assign_Decl
&& child_count(eq_node) == 2
&& child_at(eq_node, 0)->identifier == AST_Node_Type::Var_Decl
&& child_at(eq_node, 1)->identifier == AST_Node_Type::Constant
&& binary_node->identifier == AST_Node_Type::Binary
&& binary_node->text == "<"
&& child_at(eq_node, 0).identifier == AST_Node_Type::Id
&& child_at(eq_node, 1).identifier == AST_Node_Type::Constant
&& binary_node.identifier == AST_Node_Type::Binary
&& binary_node.text == "<"
&& child_count(binary_node) == 2
&& child_at(binary_node, 0)->identifier == AST_Node_Type::Id
&& child_at(binary_node, 0)->text == child_at(child_at(eq_node,0), 0)->text
&& child_at(binary_node, 1)->identifier == AST_Node_Type::Constant
&& prefix_node->identifier == AST_Node_Type::Prefix
&& prefix_node->text == "++"
&& child_at(binary_node, 0).identifier == AST_Node_Type::Id
&& child_at(binary_node, 0).text == child_at(eq_node,0).text
&& child_at(binary_node, 1).identifier == AST_Node_Type::Constant
&& prefix_node.identifier == AST_Node_Type::Prefix
&& prefix_node.text == "++"
&& child_count(prefix_node) == 1
&& child_at(prefix_node, 0)->identifier == AST_Node_Type::Id
&& child_at(prefix_node, 0)->text == child_at(child_at(eq_node,0), 0)->text)
&& child_at(prefix_node, 0).identifier == AST_Node_Type::Id
&& child_at(prefix_node, 0).text == child_at(eq_node,0).text)
{
const Boxed_Value &begin = std::dynamic_pointer_cast<const eval::Constant_AST_Node<T>>(child_at(eq_node, 1))->m_value;
const Boxed_Value &end = std::dynamic_pointer_cast<const eval::Constant_AST_Node<T>>(child_at(binary_node, 1))->m_value;
const std::string &id = child_at(prefix_node, 0)->text;
const Boxed_Value &begin = dynamic_cast<const eval::Constant_AST_Node<T> &>(child_at(eq_node, 1)).m_value;
const Boxed_Value &end = dynamic_cast<const eval::Constant_AST_Node<T> &>(child_at(binary_node, 1)).m_value;
const std::string &id = child_at(prefix_node, 0).text;
if (begin.get_type_info().bare_equal(user_type<int>())
&& end.get_type_info().bare_equal(user_type<int>())) {
@ -380,9 +419,14 @@ namespace chaiscript {
const auto start_int = boxed_cast<int>(begin);
const auto end_int = boxed_cast<int>(end);
const auto body = child_at(for_node, 3);
return make_compiled_node(for_node, {body},
// note that we are moving the last element out, then popping the empty shared_ptr
// from the vector
std::vector<eval::AST_Node_Impl_Ptr<T>> body_vector;
auto body_child = std::move(for_node->children[3]);
for_node->children.pop_back();
body_vector.emplace_back(std::move(body_child));
return make_compiled_node(std::move(for_node), std::move(body_vector),
[id, start_int, end_int](const std::vector<eval::AST_Node_Impl_Ptr<T>> &children, const chaiscript::detail::Dispatch_State &t_ss) {
assert(children.size() == 1);
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
@ -418,7 +462,7 @@ namespace chaiscript {
};
typedef Optimizer<optimizer::Partial_Fold, optimizer::Unused_Return, optimizer::Constant_Fold,
optimizer::If, optimizer::Return, optimizer::Dead_Code, optimizer::Block, optimizer::For_Loop> Optimizer_Default;
optimizer::If, optimizer::Return, optimizer::Dead_Code, optimizer::Block, optimizer::For_Loop, optimizer::Assign_Decl> Optimizer_Default;
}
}

View File

@ -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() 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;
};
static std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> build_alphabet()
{
std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> alphabet;
@ -397,7 +419,7 @@ namespace chaiscript
return m_optimizer;
}
ChaiScript_Parser(const ChaiScript_Parser &) = default;
ChaiScript_Parser(const ChaiScript_Parser &) = delete;
ChaiScript_Parser &operator=(const ChaiScript_Parser &) = delete;
ChaiScript_Parser(ChaiScript_Parser &&) = default;
ChaiScript_Parser &operator=(ChaiScript_Parser &&) = delete;
@ -406,10 +428,10 @@ namespace chaiscript
bool char_in_alphabet(char c, detail::Alphabet a) const { return m_alphabet[a][static_cast<uint8_t>(c)]; }
/// Prints the parsed ast_nodes as a tree
void debug_print(AST_NodePtr t, std::string prepend = "") const override {
std::cout << prepend << "(" << ast_node_type_to_string(t->identifier) << ") " << t->text << " : " << t->start().line << ", " << t->start().column << '\n';
for (const auto &node : t->get_children()) {
debug_print(node, prepend + " ");
void debug_print(const AST_Node &t, std::string prepend = "") const override {
std::cout << prepend << "(" << ast_node_type_to_string(t.identifier) << ") " << t.text << " : " << t.start().line << ", " << t.start().column << '\n';
for (const auto &node : t.get_children()) {
debug_print(node.get(), prepend + " ");
}
}
@ -452,7 +474,7 @@ namespace chaiscript
/// \todo fix the fact that a successful match that captured no ast_nodes doesn't have any real start position
m_match_stack.push_back(
m_optimizer.optimize(
chaiscript::make_shared<chaiscript::eval::AST_Node_Impl<Tracer>, NodeType>(
chaiscript::make_unique<chaiscript::eval::AST_Node_Impl<Tracer>, NodeType>(
std::move(t_text),
std::move(filepos),
std::move(new_children)))
@ -520,10 +542,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)) {
@ -734,6 +760,7 @@ namespace chaiscript
#ifdef CHAISCRIPT_CLANG
#pragma GCC diagnostic ignored "-Wtautological-compare"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
#endif
@ -748,7 +775,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));
@ -779,9 +809,9 @@ namespace chaiscript
}
template<typename T, typename ... Param>
std::shared_ptr<eval::AST_Node_Impl<Tracer>> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param)
std::unique_ptr<eval::AST_Node_Impl<Tracer>> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param)
{
return chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, T>(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward<Param>(param)...);
return chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, T>(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward<Param>(param)...);
}
/// Reads a number from the input, detecting if it's an integer or floating point
@ -914,7 +944,7 @@ namespace chaiscript
} break;
case utility::fnv1a_32("__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) {
@ -927,7 +957,7 @@ namespace chaiscript
} break;
case utility::fnv1a_32("__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
@ -995,7 +1025,7 @@ namespace chaiscript
int in_interpolation = 0;
bool in_quote = false;
while (m_position.has_more() && ((*m_position != '\"') || ((*m_position == '\"') && (in_interpolation > 0)) || ((*m_position == '\"') && (prev_char == '\\')))) {
while (m_position.has_more() && ((*m_position != '\"') || (in_interpolation > 0) || (prev_char == '\\'))) {
if (!Eol_()) {
if (prev_char == '$' && *m_position == '{') {
@ -1036,7 +1066,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;
@ -1051,23 +1081,30 @@ namespace chaiscript
Char_Parser &operator=(const Char_Parser &) = delete;
~Char_Parser(){
if (is_octal) {
process_octal();
}
try {
if (is_octal) {
process_octal();
}
if (is_hex) {
process_hex();
}
if (is_hex) {
process_hex();
}
if (is_unicode) {
process_unicode();
if (unicode_size > 0) {
process_unicode();
}
} catch (const std::invalid_argument &) {
} catch (const exception::eval_error &) {
// Something happened with parsing, we'll catch it later?
}
}
void process_hex()
{
auto val = stoll(hex_matches, nullptr, 16);
match.push_back(char_type(val));
if (!hex_matches.empty()) {
auto val = stoll(hex_matches, nullptr, 16);
match.push_back(char_type(val));
}
hex_matches.clear();
is_escaped = false;
is_hex = false;
@ -1076,8 +1113,10 @@ namespace chaiscript
void process_octal()
{
auto val = stoll(octal_matches, nullptr, 8);
match.push_back(char_type(val));
if (!octal_matches.empty()) {
auto val = stoll(octal_matches, nullptr, 8);
match.push_back(char_type(val));
}
octal_matches.clear();
is_escaped = false;
is_octal = false;
@ -1086,11 +1125,43 @@ namespace chaiscript
void process_unicode()
{
auto val = stoll(hex_matches, nullptr, 16);
const auto ch = static_cast<uint32_t>(std::stoi(hex_matches, nullptr, 16));
const auto match_size = hex_matches.size();
hex_matches.clear();
match += detail::Char_Parser_Helper<string_type>::str_from_ll(val);
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) {
@ -1126,16 +1197,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
@ -1158,7 +1229,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;
@ -1189,6 +1262,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;
@ -1254,6 +1328,7 @@ namespace chaiscript
cparser.saw_interpolation_marker = false;
} else {
cparser.parse(*s, start.line, start.col, *m_filename);
++s;
}
}
@ -1281,7 +1356,7 @@ namespace chaiscript
char prev_char = *m_position;
++m_position;
while (m_position.has_more() && ((*m_position != '\'') || ((*m_position == '\'') && (prev_char == '\\')))) {
while (m_position.has_more() && ((*m_position != '\'') || (prev_char == '\\'))) {
if (!Eol_()) {
if (prev_char == '\\') {
prev_char = 0;
@ -1303,6 +1378,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;
@ -1342,6 +1418,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);
}
@ -1366,6 +1443,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);
@ -1390,6 +1468,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);
@ -1424,6 +1503,7 @@ namespace chaiscript
/// Reads until the end of the current statement
bool Eos() {
Depth_Counter dc{this};
SkipWS();
return Eol_(true);
@ -1431,6 +1511,7 @@ namespace chaiscript
/// Reads (and potentially captures) an end-of-line group from input
bool Eol() {
Depth_Counter dc{this};
SkipWS();
return Eol_();
@ -1438,6 +1519,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;
@ -1463,6 +1545,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;
@ -1489,6 +1572,7 @@ namespace chaiscript
/// Reads a comma-separated list of values from input
bool Arg_List() {
Depth_Counter dc{this};
SkipWS(true);
bool retval = false;
@ -1514,6 +1598,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);
@ -1551,6 +1636,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();
@ -1592,6 +1678,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();
@ -1651,6 +1738,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();
@ -1710,6 +1798,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();
@ -1759,7 +1848,7 @@ namespace chaiscript
if ((is_if_init && num_children == 3)
|| (!is_if_init && num_children == 2)) {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
if (!is_if_init) {
@ -1775,6 +1864,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();
@ -1807,6 +1897,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();
@ -1836,6 +1927,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();
}
@ -1843,13 +1935,14 @@ namespace chaiscript
/// Reads the C-style `for` conditions from input
bool For_Guards() {
Depth_Counter dc{this};
if (!(Equation() && Eol()))
{
if (!Eol())
{
return false;
} else {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
}
@ -1859,13 +1952,13 @@ namespace chaiscript
{
return false;
} else {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Constant_AST_Node<Tracer>>(Boxed_Value(true)));
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Constant_AST_Node<Tracer>>(Boxed_Value(true)));
}
}
if (!Equation())
{
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
return true;
@ -1874,6 +1967,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();
@ -1896,9 +1990,17 @@ namespace chaiscript
throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename);
}
const auto num_children = m_match_stack.size() - prev_stack_top;
if (classic_for) {
if (num_children != 4) {
throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename);
}
build_match<eval::For_AST_Node<Tracer>>(prev_stack_top);
} else {
if (num_children != 3) {
throw exception::eval_error("Incomplete ranged-for expression", File_Position(m_position.line, m_position.col), *m_filename);
}
build_match<eval::Ranged_For_AST_Node<Tracer>>(prev_stack_top);
}
}
@ -1909,6 +2011,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();
@ -1949,6 +2052,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")) {
@ -1992,6 +2096,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();
@ -2005,7 +2110,7 @@ namespace chaiscript
}
if (m_match_stack.size() == prev_stack_top) {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
build_match<eval::Block_AST_Node<Tracer>>(prev_stack_top);
@ -2016,6 +2121,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();
@ -2029,7 +2135,7 @@ namespace chaiscript
}
if (m_match_stack.size() == prev_stack_top) {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
build_match<eval::Block_AST_Node<Tracer>>(prev_stack_top);
@ -2040,6 +2146,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")) {
@ -2053,6 +2160,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")) {
@ -2065,6 +2173,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")) {
@ -2077,6 +2186,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();
@ -2105,13 +2215,13 @@ namespace chaiscript
}
if (m_match_stack.back()->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
}
auto dot_access = m_match_stack.back()->children[0];
auto func_call = m_match_stack.back();
auto dot_access = std::move(m_match_stack.back()->children[0]);
auto func_call = std::move(m_match_stack.back());
m_match_stack.pop_back();
func_call->children.erase(func_call->children.begin());
if (dot_access->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
}
func_call->children.insert(func_call->children.begin(), dot_access->children.back());
func_call->children.insert(func_call->children.begin(), std::move(dot_access->children.back()));
dot_access->children.pop_back();
dot_access->children.push_back(std::move(func_call));
if (dot_access->children.size() != 2) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
@ -2147,6 +2257,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();
@ -2172,7 +2283,7 @@ namespace chaiscript
throw exception::eval_error("Incomplete variable declaration", File_Position(m_position.line, m_position.col), *m_filename);
}
} else if (Keyword("GLOBAL") || Keyword("global")) {
} else if (Keyword("global")) {
retval = true;
if (!(Reference() || Id(true))) {
@ -2202,6 +2313,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);
@ -2217,6 +2329,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('[')) {
@ -2248,6 +2361,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("&")) {
@ -2264,6 +2378,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;
constexpr const std::array<utility::Static_String, 6> prefix_opers{{
@ -2294,10 +2409,12 @@ 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();
}
bool Operator_Helper(const size_t t_precedence, std::string &oper) {
Depth_Counter dc{this};
for (auto & elem : m_operator_matches[t_precedence]) {
if (Symbol(elem)) {
oper = elem.c_str();
@ -2308,6 +2425,7 @@ 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();
@ -2372,6 +2490,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();
@ -2399,6 +2518,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();
@ -2426,6 +2546,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;
@ -2451,6 +2572,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;
@ -2479,6 +2601,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;
@ -2517,24 +2640,23 @@ namespace chaiscript
AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) override
{
ChaiScript_Parser<Tracer, Optimizer> parser(*this);
parser.m_match_stack.clear();
ChaiScript_Parser<Tracer, Optimizer> parser(m_tracer, m_optimizer);
return parser.parse_internal(t_input, t_fname);
}
eval::AST_Node_Impl_Ptr<Tracer> parse_instr_eval(const std::string &t_input)
{
const auto last_position = m_position;
const auto last_filename = m_filename;
const auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){});
auto last_position = m_position;
auto last_filename = m_filename;
auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){});
const auto retval = parse_internal(t_input, "instr eval");
auto retval = parse_internal(t_input, "instr eval");
m_position = std::move(last_position);
m_filename = std::move(last_filename);
m_match_stack = std::move(last_match_stack);
return std::dynamic_pointer_cast<eval::AST_Node_Impl<Tracer>>(retval);
return eval::AST_Node_Impl_Ptr<Tracer>(dynamic_cast<eval::AST_Node_Impl<Tracer>*>(retval.release()));
}
/// Parses the given input string, tagging parsed ast_nodes with the given m_filename.
@ -2546,20 +2668,21 @@ namespace chaiscript
while (m_position.has_more() && (!Eol())) {
++m_position;
}
/// \todo respect // -*- coding: utf-8 -*- on line 1 or 2 see: http://evanjones.ca/python-utf8.html)
}
if (Statements(true)) {
if (m_position.has_more()) {
throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), t_fname);
throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), *m_filename);
} else {
build_match<eval::File_AST_Node<Tracer>>(0);
}
} else {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
return m_match_stack.front();
AST_NodePtr retval(std::move(m_match_stack.front()));
m_match_stack.clear();
return retval;
}
};
}

View File

@ -41,7 +41,7 @@ namespace chaiscript
struct DLSym
{
DLSym(DLModule &t_mod, const std::string &t_symbol)
: m_symbol(cast_symbol(dlsym(t_mod.m_data, t_symbol.c_str())))
: m_symbol(reinterpret_cast<T>(dlsym(t_mod.m_data, t_symbol.c_str())))
{
if (!m_symbol)
{
@ -49,19 +49,6 @@ namespace chaiscript
}
}
static T cast_symbol(void *p)
{
union cast_union
{
T func_ptr;
void *in_ptr;
};
cast_union c;
c.in_ptr = p;
return c.func_ptr;
}
T m_symbol;
};

View File

@ -16,7 +16,11 @@ namespace chaiscript
{
Loadable_Module(const std::string &, const std::string &)
{
#ifdef CHAISCRIPT_NO_DYNLOAD
throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)");
#else
throw chaiscript::exception::load_module_error("Loadable module support not available for your platform");
#endif
}
ModulePtr m_moduleptr;

View File

@ -131,7 +131,7 @@ class JSON
}
Internal( double d ) : Float( d ), Type(Class::Floating) {}
Internal( long l ) : Int( l ), Type(Class::Integral) {}
Internal( int64_t l ) : Int( l ), Type(Class::Integral) {}
Internal( bool b ) : Bool( b ), Type(Class::Boolean) {}
Internal( std::string s ) : String(std::make_unique<std::string>(std::move(s))), Type(Class::String) {}
Internal() : Type(Class::Null) {}
@ -192,7 +192,7 @@ class JSON
std::unique_ptr<QuickFlatMap> Map;
std::unique_ptr<std::string> String;
double Float = 0;
long Int = 0;
int64_t Int = 0;
bool Bool = false;
Class Type = Class::Null;
@ -248,7 +248,7 @@ class JSON
explicit JSON( T b, typename enable_if<is_same<T,bool>::value>::type* = nullptr ) : 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 ) : internal( static_cast<long>(i) ) {}
explicit JSON( T i, typename enable_if<is_integral<T>::value && !is_same<T,bool>::value>::type* = nullptr ) : internal( static_cast<int64_t>(i) ) {}
template <typename T>
explicit JSON( T f, typename enable_if<is_floating_point<T>::value>::type* = nullptr ) : internal( static_cast<double>(f) ) {}
@ -335,8 +335,8 @@ class JSON
return ok ? internal.Float : 0.0;
}
long to_int() const { bool b; return to_int( b ); }
long to_int( bool &ok ) const {
int64_t to_int() const { bool b; return to_int( b ); }
int64_t to_int( bool &ok ) const {
ok = (internal.Type == Class::Integral);
return ok ? internal.Int : 0;
}
@ -392,7 +392,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 ) + "}" ) ;
@ -463,7 +463,7 @@ struct JSONParser {
}
static void consume_ws( const std::string &str, size_t &offset ) {
while( isspace( str[offset] ) && offset <= str.size() ) { ++offset; }
while( isspace( str.at(offset) ) && offset <= str.size() ) { ++offset; }
}
static JSON parse_object( const std::string &str, size_t &offset ) {
@ -471,29 +471,29 @@ struct JSONParser {
++offset;
consume_ws( str, offset );
if( str[offset] == '}' ) {
if( str.at(offset) == '}' ) {
++offset; return Object;
}
for (;offset<str.size();) {
JSON Key = parse_next( str, offset );
consume_ws( str, offset );
if( str[offset] != ':' ) {
throw std::runtime_error(std::string("JSON ERROR: Object: Expected colon, found '") + str[offset] + "'\n");
if( str.at(offset) != ':' ) {
throw std::runtime_error(std::string("JSON ERROR: Object: Expected colon, found '") + str.at(offset) + "'\n");
}
consume_ws( str, ++offset );
JSON Value = parse_next( str, offset );
Object[Key.to_string()] = Value;
consume_ws( str, offset );
if( str[offset] == ',' ) {
if( str.at(offset) == ',' ) {
++offset; continue;
}
else if( str[offset] == '}' ) {
else if( str.at(offset) == '}' ) {
++offset; break;
}
else {
throw std::runtime_error(std::string("JSON ERROR: Object: Expected comma, found '") + str[offset] + "'\n");
throw std::runtime_error(std::string("JSON ERROR: Object: Expected comma, found '") + str.at(offset) + "'\n");
}
}
@ -506,7 +506,7 @@ struct JSONParser {
++offset;
consume_ws( str, offset );
if( str[offset] == ']' ) {
if( str.at(offset) == ']' ) {
++offset; return Array;
}
@ -514,14 +514,14 @@ struct JSONParser {
Array[index++] = parse_next( str, offset );
consume_ws( str, offset );
if( str[offset] == ',' ) {
if( str.at(offset) == ',' ) {
++offset; continue;
}
else if( str[offset] == ']' ) {
else if( str.at(offset) == ']' ) {
++offset; break;
}
else {
throw std::runtime_error(std::string("JSON ERROR: Array: Expected ',' or ']', found '") + str[offset] + "'\n");
throw std::runtime_error(std::string("JSON ERROR: Array: Expected ',' or ']', found '") + str.at(offset) + "'\n");
}
}
@ -530,9 +530,9 @@ struct JSONParser {
static JSON parse_string( const std::string &str, size_t &offset ) {
std::string val;
for( char c = str[++offset]; c != '\"' ; c = str[++offset] ) {
for( char c = str.at(++offset); c != '\"' ; c = str.at(++offset) ) {
if( c == '\\' ) {
switch( str[ ++offset ] ) {
switch( str.at(++offset) ) {
case '\"': val += '\"'; break;
case '\\': val += '\\'; break;
case '/' : val += '/' ; break;
@ -544,7 +544,7 @@ struct JSONParser {
case 'u' : {
val += "\\u" ;
for( size_t i = 1; i <= 4; ++i ) {
c = str[offset+i];
c = str.at(offset+i);
if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) {
val += c;
} else {
@ -567,12 +567,18 @@ struct JSONParser {
std::string val, exp_str;
char c = '\0';
bool isDouble = false;
long exp = 0;
bool isNegative = false;
int64_t exp = 0;
bool isExpNegative = false;
if( offset < str.size() && str.at(offset) == '-' ) {
isNegative = true;
++offset;
}
for (; offset < str.size() ;) {
c = str[offset++];
if( (c == '-') || (c >= '0' && c <= '9') ) {
c = str.at(offset++);
if( c >= '0' && c <= '9' ) {
val += c;
} else if( c == '.' ) {
} else if( c == '.' && !isDouble ) {
val += c;
isDouble = true;
} else {
@ -580,9 +586,9 @@ struct JSONParser {
}
}
if( offset < str.size() && (c == 'E' || c == 'e' )) {
c = str[ offset++ ];
c = str.at(offset++);
if( c == '-' ) {
exp_str += '-';
isExpNegative = true;
} else if( c == '+' ) {
// do nothing
} else {
@ -590,7 +596,7 @@ struct JSONParser {
}
for (; offset < str.size() ;) {
c = str[ offset++ ];
c = str.at(offset++);
if( c >= '0' && c <= '9' ) {
exp_str += c;
} else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) {
@ -598,9 +604,9 @@ struct JSONParser {
}
else {
break;
}
}
}
exp = chaiscript::parse_num<long>( exp_str );
exp = chaiscript::parse_num<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 + "'");
@ -608,12 +614,12 @@ struct JSONParser {
--offset;
if( isDouble ) {
return JSON(chaiscript::parse_num<double>( val ) * std::pow( 10, exp ));
return JSON((isNegative?-1:1) * chaiscript::parse_num<double>( val ) * std::pow( 10, exp ));
} else {
if( !exp_str.empty() ) {
return JSON(static_cast<double>(chaiscript::parse_num<long>( val )) * std::pow( 10, exp ));
return JSON((isNegative?-1:1) * static_cast<double>(chaiscript::parse_num<int64_t>( val )) * std::pow( 10, exp ));
} else {
return JSON(chaiscript::parse_num<long>( val ));
return JSON((isNegative?-1:1) * chaiscript::parse_num<int64_t>( val ));
}
}
}
@ -641,7 +647,7 @@ struct JSONParser {
static JSON parse_next( const std::string &str, size_t &offset ) {
char value;
consume_ws( str, offset );
value = str[offset];
value = str.at(offset);
switch( value ) {
case '[' : return parse_array( str, offset );
case '{' : return parse_object( str, offset );

View File

@ -63,7 +63,11 @@ namespace chaiscript
static Boxed_Value from_json(const std::string &t_json)
{
return from_json( json::JSON::Load(t_json) );
try {
return from_json( json::JSON::Load(t_json) );
} catch (const std::out_of_range& ) {
throw std::runtime_error("Unparsed JSON input");
}
}
static std::string to_json(const Boxed_Value &t_bv)
@ -76,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);
@ -89,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]);
@ -106,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
@ -128,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);
@ -138,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

@ -0,0 +1,11 @@
def var_test(int n)
{
for (var i = 0; i < n; ++i) {
var j = 0
}
}
var n = 500000
var_test(n) // takes 2.6 s

View File

@ -25,7 +25,7 @@ Release under the BSD license, see "license.txt" for details.
Introduction
============
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/ChaiScript/ChaiScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Gitter](https://badges.gitter.im/JoinChat.svg)](https://gitter.im/ChaiScript/ChaiScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
ChaiScript is one of the only embedded scripting language designed from the
ground up to directly target C++ and take advantage of modern C++ development
@ -79,7 +79,7 @@ directory, and for more in-depth look at the language, the unit tests in the
"unittests" directory cover the most ground.
For examples of how to register parts of your C++ application, see
"example.cpp" in the "src" directory. Example.cpp is verbose and shows every
"example.cpp" in the "samples" directory. Example.cpp is verbose and shows every
possible way of working with the library. For further documentation generate
the doxygen documentation in the build folder or see the website
http://www.chaiscript.com.
@ -87,44 +87,21 @@ http://www.chaiscript.com.
The shortest complete example possible follows:
/// main.cpp
```C++
/// main.cpp
#include <chaiscript/chaiscript.hpp>
#include <chaiscript/chaiscript.hpp>
double function(int i, double j)
{
return i * j;
}
int main()
{
chaiscript::ChaiScript chai;
chai.add(chaiscript::fun(&function), "function");
double d = chai.eval<double>("function(3, 4.75);");
}
Or, if you want to compile the std lib into your code, which reduces
runtime requirements.
/// main.cpp
#include <chaiscript/chaiscript.hpp>
#include <chaiscript/chaiscript_stdlib.hpp>
double function(int i, double j)
{
return i * j;
}
int main()
{
chaiscript::ChaiScript chai(chaiscript::Std_Lib::library());
chai.add(chaiscript::fun(&function), "function");
double d = chai.eval<double>("function(3, 4.75);");
}
double function(int i, double j)
{
return i * j;
}
int main()
{
chaiscript::ChaiScript chai;
chai.add(chaiscript::fun(&function), "function");
double d = chai.eval<double>("function(3, 4.75);");
}
```

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

12
samples/BUCK Normal file
View File

@ -0,0 +1,12 @@
cxx_binary(
name = 'example',
srcs = [
'example.cpp',
],
compiler_flags = [
'-std=c++14',
],
deps = [
'//:chaiscript',
],
)

View File

@ -51,11 +51,9 @@ struct System
void do_callbacks(const std::string &inp)
{
log("Running Callbacks: " + inp);
for (std::map<std::string, std::function<std::string (const std::string &)> >::iterator itr = m_callbacks.begin();
itr != m_callbacks.end();
++itr)
for (auto & m_callback : m_callbacks)
{
log("Callback: " + itr->first, itr->second(inp));
log("Callback: " + m_callback.first, m_callback.second(inp));
}
}
};
@ -88,25 +86,25 @@ int main(int /*argc*/, char * /*argv*/[]) {
// The function "{ 'Callback1' + x }" is created in chaiscript and passed into our C++ application
// in the "add_callback" function of struct System the chaiscript function is converted into a
// std::function, so it can be handled and called easily and type-safely
chai.eval("system.add_callback(\"#1\", fun(x) { \"Callback1 \" + x });");
chai.eval(R"(system.add_callback("#1", fun(x) { "Callback1 " + x });)");
// Because we are sharing the "system" object with the chaiscript engine we have equal
// access to it both from within chaiscript and from C++ code
system.do_callbacks("TestString");
chai.eval("system.do_callbacks(\"TestString\");");
chai.eval(R"(system.do_callbacks("TestString");)");
// The log function is overloaded, therefore we have to give the C++ compiler a hint as to which
// version we want to register. One way to do this is to create a typedef of the function pointer
// then cast your function to that typedef.
typedef void (*PlainLog)(const std::string &);
typedef void (*ModuleLog)(const std::string &, const std::string &);
using PlainLog = void (*)(const std::string &);
using ModuleLog = void (*)(const std::string &, const std::string &);
chai.add(fun(PlainLog(&log)), "log");
chai.add(fun(ModuleLog(&log)), "log");
chai.eval("log(\"Test Message\")");
chai.eval(R"(log("Test Message"))");
// A shortcut to using eval is just to use the chai operator()
chai("log(\"Test Module\", \"Test Message\");");
chai(R"(log("Test Module", "Test Message");)");
//Finally, it is possible to register a lambda as a system function, in this
//way, we can, for instance add a bound member function to the system
@ -115,7 +113,9 @@ int main(int /*argc*/, char * /*argv*/[]) {
//Call bound version of do_callbacks
chai("do_callbacks()");
std::function<void ()> caller = chai.eval<std::function<void ()> >("fun() { system.do_callbacks(\"From Functor\"); }");
std::function<void ()> caller = chai.eval<std::function<void ()> >(
R"(fun() { system.do_callbacks("From Functor"); })"
);
caller();
@ -134,7 +134,7 @@ int main(int /*argc*/, char * /*argv*/[]) {
std::cout << "scripti: " << scripti << '\n';
scripti *= 2;
std::cout << "scripti (updated): " << scripti << '\n';
chai("print(\"Scripti from chai: \" + to_string(scripti))");
chai(R"(print("Scripti from chai: " + to_string(scripti)))");
//To do: Add examples of handling Boxed_Values directly when needed
@ -146,7 +146,7 @@ int main(int /*argc*/, char * /*argv*/[]) {
log("Functor test output", ss.str());
chai.add(var(std::shared_ptr<int>()), "nullvar");
chai("print(\"This should be true.\"); print(nullvar.is_var_null())");
chai(R"(print("This should be true."); print(nullvar.is_var_null()))");
// test the global const action
chai.add_global_const(const_var(1), "constvar");
@ -160,7 +160,7 @@ int main(int /*argc*/, char * /*argv*/[]) {
// Test ability to register a function that excepts a shared_ptr version of a type
chai("take_shared_ptr(\"Hello World as a shared_ptr\");");
chai(R"(take_shared_ptr("Hello World as a shared_ptr");)");
chai.add(fun(&bound_log, std::string("Msg")), "BoundFun");

View File

@ -68,6 +68,7 @@ std::vector<std::string> default_search_paths()
{
std::vector<std::string> paths;
#ifndef CHAISCRIPT_NO_DYNLOAD
#ifdef CHAISCRIPT_WINDOWS // force no unicode
CHAR path[4096];
int size = GetModuleFileNameA(0, path, sizeof(path) - 1);
@ -137,6 +138,7 @@ std::vector<std::string> default_search_paths()
paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/");
}
#endif
#endif // ifndef CHAISCRIPT_NO_DYNLOAD
return paths;
}
@ -250,7 +252,7 @@ void interactive(chaiscript::ChaiScript& chai)
catch (const chaiscript::exception::eval_error &ee) {
std::cout << ee.what();
if (ee.call_stack.size() > 0) {
std::cout << "during evaluation at (" << ee.call_stack[0]->start().line << ", " << ee.call_stack[0]->start().column << ")";
std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")";
}
std::cout << std::endl;
}

View File

@ -122,7 +122,7 @@ int main()
assert(myderived.getValue() == "1234");
chai.eval("myderived.setValue(\"new\")"); // set the value via chaiscript
chai.eval(R"(myderived.setValue("new"))"); // set the value via chaiscript
assert(myderived.getValue() == "new");
// call the other derived method via chaiscript and return the value to c++ land:

354
src/libfuzzer_client.cpp Normal file
View File

@ -0,0 +1,354 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <iostream>
#include <list>
#include <regex>
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <chaiscript/chaiscript.hpp>
#include "../static_libs/chaiscript_parser.hpp"
#include "../static_libs/chaiscript_stdlib.hpp"
#include "sha3.h"
#ifdef READLINE_AVAILABLE
#include <readline/readline.h>
#include <readline/history.h>
#else
char *mystrdup (const char *s) {
size_t len = strlen(s); // Space for length plus nul
char *d = static_cast<char*>(malloc (len+1));
if (d == nullptr) { return nullptr; } // No memory
#ifdef CHAISCRIPT_MSVC
strcpy_s(d, len+1, s); // Copy the characters
#else
strncpy(d,s,len); // Copy the characters
#endif
d[len] = '\0';
return d; // Return the new string
}
char* readline(const char* p)
{
std::string retval;
std::cout << p ;
std::getline(std::cin, retval);
return std::cin.eof() ? nullptr : mystrdup(retval.c_str());
}
void add_history(const char* /*unused*/){}
void using_history(){}
#endif
void *cast_module_symbol(std::vector<std::string> (*t_path)())
{
union cast_union
{
std::vector<std::string> (*in_ptr)();
void *out_ptr;
};
cast_union c;
c.in_ptr = t_path;
return c.out_ptr;
}
std::vector<std::string> default_search_paths()
{
std::vector<std::string> paths;
#ifndef CHAISCRIPT_NO_DYNLOAD
#ifdef CHAISCRIPT_WINDOWS // force no unicode
CHAR path[4096];
int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1);
std::string exepath(path, size);
size_t lastslash = exepath.rfind('\\');
size_t secondtolastslash = exepath.rfind('\\', lastslash - 1);
if (lastslash != std::string::npos)
{
paths.push_back(exepath.substr(0, lastslash));
}
if (secondtolastslash != std::string::npos)
{
return {exepath.substr(0, secondtolastslash) + "\\lib\\chaiscript\\"};
}
#else
std::string exepath;
std::vector<char> buf(2048);
ssize_t size = -1;
if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) >= 0)
{
exepath = std::string(&buf.front(), static_cast<size_t>(size));
}
if (exepath.empty())
{
if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) >= 0)
{
exepath = std::string(&buf.front(), static_cast<size_t>(size));
}
}
if (exepath.empty())
{
if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) >= 0)
{
exepath = std::string(&buf.front(), static_cast<size_t>(size));
}
}
if (exepath.empty())
{
Dl_info rInfo;
memset( &rInfo, 0, sizeof(rInfo) );
if ( dladdr(cast_module_symbol(&default_search_paths), &rInfo) == 0 || rInfo.dli_fname == nullptr ) {
return paths;
}
exepath = std::string(rInfo.dli_fname);
}
size_t lastslash = exepath.rfind('/');
size_t secondtolastslash = exepath.rfind('/', lastslash - 1);
if (lastslash != std::string::npos)
{
paths.push_back(exepath.substr(0, lastslash+1));
}
if (secondtolastslash != std::string::npos)
{
paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/");
}
#endif
#endif // ifndef CHAISCRIPT_NO_DYNLOAD
return paths;
}
void help(int n) {
if ( n >= 0 ) {
std::cout << "ChaiScript evaluator. To evaluate an expression, type it and press <enter>.\n";
std::cout << "Additionally, you can inspect the runtime system using:\n";
std::cout << " dump_system() - outputs all functions registered to the system\n";
std::cout << " dump_object(x) - dumps information about the given symbol\n";
} else {
std::cout << "usage : chai [option]+\n";
std::cout << "option:" << '\n';
std::cout << " -h | --help" << '\n';
std::cout << " -i | --interactive" << '\n';
std::cout << " -c | --command cmd" << '\n';
std::cout << " -v | --version" << '\n';
std::cout << " - --stdin" << '\n';
std::cout << " filepath" << '\n';
}
}
bool throws_exception(const std::function<void ()> &f)
{
try {
f();
} catch (...) {
return true;
}
return false;
}
chaiscript::exception::eval_error get_eval_error(const std::function<void ()> &f)
{
try {
f();
} catch (const chaiscript::exception::eval_error &e) {
return e;
}
throw std::runtime_error("no exception throw");
}
std::string get_next_command() {
std::string retval("quit");
if ( ! std::cin.eof() ) {
char *input_raw = readline("eval> ");
if ( input_raw != nullptr ) {
add_history(input_raw);
std::string val(input_raw);
size_t pos = val.find_first_not_of("\t \n");
if (pos != std::string::npos)
{
val.erase(0, pos);
}
pos = val.find_last_not_of("\t \n");
if (pos != std::string::npos)
{
val.erase(pos+1, std::string::npos);
}
retval = val;
::free(input_raw);
}
}
if( retval == "quit"
|| retval == "exit"
|| retval == "help"
|| retval == "version")
{
retval += "(0)";
}
return retval;
}
// We have to wrap exit with our own because Clang has a hard time with
// function pointers to functions with special attributes (system exit being marked NORETURN)
void myexit(int return_val) {
exit(return_val);
}
void interactive(chaiscript::ChaiScript_Basic& chai)
{
using_history();
for (;;) {
std::string input = get_next_command();
try {
// evaluate input
chaiscript::Boxed_Value val = chai.eval(input);
//Then, we try to print the result of the evaluation to the user
if (!val.get_type_info().bare_equal(chaiscript::user_type<void>())) {
try {
std::cout << chai.eval<std::function<std::string (const chaiscript::Boxed_Value &bv)> >("to_string")(val) << '\n';
}
catch (...) {} //If we can't, do nothing
}
}
catch (const chaiscript::exception::eval_error &ee) {
std::cout << ee.what();
if ( !ee.call_stack.empty() ) {
std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")";
}
std::cout << '\n';
}
catch (const std::exception &e) {
std::cout << e.what();
std::cout << '\n';
}
}
}
double now()
{
using namespace std::chrono;
auto now = high_resolution_clock::now();
return duration_cast<duration<double>>(now.time_since_epoch()).count();
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
chaiscript::ChaiScript chai;
chai.eval( R"chaiscript(
def assert_equal(x, y)
{
if (x == y)
{
// Passes
} else {
// Fails
print("assert_equal failure: got '" + to_string(y) + "' expected '" + to_string(x) + "'");
// exit(-1);
}
}
def assert_false(f)
{
if (f)
{
print("assert_false failure");
// exit(-1);
}
}
def assert_true(f)
{
if (!f)
{
print("assert_true failure");
// exit(-1);
}
}
def assert_not_equal(x, y)
{
if (!(x == y))
{
// Passes
} else {
// Fails
print("assert_not_equal failure: got " + to_string(y) + " which was not expected.");
// exit(-1);
}
}
def assert_throws(desc, x)
{
if (throws_exception(x))
{
// Passes
} else {
// Fails
print("assert_throws failure, function did not throw exception: " + to_string(desc));
// exit(-1);
}
})chaiscript");
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));
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 &) {
std::ofstream ofs("STD_EXCEPTION/" + sha);
ofs << input;
} catch (...) {
std::ofstream ofs("UNKOWN_EXCEPTION/" + sha);
ofs << input;
}
return 0;
}

View File

@ -71,6 +71,7 @@ std::vector<std::string> default_search_paths()
{
std::vector<std::string> paths;
#ifndef CHAISCRIPT_NO_DYNLOAD
#ifdef CHAISCRIPT_WINDOWS // force no unicode
CHAR path[4096];
int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1);
@ -140,6 +141,7 @@ std::vector<std::string> default_search_paths()
paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/");
}
#endif
#endif // ifndef CHAISCRIPT_NO_DYNLOAD
return paths;
}
@ -162,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)
@ -245,7 +247,7 @@ void interactive(chaiscript::ChaiScript_Basic& chai)
catch (const chaiscript::exception::eval_error &ee) {
std::cout << ee.what();
if ( !ee.call_stack.empty() ) {
std::cout << "during evaluation at (" << ee.call_stack[0]->start().line << ", " << ee.call_stack[0]->start().column << ")";
std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")";
}
std::cout << '\n';
}
@ -306,6 +308,7 @@ int main(int argc, char *argv[])
bool eval_error_ok = false;
bool boxed_exception_ok = false;
bool any_exception_ok = false;
for (int i = 0; i < argc; ++i) {
if ( i == 0 && argc > 1 ) {
@ -342,6 +345,9 @@ int main(int argc, char *argv[])
} else if ( arg == "--exception" ) {
boxed_exception_ok = true;
continue;
} else if ( arg == "--any-exception" ) {
any_exception_ok = true;
continue;
} else if ( arg == "-i" || arg == "--interactive" ) {
mode = eInteractive ;
} else if ( arg.find('-') == 0 ) {
@ -381,11 +387,18 @@ int main(int argc, char *argv[])
catch (const chaiscript::exception::load_module_error &e) {
std::cout << "Unhandled module load error\n" << e.what() << '\n';
}
// catch (std::exception &e) {
// std::cout << e.what() << '\n';
// return EXIT_FAILURE;
// }
catch (std::exception &e) {
std::cout << "Unhandled standard exception: " << e.what() << '\n';
if (!any_exception_ok) {
throw;
}
}
catch (...) {
std::cout << "Unhandled unknown exception" << '\n';
if (!any_exception_ok) {
throw;
}
}
}
return EXIT_SUCCESS;

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

@ -78,7 +78,7 @@ int to_int(TestEnum t)
class TestDerivedType : public TestBaseType
{
public:
virtual ~TestDerivedType() {}
~TestDerivedType() override {}
TestDerivedType(const TestDerivedType &) = default;
TestDerivedType() = default;
virtual int func() override { return 1; }

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
@ -12,6 +12,8 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wparentheses"
// This one is necessary for the const return non-reference test
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#endif
@ -350,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()
{
@ -380,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);
@ -388,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);
}
@ -466,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)"));
}
}
@ -1269,4 +1293,65 @@ 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)
{
return i + 3;
}
TEST_CASE("Test returning by const non-reference")
{
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser());
// Note, C++ will not allow us to do this:
// chai.add(chaiscript::fun(&Reference_MyClass::x) , "x");
chai.add(chaiscript::fun(&add_3), "add_3");
auto v = chai.eval<int>("add_3(12)");
CHECK(v == 15);
}
struct MyException : std::runtime_error
{
using std::runtime_error::runtime_error;
int value = 5;
};
void throws_a_thing()
{
throw MyException("Hello World");
}
TEST_CASE("Test throwing and catching custom exception")
{
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser());
chai.add(chaiscript::user_type<MyException>(), "MyException");
chai.add(chaiscript::base_class<std::runtime_error, MyException>()); // be sure to register base class relationship
chai.add(chaiscript::fun(&throws_a_thing), "throws_a_thing");
chai.add(chaiscript::fun(&MyException::value), "value");
const auto s = chai.eval<std::string>("fun(){ try { throws_a_thing(); } catch (MyException ex) { return ex.what(); } }()");
CHECK(s == "Hello World");
// this has an explicit clone to prevent returning a pointer to the `value` from inside of MyException
const auto i = chai.eval<int>("fun(){ try { throws_a_thing(); } catch (MyException ex) { var v = clone(ex.value); print(v); return v; } }()");
CHECK(i == 5);
}
TEST_CASE("Test ability to get 'use' function from default construction")
{
chaiscript::ChaiScript chai;
const auto use_function = chai.eval<std::function<chaiscript::Boxed_Value (const std::string &)>>("use");
}

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

@ -0,0 +1,53 @@
def assert_equal(x, y)
{
if (x == y)
{
// Passes
} else {
// Fails
print("assert_equal failure: got '" + to_string(y) + "' expected '" + to_string(x) + "'");
// exit(-1);
}
}
def assert_false(f)
{
if (f)
{
print("assert_false failure");
// exit(-1);
}
}
def assert_true(f)
{
if (!f)
{
print("assert_true failure");
// exit(-1);
}
}
def assert_not_equal(x, y)
{
if (!(x == y))
{
// Passes
} else {
// Fails
print("assert_not_equal failure: got " + to_string(y) + " which was not expected.");
// exit(-1);
}
}
def assert_throws(desc, x)
{
if (throws_exception(x))
{
// Passes
} else {
// Fails
print("assert_throws failure, function did not throw exception: " + to_string(desc));
// exit(-1);
}
}

Binary file not shown.

View File

@ -1,12 +1,12 @@
// Test global
GLOBAL g = 3;
global g = 3;
assert_true(g == 3);
var v := g;
assert_true(v == 3);
GLOBAL g = 2;
global g = 2;
assert_true(g == 2);
assert_true(v == 2);

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 = `-`; } );

5
unittests/json_14.chai Normal file
View File

@ -0,0 +1,5 @@
assert_equal(from_json("9.9e-02"), 9.9e-02)
assert_equal(from_json("-13.57e+3"), -13570.0)
assert_equal(from_json("1E-01"), 0.1)
assert_equal(from_json("-314159e-5"), -3.14159)
assert_equal(from_json("5e+04"), 50000)

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 +1,3 @@
assert_equal(from_json("100"), 100)
assert_equal(from_json("-100"), -100)
assert_equal(to_json(4294967295), "4294967295")

View File

@ -1 +1,22 @@
assert_equal(from_json("1.234"), 1.234)
assert_equal(from_json("-1.234"), -1.234)
auto caught = false;
try {
from_json("-1-5.3");
}
catch(e) {
assert_equal("JSON ERROR: Number: unexpected character '-'", e.what());
caught = true;
}
assert_equal(caught, true);
caught = false;
try {
from_json("-15.3.2");
}
catch(e) {
assert_equal("JSON ERROR: Number: unexpected character '.'", e.what());
caught = true;
}
assert_equal(caught, true);

View File

@ -1,2 +1,2 @@
assert_equal(from_json("[1,2,3]"), [1,2,3])
assert_equal(from_json("[1,-2,3]"), [1,-2,3])

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

@ -2,6 +2,9 @@
#include <algorithm>
#ifdef CHAISCRIPT_NO_DYNLOAD
#include <chaiscript/chaiscript.hpp>
#endif
#include <chaiscript/chaiscript_basic.hpp>
#include <chaiscript/language/chaiscript_parser.hpp>
@ -57,18 +60,22 @@ int main()
}
std::vector<std::string> modulepaths;
#ifdef CHAISCRIPT_NO_DYNLOAD
chaiscript::ChaiScript chai(/* unused */modulepaths, usepaths);
#else
modulepaths.push_back("");
if (modulepath)
{
modulepaths.push_back(modulepath);
}
// For this test we are going to load the dynamic stdlib
// to make sure it continues to work
chaiscript::ChaiScript_Basic chai(
std::make_unique<chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Noop_Tracer, chaiscript::optimizer::Optimizer_Default>>(),
modulepaths,usepaths);
#endif
std::vector<std::shared_ptr<std::thread> > threads;

View File

@ -0,0 +1,7 @@
namespace("math")
math.square = fun(x) { x * x }
math.sum_squares = fun(x, y) { math.square(x) + math.square(y) }
assert_equal(16, math.square(4))
assert_equal(29, math.sum_squares(2, 5))

View File

@ -0,0 +1,9 @@
namespace("parent")
namespace("child")
child.x = 3.0
parent.child = child
parent.child.x = 5.0
assert_equal(3.0, child.x)
assert_equal(5.0, parent.child.x)

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

@ -18,4 +18,9 @@ for (x : retro(range([1,2,3,4]))) {
assert_true(result > .6 && result < .7);
var m=["a": ["t": "val"], "b": ["t": "val"]];
for (i : m) {
var &ref=i.second["t"];
print(ref);
}

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

@ -0,0 +1,13 @@
#ifndef CHAISCRIPT_NO_THREADS
#define CHAISCRIPT_NO_THREADS
#endif
/// ChaiScript as a static is unsupported with thread support enabled
///
#include <chaiscript/chaiscript.hpp>
static chaiscript::ChaiScript chai;
int main() {}

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; })