mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-01-01 03:12:23 +08:00
Merge pull request #441 from ChaiScript/develop
Update for release to 6.1.0
This commit is contained in:
commit
2e77b9d0bf
0
.buckconfig
Normal file
0
.buckconfig
Normal file
98
.clang-format
Normal file
98
.clang-format
Normal 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
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/buck-out/
|
||||
/.buckd/
|
||||
/buckaroo/
|
||||
.buckconfig.local
|
||||
BUCKAROO_DEPS
|
||||
23
.travis.yml
23
.travis.yml
@ -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
11
BUCK
Normal 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',
|
||||
],
|
||||
)
|
||||
@ -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()
|
||||
|
||||
|
||||
10
appveyor.yml
10
appveyor.yml
@ -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
3
buckaroo.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "ChaiScript"
|
||||
}
|
||||
@ -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
175
cmake/Catch.cmake
Normal 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
76
cmake/CatchAddTests.cmake
Normal 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}")
|
||||
185
cmake/ParseAndAddCatchTests.cmake
Normal file
185
cmake/ParseAndAddCatchTests.cmake
Normal 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
1
contrib/vim
Normal file
@ -0,0 +1 @@
|
||||
vim support can be found at https://github.com/ChaiScript/vim-chaiscript
|
||||
@ -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
|
||||
@ -1,2 +0,0 @@
|
||||
au BufRead,BufNewFile *.chai set filetype=chaiscript
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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>>(),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@ -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"}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
});
|
||||
|
||||
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)) { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
11
performance_tests/create_variables.chai
Normal file
11
performance_tests/create_variables.chai
Normal 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
|
||||
|
||||
|
||||
55
readme.md
55
readme.md
@ -25,7 +25,7 @@ Release under the BSD license, see "license.txt" for details.
|
||||
Introduction
|
||||
============
|
||||
|
||||
[](https://gitter.im/ChaiScript/ChaiScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](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);");
|
||||
}
|
||||
```
|
||||
|
||||
@ -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
12
samples/BUCK
Normal file
@ -0,0 +1,12 @@
|
||||
cxx_binary(
|
||||
name = 'example',
|
||||
srcs = [
|
||||
'example.cpp',
|
||||
],
|
||||
compiler_flags = [
|
||||
'-std=c++14',
|
||||
],
|
||||
deps = [
|
||||
'//:chaiscript',
|
||||
],
|
||||
)
|
||||
@ -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");
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
354
src/libfuzzer_client.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
33
src/main.cpp
33
src/main.cpp
@ -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
289
src/sha3.cpp
Normal 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
81
src/sha3.h
Normal 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;
|
||||
};
|
||||
@ -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; }
|
||||
|
||||
@ -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 } );
|
||||
|
||||
@ -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(); } );
|
||||
|
||||
@ -1 +1 @@
|
||||
assert_throws("Illegal const function assignment", fun() { clone = `-` } );
|
||||
assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { clone = `-` } );
|
||||
|
||||
@ -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 = `-`; } );
|
||||
|
||||
|
||||
@ -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) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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 } );
|
||||
|
||||
@ -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 } );
|
||||
|
||||
19562
unittests/catch.hpp
19562
unittests/catch.hpp
File diff suppressed because it is too large
Load Diff
@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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})
|
||||
|
||||
|
||||
|
||||
|
||||
2
unittests/eval_file_with_bom.chai
Normal file
2
unittests/eval_file_with_bom.chai
Normal file
@ -0,0 +1,2 @@
|
||||
eval_file("file_with_bom.inc")
|
||||
assert_true(alwaysTrue())
|
||||
@ -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")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
11
unittests/failed_deep_include.chai
Normal file
11
unittests/failed_deep_include.chai
Normal 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");
|
||||
}
|
||||
|
||||
|
||||
3
unittests/failed_deep_include.inc
Normal file
3
unittests/failed_deep_include.inc
Normal file
@ -0,0 +1,3 @@
|
||||
use("totally_missing_file.inc");
|
||||
|
||||
|
||||
3
unittests/file_with_bom.inc
Normal file
3
unittests/file_with_bom.inc
Normal file
@ -0,0 +1,3 @@
|
||||
def alwaysTrue() {
|
||||
return true
|
||||
}
|
||||
@ -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(); } );
|
||||
|
||||
@ -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 } } );
|
||||
|
||||
|
||||
53
unittests/fuzz_unit_test.inc
Normal file
53
unittests/fuzz_unit_test.inc
Normal 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.
BIN
unittests/fuzzy_tests-2017-07-20.tar.bz2
Normal file
BIN
unittests/fuzzy_tests-2017-07-20.tar.bz2
Normal file
Binary file not shown.
@ -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);
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
assert_throws("Illegal const function assignment", fun() { clone = `-` } );
|
||||
assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { clone = `-` } );
|
||||
|
||||
@ -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
5
unittests/json_14.chai
Normal 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
19
unittests/json_15.chai
Normal 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"])
|
||||
@ -1 +1,3 @@
|
||||
assert_equal(from_json("100"), 100)
|
||||
assert_equal(from_json("-100"), -100)
|
||||
assert_equal(to_json(4294967295), "4294967295")
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
assert_equal(from_json("[1,2,3]"), [1,2,3])
|
||||
assert_equal(from_json("[1,-2,3]"), [1,-2,3])
|
||||
|
||||
|
||||
@ -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) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
7
unittests/namespaces.chai
Normal file
7
unittests/namespaces.chai
Normal 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))
|
||||
9
unittests/namespaces_nested_copy.chai
Normal file
9
unittests/namespaces_nested_copy.chai
Normal 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)
|
||||
@ -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 } );
|
||||
|
||||
|
||||
|
||||
@ -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; });
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
12
unittests/ranged_for_2.chai
Normal file
12
unittests/ranged_for_2.chai
Normal 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]));
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
13
unittests/static_chaiscript.cpp
Normal file
13
unittests/static_chaiscript.cpp
Normal 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() {}
|
||||
@ -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@")
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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; })
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user