Compare commits

..

No commits in common. "develop" and "Test_Release" have entirely different histories.

403 changed files with 25423 additions and 49873 deletions

View File

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

View File

@ -1,53 +1,30 @@
compilers: compilers:
- name: "clang" - name: "clang"
version: "3.6" version: "3.5"
skip_packaging: true skip_packaging: true
cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DRUN_PERFORMANCE_TESTS:BOOL=ON cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON
collect_performance_results: true
- name: "clang" - name: "clang"
build_tag: "LibC++" build_tag: "LibC++"
version: "3.6" version: "3.5"
skip_packaging: true skip_packaging: true
cmake_extra_flags: -DUSE_LIBCXX:BOOL=ON -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON cmake_extra_flags: -DUSE_LIBCXX:BOOL=ON -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON
- name: "clang" - name: "clang"
build_tag: AddressSanitizer build_tag: AddressSanitizer
version: "3.6" version: "3.6"
skip_packaging: true skip_packaging: true
cmake_extra_flags: -DRUN_FUZZY_TESTS:BOOL=TRUE -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_ADDRESS_SANITIZER:BOOL=ON cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_ADDRESS_SANITIZER:BOOL=ON
- name: "clang" - name: "clang"
build_tag: ThreadSanitizer build_tag: ThreadSanitizer
version: "3.6" version: "3.6"
skip_packaging: true skip_packaging: true
cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_THREAD_SANITIZER:BOOL=ON cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DENABLE_THREAD_SANITIZER:BOOL=ON
- name: "clang"
version: "3.7"
skip_packaging: true
cmake_extra_flags: -DUSE_LIBCXX:BOOL=OFF -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DRUN_PERFORMANCE_TESTS:BOOL=ON
collect_performance_results: true
- name: "clang"
build_tag: "LibC++"
version: "3.7"
skip_packaging: true
cmake_extra_flags: -DUSE_LIBCXX:BOOL=ON -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON
- name: "gcc" - name: "gcc"
version: "4.9" version: "4.8"
cmake_extra_flags: -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DBUILD_TESTING:BOOL=ON -DRUN_PERFORMANCE_TESTS:BOOL=ON cmake_extra_flags: -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DBUILD_TESTING:BOOL=ON
collect_performance_results: true
- name: "gcc" - name: "gcc"
version: "4.9" version: "4.6"
skip_packaging: true skip_packaging: true
build_tag: "NoThreads" cmake_extra_flags: -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON
cmake_extra_flags: -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DBUILD_TESTING:BOOL=ON -DRUN_PERFORMANCE_TESTS:BOOL=ON -DMULTITHREAD_SUPPORT_ENABLED:BOOL=OFF
collect_performance_results: true
- name: "gcc"
version: "5"
skip_packaging: true
cmake_extra_flags: -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DRUN_PERFORMANCE_TESTS:BOOL=ON
collect_performance_results: true
- name: cppcheck - name: cppcheck
compiler_extra_flags: --enable=all -I include --inline-suppr -Umax --suppress="*:unittests/catch.hpp" --force --suppress="unusedFunction:*" compiler_extra_flags: --enable=all -I include --inline-suppr -Umax --suppress="*:cmake*" --suppress="*:unittests/catch.hpp" --force
- name: custom_check
commands:
- ./contrib/check_for_tabs.rb
- ./contrib/check_for_todos.rb

View File

@ -2,4 +2,8 @@ compilers:
- name: clang - name: clang
cmake_extra_flags: -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DCOMMIT_SHA=$COMMIT_SHA cmake_extra_flags: -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DCOMMIT_SHA=$COMMIT_SHA
build_package_generator: TBZ2 build_package_generator: TBZ2
- name: clang
build_type: Debug
cmake_extra_flags: -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DCOMMIT_SHA=$COMMIT_SHA
skip_packaging: true

View File

@ -11,11 +11,10 @@ compilers:
compiler_extra_flags: /analyze compiler_extra_flags: /analyze
skip_packaging: true skip_packaging: true
- name: Visual Studio - name: Visual Studio
version: 14 version: 12
build_type: Debug cmake_extra_flags: -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DCOMMIT_SHA=%COMMIT_SHA%
- name: Visual Studio
version: 12
architecture: Win64 architecture: Win64
cmake_extra_flags: -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DCOMMIT_SHA=%COMMIT_SHA% cmake_extra_flags: -DBUILD_SAMPLES:BOOL=ON -DBUILD_PACKAGE:BOOL=ON -DBUILD_TESTING:BOOL=ON -DCOMMIT_SHA=%COMMIT_SHA%
compiler_extra_flags: /analyze
skip_packaging: true

View File

@ -1,41 +0,0 @@
# Contributing to ChaiScript
Thank you for contributing!
# Pull Requests
Please follow the existing style in the code you are patching.
- two space indent
- no tabs EVER
- match the existing indentation level
All ChaiScript commits are run through a large set of builds and analysis on all supported platforms. Those results are posted on the
[build dashboard](http://chaiscript.com/ChaiScript-BuildResults/index.html). No PR will be accepted until all tests pass.
The build system has full integration with GitHub and you will be notified automatically if all tests have passed.
# Issues
Please do not post a "chaiscript is too slow", "chaiscript compiles too slowly", or "chaiscript needs more documentation" issue
without first reading the following notes.
## ChaiScript is Too Slow
We are actively working on constently improving the runtime performance of ChaiScript. With the performance being
[monitored with each commit](http://chaiscript.com/ChaiScript-BuildResults/performance.html).
If you feel you *must* post an issue about performance, please post a complete example that illustrates the exact case you
feel should be better optimized.
Any issue request regarding performance without a complete example of the issue experienced will be closed.
## ChaiScript Compiles Too Slowly
This is also something we are actively working on. If you need highly optimized build times, please see [this discussion
on the discourse site](http://discourse.chaiscript.com/t/slow-build-times/94).
## ChaiScript Needs More Documentation
If you have a question that is not addressed in the [cheatsheet](https://github.com/ChaiScript/ChaiScript/blob/develop/cheatsheet.md)
please open an issue so we can get the Cheatsheet updated.

View File

@ -1,10 +0,0 @@
* Compiler Used:
* Operating System:
* Architecture (ARM/x86/32bit/64bit/etc):
### Expected Behavior
### Actual Behavior
### Minimal Example to Reproduce Behavior

View File

@ -1,8 +0,0 @@
Issue this pull request references: #
Changes proposed in this pull request
-
-
-

View File

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

View File

@ -1,163 +0,0 @@
name: CI
on:
push:
branches: [develop, main]
pull_request:
branches: [develop, main]
workflow_dispatch:
jobs:
linux:
name: Linux GCC ${{ matrix.build_type }} threads=${{ matrix.multithread }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
multithread: [ON, OFF]
steps:
- uses: actions/checkout@v4
- name: Install Ninja
run: sudo apt-get install -y ninja-build
- name: Configure
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DMULTITHREAD_SUPPORT_ENABLED=${{ matrix.multithread }}
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build --output-on-failure
macos:
name: macOS AppleClang ${{ matrix.build_type }} threads=${{ matrix.multithread }}
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
multithread: [ON, OFF]
steps:
- uses: actions/checkout@v4
- name: Install Ninja
run: brew install ninja
- name: Configure
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DMULTITHREAD_SUPPORT_ENABLED=${{ matrix.multithread }}
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build --output-on-failure
linux-sanitizers:
name: Linux GCC ASAN+UBSAN ${{ matrix.build_type }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
steps:
- uses: actions/checkout@v4
- name: Install Ninja
run: sudo apt-get install -y ninja-build
- name: Configure
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DENABLE_ADDRESS_SANITIZER=ON -DENABLE_UNDEFINED_SANITIZER=ON
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build --output-on-failure
macos-sanitizers:
name: macOS AppleClang ASAN+UBSAN ${{ matrix.build_type }}
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
steps:
- uses: actions/checkout@v4
- name: Install Ninja
run: brew install ninja
- name: Configure
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DENABLE_ADDRESS_SANITIZER=ON -DENABLE_UNDEFINED_SANITIZER=ON
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build --output-on-failure
windows:
name: Windows MSVC ${{ matrix.build_type }} threads=${{ matrix.multithread }}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
multithread: [ON, OFF]
steps:
- uses: actions/checkout@v4
- name: Configure
run: cmake -B build -DMULTITHREAD_SUPPORT_ENABLED=${{ matrix.multithread }}
- name: Build
run: cmake --build build --config ${{ matrix.build_type }} -j
- name: Test
run: ctest --test-dir build --output-on-failure -C ${{ matrix.build_type }}
linux-tsan:
name: Linux GCC TSAN ${{ matrix.build_type }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
steps:
- uses: actions/checkout@v4
- name: Install Ninja
run: sudo apt-get install -y ninja-build
- name: Configure
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DENABLE_THREAD_SANITIZER=ON -DMULTITHREAD_SUPPORT_ENABLED=ON
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build --output-on-failure
macos-tsan:
name: macOS AppleClang TSAN ${{ matrix.build_type }}
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
steps:
- uses: actions/checkout@v4
- name: Install Ninja
run: brew install ninja
- name: Configure
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DENABLE_THREAD_SANITIZER=ON -DMULTITHREAD_SUPPORT_ENABLED=ON
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build --output-on-failure

View File

@ -1,71 +0,0 @@
name: Emscripten
on:
push:
branches: [develop, main]
pull_request:
branches: [develop, main]
workflow_dispatch:
jobs:
emscripten:
name: Emscripten WebAssembly Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Emscripten
uses: mymindstorm/setup-emsdk@v14
- name: Configure
run: emcmake cmake -B build-em -S emscripten -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build-em -j
- name: Verify artifacts
run: |
test -f build-em/chaiscript.js
test -f build-em/chaiscript.wasm
test -f build-em/chaiscript.html
echo "All expected artifacts present"
ls -lh build-em/chaiscript.*
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: chaiscript-web
path: |
build-em/chaiscript.js
build-em/chaiscript.wasm
build-em/chaiscript.html
retention-days: 90
publish:
name: Publish WASM Release
needs: emscripten
if: github.ref == 'refs/heads/develop' && github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
name: chaiscript-web
path: artifacts
- name: Publish to wasm-latest release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Flatten: artifacts may contain build-em/ subdirectory
find artifacts -type f \( -name 'chaiscript.js' -o -name 'chaiscript.wasm' -o -name 'chaiscript.html' \) -exec cp {} . \;
# Delete existing release if present, then recreate
gh release delete wasm-latest --repo "$GITHUB_REPOSITORY" -y 2>/dev/null || true
gh release create wasm-latest \
--repo "$GITHUB_REPOSITORY" \
--title "ChaiScript WASM Build (latest)" \
--notes "Auto-published from develop branch. Built with Emscripten for browser use." \
--prerelease \
chaiscript.js chaiscript.wasm chaiscript.html

6
.gitignore vendored
View File

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

View File

@ -1,71 +1,33 @@
language: cpp language: cpp
compiler:
- gcc
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-5.0
- sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
packages:
- g++-7
- g++-8
coverity_scan:
project:
name: "ChaiScript/ChaiScript"
description: "Build submitted via Travis CI"
notification_email: jason@emptycrate.com
build_command_prepend: "cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug . "
build_command: "cmake --build . -- -j2"
branch_pattern: coverity_scan
matrix:
include:
- os: linux
sudo: false
env: GCC_VER="7"
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="7" CPPCHECK=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE"
compiler: gcc
- os: linux
sudo: false
env: GCC_VER="8" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE"
compiler: gcc
#- os: osx
# compiler: clang
# osx_image: xcode10
# env: CLANG_VER="5.0"
#- os: osx
# compiler: clang
# osx_image: xcode10
# env: CLANG_VER="5.0" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1
env: env:
matrix:
- GCC_VER="4.6"
- GCC_VER="4.8"
global: global:
- secure: eiaR6pXiiEpyB8+LLQ1NvZdl0Yylru1BLy9lMoHl+IpUNGGQGywmW/2WAn77rFfmR1OPA2qWQLfgPwgK0HxUA9HHlot9tre5QhiN2Lw8NOT8tCZ6tTm2+QntDBjBGJyal/knRvQkn/6qs6GxlXRerz4ArnnuPL1vESt3zwB0YtU= - secure: eiaR6pXiiEpyB8+LLQ1NvZdl0Yylru1BLy9lMoHl+IpUNGGQGywmW/2WAn77rFfmR1OPA2qWQLfgPwgK0HxUA9HHlot9tre5QhiN2Lw8NOT8tCZ6tTm2+QntDBjBGJyal/knRvQkn/6qs6GxlXRerz4ArnnuPL1vESt3zwB0YtU=
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
- secure: "LfolGjP8tWm3yAwthfu3yp8Zn40aueFae580UUR34gusG87cyglq2tQDtxdy+63gWEeNfArbv9n5rZv+bDW3ggHyPjuCKKc1PlZAy07lfXUXf1uz+SFhNvNoYTn3mQG3VZ08o116p4Le2p8yqu4bylJ8wckEq7PrTwvSGVQWTWM=" - secure: "LfolGjP8tWm3yAwthfu3yp8Zn40aueFae580UUR34gusG87cyglq2tQDtxdy+63gWEeNfArbv9n5rZv+bDW3ggHyPjuCKKc1PlZAy07lfXUXf1uz+SFhNvNoYTn3mQG3VZ08o116p4Le2p8yqu4bylJ8wckEq7PrTwvSGVQWTWM="
before_install: before_install:
- if [ "${GCC_VER}" != "" ]; then export CXX="g++-$GCC_VER" CC="gcc-$GCC_VER" GCOV="gcov-$GCC_VER" ; fi - export CXX="g++-$GCC_VER" CC="gcc-$GCC_VER" GCOV="gcov-$GCC_VER"
#- if [ "${CLANG_VER}" != "" ]; then export CXX="clang++-$CLANG_VER"; fi - if [ "$GCC_VER" = "4.8" ]; then export COVERAGE=1 CPPCHECK=1; fi
- pip install --user cpp-coveralls - if [ ${COVERAGE} = 1 ]; then export FUZZY_CMD="-D RUN_FUZZY_TESTS:BOOL=TRUE"; fi
- sudo pip install cpp-coveralls
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
- sudo apt-get update
- sudo apt-get install -qq g++-$GCC_VER
script: script:
- cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $CMAKE_OPTIONS . - if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $FUZZY_CMD . ; fi
- cmake --build . -- -j2 - if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then make -j2 ; fi
- if [ "${BUILD_ONLY}" != "1" ]; then ctest; fi - make test
- if [ "${COVERAGE}" = "1" ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; 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: after_script:
# - if [ ${CPPCHECK} = 1 ]; then contrib/codeanalysis/runcppcheck.sh ; fi - if [ ${CPPCHECK} = 1 ]; then contrib/codeanalysis/runcppcheck.sh ; fi
notifications: notifications:
@ -80,3 +42,15 @@ notifications:
on_success: change # options: [always|never|change] default: always on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always
on_start: false # default: false on_start: false # default: false
addons:
coverity_scan:
project:
name: "ChaiScript/ChaiScript"
description: "Build submitted via Travis CI"
notification_email: jason@emptycrate.com
build_command_prepend: "cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug . "
build_command: "cmake --build . -- -j2"
branch_pattern: coverity_scan

11
BUCK
View File

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

View File

@ -1,23 +1,25 @@
cmake_minimum_required(VERSION 3.12) cmake_minimum_required(VERSION 2.8)
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.1")
cmake_policy(SET CMP0054 NEW) cmake_policy(SET CMP0054 NEW)
endif()
set(CMAKE_CXX_STANDARD 20) IF(BIICODE)
set(CMAKE_CXX_STANDARD_REQUIRED ON) INIT_BIICODE_BLOCK()
ADD_BIICODE_TARGETS()
ELSE()
# Your regular CMakeLists configuration here
# required since cmake 3.4 at least for libc++
set(CMAKE_ENABLE_EXPORTS ON)
project(chaiscript) project(chaiscript)
option(MULTITHREAD_SUPPORT_ENABLED "Multithreaded Support Enabled" TRUE) option(MULTITHREAD_SUPPORT_ENABLED "Multithreaded Support Enabled" TRUE)
option(DYNLOAD_ENABLED "Dynamic Loading Support Enabled" TRUE)
option(BUILD_MODULES "Build Extra Modules (stl)" TRUE) option(BUILD_MODULES "Build Extra Modules (stl)" TRUE)
option(BUILD_SAMPLES "Build Samples Folder" FALSE) option(BUILD_SAMPLES "Build Samples Folder" FALSE)
option(RUN_FUZZY_TESTS "Run tests generated by AFL" 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(USE_STD_MAKE_SHARED "Use std::make_shared instead of chaiscript::make_shared" FALSE)
option(RUN_PERFORMANCE_TESTS "Run Performance Tests" FALSE)
mark_as_advanced(USE_STD_MAKE_SHARED) mark_as_advanced(USE_STD_MAKE_SHARED)
@ -34,7 +36,7 @@ if(CMAKE_COMPILER_IS_GNUCC)
endif() endif()
endif() endif()
if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
option(ENABLE_THREAD_SANITIZER "Enable thread sanitizer testing in gcc/clang" FALSE) option(ENABLE_THREAD_SANITIZER "Enable thread sanitizer testing in gcc/clang" FALSE)
if(ENABLE_THREAD_SANITIZER) if(ENABLE_THREAD_SANITIZER)
add_definitions(-fsanitize=thread -g) add_definitions(-fsanitize=thread -g)
@ -45,14 +47,12 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${
if(ENABLE_ADDRESS_SANITIZER) if(ENABLE_ADDRESS_SANITIZER)
add_definitions(-fsanitize=address -g) add_definitions(-fsanitize=address -g)
set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=address") set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=address")
option(BUILD_LIBFUZZ_TESTER "Build libfuzzer tool" FALSE)
endif() endif()
option(ENABLE_MEMORY_SANITIZER "Enable memory sanitizer testing in gcc/clang" FALSE) option(ENABLE_MEMORY_SANITIZER "Enable memory sanitizer testing in gcc/clang" FALSE)
if(ENABLE_MEMORY_SANITIZER) if(ENABLE_MEMORY_SANITIZER)
add_definitions(-fsanitize=memory -fsanitize-memory-track-origins -g) add_definitions(-fsanitize=memory -g)
set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=memory -fsanitize-memory-track-origins ") set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=memory")
endif() endif()
option(ENABLE_UNDEFINED_SANITIZER "Enable undefined behavior sanitizer testing in gcc/clang" FALSE) option(ENABLE_UNDEFINED_SANITIZER "Enable undefined behavior sanitizer testing in gcc/clang" FALSE)
@ -62,9 +62,10 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${
endif() endif()
option(ENABLE_LTO "Enable Link Time Optimization" FALSE) option(ENABLE_LTO "Enable Link Time Optimization" FALSE)
if (ENABLE_LTO) if (ENABLE_LTO)
check_ipo_supported() add_definitions(-flto)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) set(LINKER_FLAGS "${LINKER_FLAGS} -flto")
endif() endif()
option(GPROF_OUTPUT "Generate profile data" FALSE) option(GPROF_OUTPUT "Generate profile data" FALSE)
@ -87,16 +88,6 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${
endif() endif()
elseif(MSVC)
option(ENABLE_ADDRESS_SANITIZER "Enable address sanitizer testing in MSVC" FALSE)
if(ENABLE_ADDRESS_SANITIZER)
add_compile_options(/fsanitize=address)
# ASAN is incompatible with /RTC (runtime error checks) and incremental linking
string(REGEX REPLACE "/RTC[^ ]*" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
string(REGEX REPLACE "/RTC[^ ]*" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
add_link_options(/INCREMENTAL:NO)
endif()
endif() endif()
list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_BINARY_DIR}") list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_BINARY_DIR}")
@ -109,9 +100,9 @@ set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/license.txt")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/readme.md") 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_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/description.txt")
set(CPACK_PACKAGE_VERSION_MAJOR 7) set(CPACK_PACKAGE_VERSION_MAJOR 5)
set(CPACK_PACKAGE_VERSION_MINOR 0) set(CPACK_PACKAGE_VERSION_MINOR 7)
set(CPACK_PACKAGE_VERSION_PATCH 0) set(CPACK_PACKAGE_VERSION_PATCH 2)
set(CPACK_PACKAGE_EXECUTABLES "chai;ChaiScript Eval") set(CPACK_PACKAGE_EXECUTABLES "chai;ChaiScript Eval")
set(CPACK_PACKAGE_VENDOR "ChaiScript.com") set(CPACK_PACKAGE_VENDOR "ChaiScript.com")
@ -130,14 +121,17 @@ configure_file(Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile)
include(CTest) include(CTest)
include(CPack) include(CPack)
include(cmake/Catch.cmake)
include(cmake/CheckCXX11Features.cmake)
if(NOT MINGW) if(NOT MINGW)
find_library(READLINE_LIBRARY NAMES readline PATH /usr/lib /usr/local/lib /opt/local/lib) find_library(READLINE_LIBRARY NAMES readline PATH /usr/lib /usr/local/lib /opt/local/lib)
endif() endif()
if(UNIX AND NOT APPLE) if(HAS_CXX11_VARIADIC_TEMPLATES)
find_program(VALGRIND NAMES valgrind PATH /usr/bin /usr/local/bin) message(STATUS "Variadic Template support detected")
else()
message(SEND_ERROR "The selected compiler does not support the C++11 feature Variadic Templates.")
endif() endif()
enable_testing() enable_testing()
@ -154,18 +148,27 @@ else()
set(READLINE_FLAG) set(READLINE_FLAG)
endif() endif()
if(MSVC) if(CMAKE_COMPILER_IS_GNUCC)
add_definitions(/WX /W4 /w14545 /w34242 /w34254 /w34287 /w44263 /w44265 /w44296 /w44311 /w44826 /we4289 /w14546 /w14547 /w14549 /w14555 /w14619 /w14905 /w14906 /w14928) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
if(MSVC_VERSION STREQUAL "1800") if(GCC_VERSION VERSION_LESS 4.8)
# VS2013 doesn't have magic statics set(CPP11_FLAG "-std=c++0x")
add_definitions(/w44640)
else() else()
# enum warnings are too noisy on MSVC2013 set(CPP11_FLAG "-std=c++11")
add_definitions(/w34062) endif()
else()
set(CPP11_FLAG "-std=c++11")
endif() endif()
add_definitions(/bigobj /permissive- /utf-8) if(MSVC)
add_definitions(/W4)
# VS2013 doesn't have magic statics
if (MSVC_VERSION STREQUAL "1800")
add_definitions(/w44640)
endif()
add_definitions(/bigobj)
# Note on MSVC compiler flags. # Note on MSVC compiler flags.
# The code base selective disables warnings as necessary when the compiler is complaining too much # 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 # about something that is perfectly valid, or there is simply no technical way around it
@ -175,10 +178,10 @@ if(MSVC)
# how to workaround or fix the error. So I'm disabling it globally. # how to workaround or fix the error. So I'm disabling it globally.
add_definitions(/wd4503) add_definitions(/wd4503)
else() else()
add_definitions(-Werror -Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -Wno-noexcept-type -Wpedantic -Werror=return-type) add_definitions(-Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP11_FLAG})
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") 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 -Wno-unused-template -Wno-undef -Wno-double-promotion -Wno-switch-default -Wno-nrvo -Wno-shadow-uncaptured-local -Wno-unsafe-buffer-usage-in-libc-call -Wno-c++20-extensions -Wno-unknown-warning-option -Wno-poison-system-directories -Wno-c++20-compat -Wno-c++17-compat) add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-sign-conversion -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors)
else() else()
add_definitions(-Wnoexcept) add_definitions(-Wnoexcept)
endif() endif()
@ -189,12 +192,16 @@ else()
endif() endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
option(USE_LIBCXX "Use clang's libcxx" FALSE) option(USE_LIBCXX "Use clang's libcxx" TRUE)
if(USE_LIBCXX) if(USE_LIBCXX)
add_definitions(-stdlib=libc++) add_definitions(-stdlib=libc++)
set(LINKER_FLAGS "${LINKER_FLAGS} -stdlib=libc++") set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG} -stdlib=libc++")
else()
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG}")
endif() endif()
elseif(CMAKE_COMPILER_IS_GNUCC)
set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG}")
endif() endif()
# limitations in MinGW require us to make an optimized build # limitations in MinGW require us to make an optimized build
@ -206,7 +213,7 @@ endif()
include_directories(include) include_directories(include)
set(Chai_INCLUDES include/chaiscript/chaiscript.hpp include/chaiscript/chaiscript_threading.hpp include/chaiscript/dispatchkit/bad_boxed_cast.hpp include/chaiscript/dispatchkit/bind_first.hpp include/chaiscript/dispatchkit/bootstrap.hpp include/chaiscript/dispatchkit/bootstrap_stl.hpp include/chaiscript/dispatchkit/boxed_cast.hpp include/chaiscript/dispatchkit/boxed_cast_helper.hpp include/chaiscript/dispatchkit/boxed_number.hpp include/chaiscript/dispatchkit/boxed_value.hpp include/chaiscript/dispatchkit/dispatchkit.hpp include/chaiscript/dispatchkit/type_conversions.hpp include/chaiscript/dispatchkit/dynamic_object.hpp include/chaiscript/dispatchkit/exception_specification.hpp include/chaiscript/dispatchkit/function_call.hpp include/chaiscript/dispatchkit/function_call_detail.hpp include/chaiscript/dispatchkit/handle_return.hpp include/chaiscript/dispatchkit/operators.hpp include/chaiscript/dispatchkit/proxy_constructors.hpp include/chaiscript/dispatchkit/proxy_functions.hpp include/chaiscript/dispatchkit/proxy_functions_detail.hpp include/chaiscript/dispatchkit/register_function.hpp include/chaiscript/dispatchkit/type_info.hpp include/chaiscript/language/chaiscript_algebraic.hpp include/chaiscript/language/chaiscript_common.hpp include/chaiscript/language/chaiscript_engine.hpp include/chaiscript/language/chaiscript_eval.hpp include/chaiscript/language/chaiscript_parser.hpp include/chaiscript/language/chaiscript_prelude.hpp include/chaiscript/language/chaiscript_prelude_docs.hpp include/chaiscript/utility/utility.hpp include/chaiscript/utility/json.hpp include/chaiscript/utility/json_wrap.hpp) set(Chai_INCLUDES include/chaiscript/chaiscript.hpp include/chaiscript/chaiscript_threading.hpp include/chaiscript/dispatchkit/bad_boxed_cast.hpp include/chaiscript/dispatchkit/bind_first.hpp include/chaiscript/dispatchkit/bootstrap.hpp include/chaiscript/dispatchkit/bootstrap_stl.hpp include/chaiscript/dispatchkit/boxed_cast.hpp include/chaiscript/dispatchkit/boxed_cast_helper.hpp include/chaiscript/dispatchkit/boxed_number.hpp include/chaiscript/dispatchkit/boxed_value.hpp include/chaiscript/dispatchkit/dispatchkit.hpp include/chaiscript/dispatchkit/type_conversions.hpp include/chaiscript/dispatchkit/dynamic_object.hpp include/chaiscript/dispatchkit/exception_specification.hpp include/chaiscript/dispatchkit/function_call.hpp include/chaiscript/dispatchkit/function_call_detail.hpp include/chaiscript/dispatchkit/handle_return.hpp include/chaiscript/dispatchkit/operators.hpp include/chaiscript/dispatchkit/proxy_constructors.hpp include/chaiscript/dispatchkit/proxy_functions.hpp include/chaiscript/dispatchkit/proxy_functions_detail.hpp include/chaiscript/dispatchkit/register_function.hpp include/chaiscript/dispatchkit/type_info.hpp include/chaiscript/language/chaiscript_algebraic.hpp include/chaiscript/language/chaiscript_common.hpp include/chaiscript/language/chaiscript_engine.hpp include/chaiscript/language/chaiscript_eval.hpp include/chaiscript/language/chaiscript_parser.hpp include/chaiscript/language/chaiscript_prelude.chai include/chaiscript/language/chaiscript_prelude_docs.hpp include/chaiscript/utility/utility.hpp)
set_source_files_properties(${Chai_INCLUDES} PROPERTIES HEADER_FILE_ONLY TRUE) set_source_files_properties(${Chai_INCLUDES} PROPERTIES HEADER_FILE_ONLY TRUE)
@ -214,16 +221,10 @@ if(NOT MULTITHREAD_SUPPORT_ENABLED)
add_definitions(-DCHAISCRIPT_NO_THREADS) add_definitions(-DCHAISCRIPT_NO_THREADS)
endif() endif()
if(NOT DYNLOAD_ENABLED)
add_definitions(-DCHAISCRIPT_NO_DYNLOAD)
endif()
if(CMAKE_HOST_UNIX) if(CMAKE_HOST_UNIX)
if(DYNLOAD_ENABLED)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku") if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku")
list(APPEND LIBS "dl") list(APPEND LIBS "dl")
endif() endif()
endif()
if(MULTITHREAD_SUPPORT_ENABLED) if(MULTITHREAD_SUPPORT_ENABLED)
if(CMAKE_COMPILER_IS_GNUCC) if(CMAKE_COMPILER_IS_GNUCC)
@ -248,80 +249,70 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}")
add_library(stdlib STATIC static_libs/chaiscript_stdlib.cpp)
add_library(parser STATIC static_libs/chaiscript_parser.cpp)
add_library(chaiscript_stdlib-${CHAI_VERSION} MODULE src/chaiscript_stdlib_module.cpp) add_library(chaiscript_stdlib-${CHAI_VERSION} MODULE src/chaiscript_stdlib.cpp)
target_link_libraries(chaiscript_stdlib-${CHAI_VERSION} ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(chaiscript_stdlib-${CHAI_VERSION} ${LIBS} ${CMAKE_THREAD_LIBS_INIT})
set(CHAISCRIPT_LIBS stdlib parser)
add_executable(chai src/main.cpp ${Chai_INCLUDES}) add_executable(chai src/main.cpp ${Chai_INCLUDES})
target_link_libraries(chai ${LIBS} ${CHAISCRIPT_LIBS}) target_link_libraries(chai ${LIBS})
add_dependencies(chai chaiscript_stdlib-${CHAI_VERSION})
add_library(chaiscript INTERFACE)
target_include_directories(chaiscript INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
if(BUILD_SAMPLES) if(BUILD_SAMPLES)
add_executable(sanity_checks src/sanity_checks.cpp) add_executable(example samples/example.cpp)
target_link_libraries(sanity_checks ${LIBS}) target_link_libraries(example ${LIBS})
add_executable(test_num_exceptions samples/test_num_exceptions.cpp) add_executable(test_num_exceptions samples/test_num_exceptions.cpp)
target_link_libraries(test_num_exceptions ${LIBS} ${CHAISCRIPT_LIBS}) target_link_libraries(test_num_exceptions ${LIBS})
add_executable(memory_leak_test samples/memory_leak_test.cpp) add_executable(memory_leak_test samples/memory_leak_test.cpp)
target_link_libraries(memory_leak_test ${LIBS} ${CHAISCRIPT_LIBS}) target_link_libraries(memory_leak_test ${LIBS})
add_executable(inheritance samples/inheritance.cpp) add_executable(inheritance samples/inheritance.cpp)
target_link_libraries(inheritance ${LIBS} ${CHAISCRIPT_LIBS}) target_link_libraries(inheritance ${LIBS})
add_executable(factory samples/factory.cpp)
target_link_libraries(factory ${LIBS} ${CHAISCRIPT_LIBS})
add_executable(fun_call_performance samples/fun_call_performance.cpp) add_executable(fun_call_performance samples/fun_call_performance.cpp)
target_link_libraries(fun_call_performance ${LIBS} ${CHAISCRIPT_LIBS}) target_link_libraries(fun_call_performance ${LIBS})
endif() endif()
if(BUILD_MODULES) if(BUILD_MODULES)
add_library(test_module MODULE src/test_module.cpp)
target_link_libraries(test_module ${LIBS})
add_library(stl_extra MODULE src/stl_extra.cpp) add_library(stl_extra MODULE src/stl_extra.cpp)
target_link_libraries(stl_extra ${LIBS}) target_link_libraries(stl_extra ${LIBS})
set(MODULES stl_extra) set(MODULES stl_extra)
endif() 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) 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) list(SORT UNIT_TESTS)
if(NOT MULTITHREAD_SUPPORT_ENABLED)
list(REMOVE_ITEM UNIT_TESTS
async_engine_lifetime.chai
async_return_value.chai
future.chai
list_push_front.chai
move_async.chai
)
endif()
file(GLOB PERFORMANCE_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/performance_tests/ ${CMAKE_CURRENT_SOURCE_DIR}/performance_tests/*.chai)
list(SORT PERFORMANCE_TESTS)
if (RUN_FUZZY_TESTS) if (RUN_FUZZY_TESTS)
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/unittests") file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/unittests")
execute_process( execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2017-07-20.tar.bz2 COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2015-07-16.tar.bz2
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests
) )
file(GLOB FUZZY_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/MINIMIZED/*) file(GLOB FUZZY_CRASH_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/id*)
list(SORT FUZZY_TESTS) list(SORT FUZZY_CRASH_TESTS)
foreach(filename ${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})
message(STATUS "Adding test ${filename}") message(STATUS "Adding test ${filename}")
add_test(fuzz.${filename} chai "-e" "--exception" "--any-exception" ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzz_unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${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})
endforeach() endforeach()
set_property(TEST ${FUZZY_EXCEPTION_TESTS} set_property(TEST ${FUZZY_EXCEPTION_TESTS}
@ -335,6 +326,37 @@ endif()
if(BUILD_TESTING) if(BUILD_TESTING)
# Add catch tests macro
macro(ADD_CATCH_TESTS executable)
if (MSVC)
file(TO_NATIVE_PATH "${QT_LIBRARY_DIR}" QT_LIB_PATH)
set(NEWPATH "${QT_LIB_PATH};$ENV{PATH}")
else()
set(NEWPATH $ENV{PATH})
endif()
get_target_property(target_files ${executable} SOURCES)
foreach(source ${target_files})
if(NOT "${source}" MATCHES "/moc_.*cxx")
string(REGEX MATCH .*cpp source "${source}")
if(source)
file(READ "${source}" contents)
string(REGEX MATCHALL "TEST_CASE\\([ ]*\"[^\"]+\"" found_tests ${contents})
foreach(hit ${found_tests})
string(REGEX REPLACE "TEST_CASE\\([ ]*(\"[^\"]+\").*" "\\1" test_name ${hit})
add_test(${test_name} "${executable}" ${test_name})
set_tests_properties(${test_name} PROPERTIES TIMEOUT 660 ENVIRONMENT "PATH=${NEWPATH}")
endforeach()
endif()
endif()
endforeach()
endmacro()
option(UNIT_TEST_LIGHT "Unit tests light (expect module loading failures)" FALSE) option(UNIT_TEST_LIGHT "Unit tests light (expect module loading failures)" FALSE)
add_test(version_check chai -c "if(\"\\\${ version() };\\\${version_major()};\\\${version_minor()};\\\${version_patch()}\" != \"${CHAI_VERSION};${CPACK_PACKAGE_VERSION_MAJOR};${CPACK_PACKAGE_VERSION_MINOR};${CPACK_PACKAGE_VERSION_PATCH}\") { exit(-1) }") add_test(version_check chai -c "if(\"\\\${ version() };\\\${version_major()};\\\${version_minor()};\\\${version_patch()}\" != \"${CHAI_VERSION};${CPACK_PACKAGE_VERSION_MAJOR};${CPACK_PACKAGE_VERSION_MINOR};${CPACK_PACKAGE_VERSION_PATCH}\") { exit(-1) }")
@ -358,68 +380,37 @@ if(BUILD_TESTING)
) )
set(TESTS "")
foreach(filename ${UNIT_TESTS}) foreach(filename ${UNIT_TESTS})
message(STATUS "Adding unit test ${filename}") message(STATUS "Adding test ${filename}")
add_test(unit.${filename} chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/unit_test.inc ${CMAKE_CURRENT_SOURCE_DIR}/unittests/${filename}) add_test(${filename} chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/unit_test.inc ${CMAKE_CURRENT_SOURCE_DIR}/unittests/${filename})
list(APPEND TESTS unit.${filename})
endforeach() endforeach()
if(RUN_PERFORMANCE_TESTS) set_property(TEST ${UNIT_TESTS}
foreach(filename ${PERFORMANCE_TESTS})
message(STATUS "Adding performance test ${filename}")
add_test(NAME performance.${filename} COMMAND ${VALGRIND} --tool=callgrind --callgrind-out-file=callgrind.performance.${filename} $<TARGET_FILE:chai> ${CMAKE_CURRENT_SOURCE_DIR}/performance_tests/${filename})
list(APPEND TESTS performance.${filename})
endforeach()
add_executable(profile_cpp_calls_2 performance_tests/profile_cpp_calls_2.cpp)
target_link_libraries(profile_cpp_calls_2 ${LIBS})
add_test(NAME performance.profile_cpp_calls_2 COMMAND ${VALGRIND} --tool=callgrind --callgrind-out-file=callgrind.performance.profile_cpp_calls_2 $<TARGET_FILE:profile_cpp_calls_2>)
add_executable(profile_fun_wrappers performance_tests/profile_fun_wrappers.cpp)
target_link_libraries(profile_fun_wrappers ${LIBS})
add_test(NAME performance.profile_fun_wrappers COMMAND ${VALGRIND} --tool=callgrind --callgrind-out-file=callgrind.performance.profile_fun_wrappers $<TARGET_FILE:profile_fun_wrappers>)
endif()
set_property(TEST ${TESTS}
PROPERTY ENVIRONMENT PROPERTY ENVIRONMENT
"CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/" "CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/"
"CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/"
) )
if(NOT UNIT_TEST_LIGHT) if(NOT UNIT_TEST_LIGHT)
add_library(catch2 STATIC unittests/catch_amalgamated.cpp)
if(NOT MSVC)
target_compile_options(catch2 PRIVATE -Wno-conversion -Wno-noexcept -Wno-maybe-uninitialized)
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
target_compile_options(catch2 PUBLIC -Wno-unknown-warning-option -Wno-covered-switch-default -Wno-disabled-macro-expansion -Wno-unsafe-buffer-usage -Wno-unused-macros)
endif()
add_executable(compiled_tests unittests/compiled_tests.cpp) add_executable(compiled_tests unittests/compiled_tests.cpp)
target_link_libraries(compiled_tests catch2 ${LIBS} ${CHAISCRIPT_LIBS}) target_link_libraries(compiled_tests ${LIBS})
catch_discover_tests(compiled_tests TEST_PREFIX "compiled.") ADD_CATCH_TESTS(compiled_tests)
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) add_executable(boxed_cast_test unittests/boxed_cast_test.cpp)
target_link_libraries(boxed_cast_test ${LIBS}) target_link_libraries(boxed_cast_test ${LIBS})
add_test(NAME Boxed_Cast_Test COMMAND boxed_cast_test) add_test(NAME Boxed_Cast_Test COMMAND boxed_cast_test)
add_executable(type_info_test unittests/type_info_test.cpp) add_executable(type_info_test unittests/type_info_test.cpp)
target_link_libraries(type_info_test catch2 ${LIBS}) target_link_libraries(type_info_test ${LIBS})
add_test(NAME Type_Info_Test COMMAND type_info_test) add_test(NAME Type_Info_Test COMMAND type_info_test)
add_executable(c_linkage_test unittests/c_linkage_test.cpp) add_executable(c_linkage_test unittests/c_linkage_test.cpp)
target_link_libraries(c_linkage_test ${LIBS} ${CHAISCRIPT_LIBS}) target_link_libraries(c_linkage_test ${LIBS})
add_test(NAME C_Linkage_Test COMMAND c_linkage_test) add_test(NAME C_Linkage_Test COMMAND c_linkage_test)
add_executable(integer_literal_test unittests/integer_literal_test.cpp) add_executable(integer_literal_test unittests/integer_literal_test.cpp)
target_link_libraries(integer_literal_test ${LIBS} ${CHAISCRIPT_LIBS}) target_link_libraries(integer_literal_test ${LIBS})
add_test(NAME Integer_Literal_Test COMMAND integer_literal_test) add_test(NAME Integer_Literal_Test COMMAND integer_literal_test)
if(MULTITHREAD_SUPPORT_ENABLED) if(MULTITHREAD_SUPPORT_ENABLED)
@ -431,11 +422,10 @@ if(BUILD_TESTING)
"CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/" "CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/"
"CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/"
) )
add_executable(async_engine_lifetime_test unittests/async_engine_lifetime_test.cpp)
target_link_libraries(async_engine_lifetime_test ${LIBS})
add_test(NAME Async_Engine_Lifetime_Test COMMAND async_engine_lifetime_test)
endif() endif()
add_executable(multifile_test add_executable(multifile_test
unittests/multifile_test_main.cpp unittests/multifile_test_main.cpp
unittests/multifile_test_chai.cpp unittests/multifile_test_chai.cpp
@ -444,35 +434,14 @@ if(BUILD_TESTING)
target_link_libraries(multifile_test ${LIBS}) target_link_libraries(multifile_test ${LIBS})
add_test(NAME MultiFile_Test COMMAND multifile_test) add_test(NAME MultiFile_Test COMMAND multifile_test)
add_executable(emscripten_eval_test unittests/emscripten_eval_test.cpp) add_library(test_module MODULE src/test_module.cpp)
target_link_libraries(emscripten_eval_test ${LIBS}) target_link_libraries(test_module ${LIBS})
add_test(NAME Emscripten_Eval_Test COMMAND emscripten_eval_test)
add_executable(emscripten_exception_test unittests/emscripten_exception_test.cpp) install(TARGETS test_module RUNTIME DESTINATION bin LIBRARY DESTINATION lib/chaiscript)
target_link_libraries(emscripten_exception_test ${LIBS})
add_test(NAME Emscripten_Exception_Test COMMAND emscripten_exception_test)
add_executable(emscripten_state_test unittests/emscripten_state_test.cpp)
target_link_libraries(emscripten_state_test ${LIBS})
add_test(NAME Emscripten_State_Test COMMAND emscripten_state_test)
add_executable(threading_config_test unittests/threading_config_test.cpp)
target_link_libraries(threading_config_test ${LIBS})
add_test(NAME Threading_Config_Test COMMAND threading_config_test)
install(TARGETS test_module RUNTIME DESTINATION bin LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/chaiscript")
endif() endif()
endif() endif()
install(TARGETS chai chaiscript_stdlib-${CHAI_VERSION} ${MODULES} RUNTIME DESTINATION bin LIBRARY DESTINATION lib/chaiscript)
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 "${CMAKE_INSTALL_LIBDIR}/chaiscript")
install(DIRECTORY include/chaiscript DESTINATION include install(DIRECTORY include/chaiscript DESTINATION include
PATTERN "*.hpp" PATTERN "*.hpp"
@ -493,6 +462,8 @@ install(DIRECTORY samples DESTINATION share/chaiscript
configure_file(contrib/pkgconfig/chaiscript.pc.in lib/pkgconfig/chaiscript.pc @ONLY) configure_file(contrib/pkgconfig/chaiscript.pc.in lib/pkgconfig/chaiscript.pc @ONLY)
install(FILES "${chaiscript_BINARY_DIR}/lib/pkgconfig/chaiscript.pc" install(FILES "${chaiscript_BINARY_DIR}/lib/pkgconfig/chaiscript.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") DESTINATION lib/pkgconfig)
ENDIF()

View File

@ -1,28 +0,0 @@
# Introduction
This document outlines the principles that drive the development of ChaiScript. ChaiScript does not intent to be the perfect tool for *every* situation, but it does intend to be a good general purpose tool for *most* situations.
# Goals
1. Trivially easy to integrate with C++ projects
2. 0 external depenencies
3. "Perfect" integration with C++
* Direct mapping between ChaiScript objects and C++ objects
* Direct mapping between ChaiScript functions and C++ functions
* Direct mapping between ChaiScript exceptions and C++ exceptions
3. Never surprise the C++ developer
* Object lifetimes managed by the stack
* Familiar syntax to C++ developers
4. Perform "well enough" to not get in the way
# Alternatives
## Sol2
If you are looking for the fastest performing scripting language and don't mind Lua, you might want to consider [sol2](https://github.com/ThePhD/sol2).
## SWIG
If you are looking for the most flexible solution to be able to support multiple target languages, consider [SWIG](http://swig.org)

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,103 @@
# Checks for C++11 features
# CXX11_FEATURE_LIST - a list containing all supported features
# HAS_CXX11_AUTO - auto keyword
# HAS_CXX11_NULLPTR - nullptr
# HAS_CXX11_LAMBDA - lambdas
# HAS_CXX11_STATIC_ASSERT - static_assert()
# HAS_CXX11_RVALUE_REFERENCES - rvalue references
# HAS_CXX11_DECLTYPE - decltype keyword
# HAS_CXX11_CSTDINT_H - cstdint header
# HAS_CXX11_LONG_LONG - long long signed & unsigned types
# HAS_CXX11_VARIADIC_TEMPLATES - variadic templates
# HAS_CXX11_CONSTEXPR - constexpr keyword
# HAS_CXX11_SIZEOF_MEMBER - sizeof() non-static members
# HAS_CXX11_FUNC - __func__ preprocessor constant
#
# Original script by Rolf Eike Beer
# Modifications by Andreas Weis
#
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.3)
SET(CHECK_CXX11_OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
IF(CMAKE_COMPILER_IS_GNUCXX)
SET(CMAKE_CXX_FLAGS "-std=c++0x")
endif()
MACRO(CXX11_CHECK_FEATURE FEATURE_NAME FEATURE_NUMBER RESULT_VAR)
IF (NOT DEFINED ${RESULT_VAR})
SET(_bindir "${CMAKE_CURRENT_BINARY_DIR}/cxx11/cxx11_${FEATURE_NAME}")
IF (${FEATURE_NUMBER})
SET(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/c++11-test-${FEATURE_NAME}-N${FEATURE_NUMBER})
SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})")
ELSE (${FEATURE_NUMBER})
SET(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/c++11-test-${FEATURE_NAME})
SET(_LOG_NAME "\"${FEATURE_NAME}\"")
ENDIF (${FEATURE_NUMBER})
MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME}")
SET(_SRCFILE "${_SRCFILE_BASE}.cpp")
SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.cpp")
SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.cpp")
IF (CROSS_COMPILING)
try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}")
IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}")
ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
ELSE (CROSS_COMPILING)
try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
"${_bindir}" "${_SRCFILE}")
IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
SET(${RESULT_VAR} TRUE)
ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
SET(${RESULT_VAR} FALSE)
ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
"${_bindir}_fail" "${_SRCFILE_FAIL}")
IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
SET(${RESULT_VAR} TRUE)
ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
SET(${RESULT_VAR} FALSE)
ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
ENDIF (CROSS_COMPILING)
IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}")
IF (_TMP_RESULT)
SET(${RESULT_VAR} FALSE)
ELSE (_TMP_RESULT)
SET(${RESULT_VAR} TRUE)
ENDIF (_TMP_RESULT)
ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
IF (${RESULT_VAR})
MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME} -- works")
LIST(APPEND CXX11_FEATURE_LIST ${RESULT_VAR})
ELSE (${RESULT_VAR})
MESSAGE(STATUS "Checking C++11 support for ${_LOG_NAME} -- not supported")
ENDIF (${RESULT_VAR})
SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "C++11 support for ${_LOG_NAME}")
ENDIF (NOT DEFINED ${RESULT_VAR})
ENDMACRO(CXX11_CHECK_FEATURE)
CXX11_CHECK_FEATURE("auto" 2546 HAS_CXX11_AUTO)
CXX11_CHECK_FEATURE("nullptr" 2431 HAS_CXX11_NULLPTR)
CXX11_CHECK_FEATURE("lambda" 2927 HAS_CXX11_LAMBDA)
CXX11_CHECK_FEATURE("static_assert" 1720 HAS_CXX11_STATIC_ASSERT)
CXX11_CHECK_FEATURE("rvalue_references" 2118 HAS_CXX11_RVALUE_REFERENCES)
CXX11_CHECK_FEATURE("decltype" 2343 HAS_CXX11_DECLTYPE)
CXX11_CHECK_FEATURE("cstdint" "" HAS_CXX11_CSTDINT_H)
CXX11_CHECK_FEATURE("long_long" 1811 HAS_CXX11_LONG_LONG)
CXX11_CHECK_FEATURE("variadic_templates" 2555 HAS_CXX11_VARIADIC_TEMPLATES)
CXX11_CHECK_FEATURE("constexpr" 2235 HAS_CXX11_CONSTEXPR)
CXX11_CHECK_FEATURE("sizeof_member" 2253 HAS_CXX11_SIZEOF_MEMBER)
CXX11_CHECK_FEATURE("__func__" 2340 HAS_CXX11_FUNC)
SET(CXX11_FEATURE_LIST ${CXX11_FEATURE_LIST} CACHE STRING "C++11 feature support list")
MARK_AS_ADVANCED(FORCE CXX11_FEATURE_LIST)
SET(CMAKE_CXX_FLAGS ${CHECK_CXX11_OLD_CMAKE_CXX_FLAGS})
UNSET(CHECK_CXX11_OLD_CMAKE_CXX_FLAGS)

View File

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

View File

@ -0,0 +1,8 @@
#include <cstring>
int main()
{
if (!__func__) { return 1; }
if(std::strlen(__func__) <= 0) { return 1; }
return 0;
}

View File

@ -0,0 +1,12 @@
int main()
{
auto i = 5;
auto f = 3.14159f;
auto d = 3.14159;
bool ret = (
(sizeof(f) < sizeof(d)) &&
(sizeof(i) == sizeof(int))
);
return ret ? 0 : 1;
}

View File

@ -0,0 +1,19 @@
constexpr int square(int x)
{
return x*x;
}
constexpr int the_answer()
{
return 42;
}
int main()
{
int test_arr[square(3)];
bool ret = (
(square(the_answer()) == 1764) &&
(sizeof(test_arr)/sizeof(test_arr[0]) == 9)
);
return ret ? 0 : 1;
}

View File

@ -0,0 +1,10 @@
#include <cstdint>
int main()
{
bool test =
(sizeof(int8_t) == 1) &&
(sizeof(int16_t) == 2) &&
(sizeof(int32_t) == 4) &&
(sizeof(int64_t) == 8);
return test ? 0 : 1;
}

View File

@ -0,0 +1,11 @@
bool check_size(int i)
{
return sizeof(int) == sizeof(decltype(i));
}
int main()
{
bool ret = check_size(42);
return ret ? 0 : 1;
}

View File

@ -0,0 +1,5 @@
int main()
{
int ret = 0;
return ([&ret]() -> int { return ret; })();
}

View File

@ -0,0 +1,7 @@
int main(void)
{
long long l;
unsigned long long ul;
return ((sizeof(l) >= 8) && (sizeof(ul) >= 8)) ? 0 : 1;
}

View File

@ -0,0 +1,5 @@
int main()
{
int* test = nullptr;
return test ? 1 : 0;
}

View File

@ -0,0 +1,5 @@
int main()
{
int i = nullptr;
return 1;
}

View File

@ -0,0 +1,15 @@
int foo(int& lvalue)
{
return 123;
}
int foo(int&& rvalue)
{
return 321;
}
int main()
{
int i = 42;
return ((foo(i) == 123) && (foo(42) == 321)) ? 0 : 1;
}

View File

@ -0,0 +1,14 @@
struct foo {
char bar;
int baz;
};
int main(void)
{
bool ret = (
(sizeof(foo::bar) == 1) &&
(sizeof(foo::baz) >= sizeof(foo::bar)) &&
(sizeof(foo) >= sizeof(foo::bar)+sizeof(foo::baz))
);
return ret ? 0 : 1;
}

View File

@ -0,0 +1,5 @@
int main()
{
static_assert(0 < 1, "your ordering of integers is screwed");
return 0;
}

View File

@ -0,0 +1,5 @@
int main()
{
static_assert(1 < 0, "this should fail");
return 0;
}

View File

@ -0,0 +1,23 @@
int Accumulate()
{
return 0;
}
template<typename T, typename... Ts>
int Accumulate(T v, Ts... vs)
{
return v + Accumulate(vs...);
}
template<int... Is>
int CountElements()
{
return sizeof...(Is);
}
int main()
{
int acc = Accumulate(1, 2, 3, 4, -5);
int count = CountElements<1,2,3,4,5>();
return ((acc == 5) && (count == 5)) ? 0 : 1;
}

View File

@ -1,11 +0,0 @@
#!/usr/bin/env ruby
require 'json'
`grep -rPIHn '\t' src/* include/* samples/*`.lines { |line|
if /(?<filename>.+(hpp|cpp|chai)):(?<linenumber>[0-9]+):(?<restofline>.+)/ =~ line
puts(JSON.dump({:line => linenumber, :filename => filename, :tool => "tab_checker", :message => "Source Code Line Contains Tabs", :messagetype => "warning"}))
end
}

View File

@ -1,11 +0,0 @@
#!/usr/bin/env ruby
require 'json'
`grep -rPIHni 'todo' src/* include/* samples/*`.lines { |line|
if /(?<filename>.+(hpp|cpp|chai)):(?<linenumber>[0-9]+):(?<restofline>.+)/ =~ line
puts(JSON.dump({:line => linenumber, :filename => filename, :tool => "todo_checker", :message => "todo: #{restofline.strip}", :messagetype => "info"}))
end
}

View File

@ -1,61 +0,0 @@
# My dict
for="for"
while="while"
def="def"
fun="fun"
if="if"
else="else"
and="&&"
or="||"
auto="auto"
var="var"
begin_block="{"
end_block="}"
empty_vec="[]"
string="string"
vector="Vector"
map="Map"
return="return"
break="break"
true="true"
false="false"
class="class"
attr="attr"
var="var"
global="global"
empty_lambda=" fun(){} "
empty_fun=" def empty_fun() {} "
continue="continue"
float=" 1.1f "
double=" 2.2 "
long_double=" 2.2ll "
unsigned=" 3u "
unsigned_long=" 4ul "
unsigned_long_long=" 4ull "
long_long=" 5ll "
attr="attr"
reference_del="auto &"
int8=" int8_t(1) "
int16=" int16_t(2) "
int32=" int32_t(3) "
int64=" int64_t(4) "
uint8=" uint8_t(1) "
uint16=" uint16_t(2) "
uint32=" uint32_t(3) "
uint64=" uint64_t(4) "
int8t="int8_t"
int16t="int16_t"
int32t="int32_t"
int64t="int64_t"
uint8t="uint8_t"
uint16t="uint16_t"
uint32t="uint32_t"
uint64t="uint64_t"

View File

@ -1,17 +0,0 @@
Command line used to find this crash:
../../Downloads/afl-1.80b/afl-fuzz -i- -o findings -x chaiscript.dict -- ../a.out unit_test.inc @@
If you can't reproduce a bug outside of afl-fuzz, be sure to set the same
memory limit. The limit used for this fuzzing session was 50.0 MB.
Need a tool to minimize test cases before investigating the crashes or sending
them to a vendor? Check out the afl-tmin that comes with the fuzzer!
Found any cool bugs in open-source tools using afl-fuzz? If yes, please drop
me a mail at <lcamtuf@coredump.cx> once the issues are fixed - I'd love to
add your finds to the gallery at:
http://lcamtuf.coredump.cx/afl/
Thanks :-)

View File

@ -4,7 +4,7 @@ pushd ..
wget http://sourceforge.net/projects/cppcheck/files/cppcheck/1.66/cppcheck-1.66.tar.bz2 wget http://sourceforge.net/projects/cppcheck/files/cppcheck/1.66/cppcheck-1.66.tar.bz2
tar -xvf cppcheck-1.66.tar.bz2 tar -xvf cppcheck-1.66.tar.bz2
cd cppcheck-1.66 cd cppcheck-1.66
make -j2 CXX=g++-4.8 make -j2
popd popd
../cppcheck-1.66/cppcheck --enable=all -I include --inline-suppr --suppress=missingIncludeSystem --std=c++11 --platform=unix64 src/main.cpp src/chai*.cpp --template ' - __{severity}__: [{file}:{line}](../blob/TRAVIS_COMMIT/{file}#L{line}) {message} ({id})' 2>output ../cppcheck-1.66/cppcheck --enable=all -I include --inline-suppr --suppress=missingIncludeSystem --std=c++11 --platform=unix64 src/main.cpp src/chai*.cpp --template ' - __{severity}__: [{file}:{line}](../blob/TRAVIS_COMMIT/{file}#L{line}) {message} ({id})' 2>output
sed -i "s/TRAVIS_COMMIT/${TRAVIS_COMMIT}/g" output sed -i "s/TRAVIS_COMMIT/${TRAVIS_COMMIT}/g" output

View File

@ -0,0 +1,93 @@
# [PackageDev] target_format: plist, ext: tmLanguage
---
comment: 'ChaiScript Syntax: version 2.0'
fileTypes: [chai]
firstLineMatch: ^#!/usr/bin/env node
foldingStartMarker: ^.*\bdef\s*(\w+\s*)?\([^\)]*\)(\s*\{[^\}]*)?\s*$
foldingStopMarker: ^\s*\}
keyEquivalent: ^~J
name: ChaiScript
patterns:
- {comment: chaiscript shebang, match: ^#, name: comment.line.chai}
- {match: '\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?))\b', name: constant.numeric.chai}
- begin: ''''
beginCaptures:
'0': {name: punctuation.definition.string.begin.chai}
end: ''''
endCaptures:
'0': {name: punctuation.definition.string.end.chai}
name: string.quoted.single.chai
patterns:
- {match: '\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)', name: constant.character.escape.chai}
- begin: '"'
beginCaptures:
'0': {name: punctuation.definition.string.begin.chai}
end: '"'
endCaptures:
'0': {name: punctuation.definition.string.end.chai}
name: string.quoted.double.chai
patterns:
- {match: '\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]|37[0-7]?|[4-7][0-7]?|.)', name: constant.character.escape.chai}
- begin: /\*\*(?!/)
captures:
'0': {name: punctuation.definition.comment.chai}
end: \*/
name: comment.block.documentation.chai
- begin: /\*
captures:
'0': {name: punctuation.definition.comment.chai}
end: \*/
name: comment.block.chai
- captures:
'1': {name: punctuation.definition.comment.chai}
match: (//).*$\n?
name: comment.line.double-slash.chai
- captures:
'0': {name: punctuation.definition.comment.html.chai}
'2': {name: punctuation.definition.comment.html.chai}
match: (<!--|-->)
name: comment.block.html.chai
- {match: \b(boolean|byte|char|class|double|enum|float|fun|def|int|interface|long|short|var|auto|attr)\b,
name: storage.type.chai}
- {match: \b(break|case|catch|continue|default|do|else|finally|else if|for|goto|if|return|switch|throw|try|while)\b,
name: keyword.control.chai}
- {match: \b(delete|in|instanceof|new|typeof|with)\b, name: keyword.operator.chai}
- {match: \btrue\b, name: constant.language.boolean.true.chai}
- {match: \bfalse\b, name: constant.language.boolean.false.chai}
- {match: \bnull\b, name: constant.language.null.chai}
- {match: \b(Anchor|Applet|Area|Array|Boolean|Button|Checkbox|Date|document|event|FileUpload|Form|Frame|Function|Hidden|History|Image|JavaArray|JavaClass|JavaObject|JavaPackage|java|Layer|Link|Location|Math|MimeType|Number|navigator|netscape|Object|Option|Packages|Password|Plugin|Radio|RegExp|Reset|Select|String|Style|Submit|screen|sun|Text|Textarea|window|XMLHttpRequest)\b,
name: support.class.chai}
- {match: '\b(s(h(ift|ow(Mod(elessDialog|alDialog)|Help))|croll(X|By(Pages|Lines)?|Y|To)?|t(op|rike)|i(n|zeToContent|debar|gnText)|ort|u(p|b(str(ing)?)?)|pli(ce|t)|e(nd|t(Re(sizable|questHeader)|M(i(nutes|lliseconds)|onth)|Seconds|Ho(tKeys|urs)|Year|Cursor|Time(out)?|Interval|ZOptions|Date|UTC(M(i(nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(ome|andleEvent)|navigate|c(har(CodeAt|At)|o(s|n(cat|textual|firm)|mpile)|eil|lear(Timeout|Interval)?|a(ptureEvents|ll)|reate(StyleSheet|Popup|EventObject))|t(o(GMTString|S(tring|ource)|U(TCString|pperCase)|Lo(caleString|werCase))|est|a(n|int(Enabled)?))|i(s(NaN|Finite)|ndexOf|talics)|d(isableExternalCapture|ump|etachEvent)|u(n(shift|taint|escape|watch)|pdateCommands)|j(oin|avaEnabled)|p(o(p|w)|ush|lugins.refresh|a(ddings|rse(Int|Float)?)|r(int|ompt|eference))|e(scape|nableExternalCapture|val|lementFromPoint|x(p|ec(Script|Command)?))|valueOf|UTC|queryCommand(State|Indeterm|Enabled|Value)|f(i(nd|le(ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(nt(size|color)|rward)|loor|romCharCode)|watch|l(ink|o(ad|g)|astIndexOf)|a(sin|nchor|cos|t(tachEvent|ob|an(2)?)|pply|lert|b(s|ort))|r(ou(nd|teEvents)|e(size(By|To)|calc|turnValue|place|verse|l(oad|ease(Capture|Events)))|andom)|g(o|et(ResponseHeader|M(i(nutes|lliseconds)|onth)|Se(conds|lection)|Hours|Year|Time(zoneOffset)?|Da(y|te)|UTC(M(i(nutes|lliseconds)|onth)|Seconds|Hours|Da(y|te)|FullYear)|FullYear|A(ttention|llResponseHeaders)))|m(in|ove(B(y|elow)|To(Absolute)?|Above)|ergeAttributes|a(tch|rgins|x))|b(toa|ig|o(ld|rderWidths)|link|ack))\b(?=\()',
name: support.function.chai}
- {match: '\b(s(ub(stringData|mit)|plitText|e(t(NamedItem|Attribute(Node)?)|lect))|has(ChildNodes|Feature)|namedItem|c(l(ick|o(se|neNode))|reate(C(omment|DATASection|aption)|T(Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(ntityReference|lement)|Attribute))|tabIndex|i(nsert(Row|Before|Cell|Data)|tem)|open|delete(Row|C(ell|aption)|T(Head|Foot)|Data)|focus|write(ln)?|a(dd|ppend(Child|Data))|re(set|place(Child|Data)|move(NamedItem|Child|Attribute(Node)?)?)|get(NamedItem|Element(sBy(Name|TagName)|ById)|Attribute(Node)?)|blur)\b(?=\()',
name: support.function.dom.chai}
- {match: '(?<=\.)(s(ystemLanguage|cr(ipts|ollbars|een(X|Y|Top|Left))|t(yle(Sheets)?|atus(Text|bar)?)|ibling(Below|Above)|ource|uffixes|e(curity(Policy)?|l(ection|f)))|h(istory|ost(name)?|as(h|Focus))|y|X(MLDocument|SLDocument)|n(ext|ame(space(s|URI)|Prop))|M(IN_VALUE|AX_VALUE)|c(haracterSet|o(n(structor|trollers)|okieEnabled|lorDepth|mp(onents|lete))|urrent|puClass|l(i(p(boardData)?|entInformation)|osed|asses)|alle(e|r)|rypto)|t(o(olbar|p)|ext(Transform|Indent|Decoration|Align)|ags)|SQRT(1_2|2)|i(n(ner(Height|Width)|put)|ds|gnoreCase)|zIndex|o(scpu|n(readystatechange|Line)|uter(Height|Width)|p(sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(i(splay|alog(Height|Top|Width|Left|Arguments)|rectories)|e(scription|fault(Status|Ch(ecked|arset)|View)))|u(ser(Profile|Language|Agent)|n(iqueID|defined)|pdateInterval)|_content|p(ixelDepth|ort|ersonalbar|kcs11|l(ugins|atform)|a(thname|dding(Right|Bottom|Top|Left)|rent(Window|Layer)?|ge(X(Offset)?|Y(Offset)?))|r(o(to(col|type)|duct(Sub)?|mpter)|e(vious|fix)))|e(n(coding|abledPlugin)|x(ternal|pando)|mbeds)|v(isibility|endor(Sub)?|Linkcolor)|URLUnencoded|P(I|OSITIVE_INFINITY)|f(ilename|o(nt(Size|Family|Weight)|rmName)|rame(s|Element)|gColor)|E|whiteSpace|l(i(stStyleType|n(eHeight|kColor))|o(ca(tion(bar)?|lName)|wsrc)|e(ngth|ft(Context)?)|a(st(M(odified|atch)|Index|Paren)|yer(s|X)|nguage))|a(pp(MinorVersion|Name|Co(deName|re)|Version)|vail(Height|Top|Width|Left)|ll|r(ity|guments)|Linkcolor|bove)|r(ight(Context)?|e(sponse(XML|Text)|adyState))|global|x|m(imeTypes|ultiline|enubar|argin(Right|Bottom|Top|Left))|L(N(10|2)|OG(10E|2E))|b(o(ttom|rder(Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(Color|Image)))\b',
name: support.constant.chai}
- {match: '(?<=\.)(s(hape|ystemId|c(heme|ope|rolling)|ta(ndby|rt)|ize|ummary|pecified|e(ctionRowIndex|lected(Index)?)|rc)|h(space|t(tpEquiv|mlFor)|e(ight|aders)|ref(lang)?)|n(o(Resize|tation(s|Name)|Shade|Href|de(Name|Type|Value)|Wrap)|extSibling|ame)|c(h(ildNodes|Off|ecked|arset)?|ite|o(ntent|o(kie|rds)|de(Base|Type)?|l(s|Span|or)|mpact)|ell(s|Spacing|Padding)|l(ear|assName)|aption)|t(ype|Bodies|itle|Head|ext|a(rget|gName)|Foot)|i(sMap|ndex|d|m(plementation|ages))|o(ptions|wnerDocument|bject)|d(i(sabled|r)|o(c(type|umentElement)|main)|e(clare|f(er|ault(Selected|Checked|Value)))|at(eTime|a))|useMap|p(ublicId|arentNode|r(o(file|mpt)|eviousSibling))|e(n(ctype|tities)|vent|lements)|v(space|ersion|alue(Type)?|Link|Align)|URL|f(irstChild|orm(s)?|ace|rame(Border)?)|width|l(ink(s)?|o(ngDesc|wSrc)|a(stChild|ng|bel))|a(nchors|c(ce(ssKey|pt(Charset)?)|tion)|ttributes|pplets|l(t|ign)|r(chive|eas)|xis|Link|bbr)|r(ow(s|Span|Index)|ules|e(v|ferrer|l|adOnly))|m(ultiple|e(thod|dia)|a(rgin(Height|Width)|xLength))|b(o(dy|rder)|ackground|gColor))\b',
name: support.constant.dom.chai}
- {match: \b(ELEMENT_NODE|ATTRIBUTE_NODE|TEXT_NODE|CDATA_SECTION_NODE|ENTITY_REFERENCE_NODE|ENTITY_NODE|PROCESSING_INSTRUCTION_NODE|COMMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE|DOCUMENT_FRAGMENT_NODE|NOTATION_NODE|INDEX_SIZE_ERR|DOMSTRING_SIZE_ERR|HIERARCHY_REQUEST_ERR|WRONG_DOCUMENT_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR|NOT_SUPPORTED_ERR|INUSE_ATTRIBUTE_ERR)\b,
name: support.constant.dom.chai}
- {match: '\bon(R(ow(s(inserted|delete)|e(nter|xit))|e(s(ize(start|end)?|et)|adystatechange))|Mouse(o(ut|ver)|down|up|move)|B(efore(cut|deactivate|u(nload|pdate)|p(aste|rint)|editfocus|activate)|lur)|S(croll|top|ubmit|elect(start|ionchange)?)|H(over|elp)|C(hange|ont(extmenu|rolselect)|ut|ellchange|l(ick|ose))|D(eactivate|ata(setc(hanged|omplete)|available)|r(op|ag(start|over|drop|en(ter|d)|leave)?)|blclick)|Unload|P(aste|ropertychange)|Error(update)?|Key(down|up|press)|Focus|Load|A(ctivate|fter(update|print)|bort))\b',
name: support.function.event-handler.chai}
- {match: '!|\$|%|&|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\|\||\?\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\b(in|instanceof|new|delete|typeof|void)\b',
name: keyword.operator.chai}
- {match: \b(Infinity|NaN|undefined)\b, name: constant.language.chai}
- begin: (?<=[=(:]|^|return|&&|\|\||!)\s*(/)(?![/*+{}?])
beginCaptures:
'1': {name: punctuation.definition.string.begin.chai}
end: (/)[igm]*
endCaptures:
'1': {name: punctuation.definition.string.end.chai}
name: string.regexp.chai
patterns:
- {match: \\., name: constant.character.escape.chai}
- {match: \;, name: punctuation.terminator.statement.chai}
- {match: ',[ |\t]*', name: meta.delimiter.object.comma.chai}
- {match: \., name: meta.delimiter.method.period.chai}
- {match: '\{|\}', name: meta.brace.curly.chai}
- {match: \(|\), name: meta.brace.round.chai}
- {match: '\[|\]', name: meta.brace.square.chai}
scopeName: source.chai
uuid: 93E017CC-6F27-11D9-90EB-000D93589AF6
...

View File

@ -0,0 +1,333 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>comment</key>
<string>ChaiScript Syntax: version 2.0</string>
<key>fileTypes</key>
<array>
<string>chai</string>
</array>
<key>firstLineMatch</key>
<string>^#!/usr/bin/env node</string>
<key>foldingStartMarker</key>
<string>^.*\bdef\s*(\w+\s*)?\([^\)]*\)(\s*\{[^\}]*)?\s*$</string>
<key>foldingStopMarker</key>
<string>^\s*\}</string>
<key>keyEquivalent</key>
<string>^~J</string>
<key>name</key>
<string>ChaiScript</string>
<key>patterns</key>
<array>
<dict>
<key>comment</key>
<string>chaiscript shebang</string>
<key>match</key>
<string>^#</string>
<key>name</key>
<string>comment.line.chai</string>
</dict>
<dict>
<key>match</key>
<string>\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?))\b</string>
<key>name</key>
<string>constant.numeric.chai</string>
</dict>
<dict>
<key>begin</key>
<string>'</string>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.chai</string>
</dict>
</dict>
<key>end</key>
<string>'</string>
<key>endCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.chai</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.single.chai</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)</string>
<key>name</key>
<string>constant.character.escape.chai</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>"</string>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.chai</string>
</dict>
</dict>
<key>end</key>
<string>"</string>
<key>endCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.chai</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.double.chai</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]|37[0-7]?|[4-7][0-7]?|.)</string>
<key>name</key>
<string>constant.character.escape.chai</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>/\*\*(?!/)</string>
<key>captures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.comment.chai</string>
</dict>
</dict>
<key>end</key>
<string>\*/</string>
<key>name</key>
<string>comment.block.documentation.chai</string>
</dict>
<dict>
<key>begin</key>
<string>/\*</string>
<key>captures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.comment.chai</string>
</dict>
</dict>
<key>end</key>
<string>\*/</string>
<key>name</key>
<string>comment.block.chai</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.comment.chai</string>
</dict>
</dict>
<key>match</key>
<string>(//).*$\n?</string>
<key>name</key>
<string>comment.line.double-slash.chai</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.comment.html.chai</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>punctuation.definition.comment.html.chai</string>
</dict>
</dict>
<key>match</key>
<string>(&lt;!--|--&gt;)</string>
<key>name</key>
<string>comment.block.html.chai</string>
</dict>
<dict>
<key>match</key>
<string>\b(boolean|byte|char|class|double|enum|float|fun|def|int|interface|long|short|var|auto|attr)\b</string>
<key>name</key>
<string>storage.type.chai</string>
</dict>
<dict>
<key>match</key>
<string>\b(break|case|catch|continue|default|do|else|finally|else if|for|goto|if|return|switch|throw|try|while)\b</string>
<key>name</key>
<string>keyword.control.chai</string>
</dict>
<dict>
<key>match</key>
<string>\b(delete|in|instanceof|new|typeof|with)\b</string>
<key>name</key>
<string>keyword.operator.chai</string>
</dict>
<dict>
<key>match</key>
<string>\btrue\b</string>
<key>name</key>
<string>constant.language.boolean.true.chai</string>
</dict>
<dict>
<key>match</key>
<string>\bfalse\b</string>
<key>name</key>
<string>constant.language.boolean.false.chai</string>
</dict>
<dict>
<key>match</key>
<string>\bnull\b</string>
<key>name</key>
<string>constant.language.null.chai</string>
</dict>
<dict>
<key>match</key>
<string>\b(Anchor|Applet|Area|Array|Boolean|Button|Checkbox|Date|document|event|FileUpload|Form|Frame|Function|Hidden|History|Image|JavaArray|JavaClass|JavaObject|JavaPackage|java|Layer|Link|Location|Math|MimeType|Number|navigator|netscape|Object|Option|Packages|Password|Plugin|Radio|RegExp|Reset|Select|String|Style|Submit|screen|sun|Text|Textarea|window|XMLHttpRequest)\b</string>
<key>name</key>
<string>support.class.chai</string>
</dict>
<dict>
<key>match</key>
<string>\b(s(h(ift|ow(Mod(elessDialog|alDialog)|Help))|croll(X|By(Pages|Lines)?|Y|To)?|t(op|rike)|i(n|zeToContent|debar|gnText)|ort|u(p|b(str(ing)?)?)|pli(ce|t)|e(nd|t(Re(sizable|questHeader)|M(i(nutes|lliseconds)|onth)|Seconds|Ho(tKeys|urs)|Year|Cursor|Time(out)?|Interval|ZOptions|Date|UTC(M(i(nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(ome|andleEvent)|navigate|c(har(CodeAt|At)|o(s|n(cat|textual|firm)|mpile)|eil|lear(Timeout|Interval)?|a(ptureEvents|ll)|reate(StyleSheet|Popup|EventObject))|t(o(GMTString|S(tring|ource)|U(TCString|pperCase)|Lo(caleString|werCase))|est|a(n|int(Enabled)?))|i(s(NaN|Finite)|ndexOf|talics)|d(isableExternalCapture|ump|etachEvent)|u(n(shift|taint|escape|watch)|pdateCommands)|j(oin|avaEnabled)|p(o(p|w)|ush|lugins.refresh|a(ddings|rse(Int|Float)?)|r(int|ompt|eference))|e(scape|nableExternalCapture|val|lementFromPoint|x(p|ec(Script|Command)?))|valueOf|UTC|queryCommand(State|Indeterm|Enabled|Value)|f(i(nd|le(ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(nt(size|color)|rward)|loor|romCharCode)|watch|l(ink|o(ad|g)|astIndexOf)|a(sin|nchor|cos|t(tachEvent|ob|an(2)?)|pply|lert|b(s|ort))|r(ou(nd|teEvents)|e(size(By|To)|calc|turnValue|place|verse|l(oad|ease(Capture|Events)))|andom)|g(o|et(ResponseHeader|M(i(nutes|lliseconds)|onth)|Se(conds|lection)|Hours|Year|Time(zoneOffset)?|Da(y|te)|UTC(M(i(nutes|lliseconds)|onth)|Seconds|Hours|Da(y|te)|FullYear)|FullYear|A(ttention|llResponseHeaders)))|m(in|ove(B(y|elow)|To(Absolute)?|Above)|ergeAttributes|a(tch|rgins|x))|b(toa|ig|o(ld|rderWidths)|link|ack))\b(?=\()</string>
<key>name</key>
<string>support.function.chai</string>
</dict>
<dict>
<key>match</key>
<string>\b(s(ub(stringData|mit)|plitText|e(t(NamedItem|Attribute(Node)?)|lect))|has(ChildNodes|Feature)|namedItem|c(l(ick|o(se|neNode))|reate(C(omment|DATASection|aption)|T(Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(ntityReference|lement)|Attribute))|tabIndex|i(nsert(Row|Before|Cell|Data)|tem)|open|delete(Row|C(ell|aption)|T(Head|Foot)|Data)|focus|write(ln)?|a(dd|ppend(Child|Data))|re(set|place(Child|Data)|move(NamedItem|Child|Attribute(Node)?)?)|get(NamedItem|Element(sBy(Name|TagName)|ById)|Attribute(Node)?)|blur)\b(?=\()</string>
<key>name</key>
<string>support.function.dom.chai</string>
</dict>
<dict>
<key>match</key>
<string>(?&lt;=\.)(s(ystemLanguage|cr(ipts|ollbars|een(X|Y|Top|Left))|t(yle(Sheets)?|atus(Text|bar)?)|ibling(Below|Above)|ource|uffixes|e(curity(Policy)?|l(ection|f)))|h(istory|ost(name)?|as(h|Focus))|y|X(MLDocument|SLDocument)|n(ext|ame(space(s|URI)|Prop))|M(IN_VALUE|AX_VALUE)|c(haracterSet|o(n(structor|trollers)|okieEnabled|lorDepth|mp(onents|lete))|urrent|puClass|l(i(p(boardData)?|entInformation)|osed|asses)|alle(e|r)|rypto)|t(o(olbar|p)|ext(Transform|Indent|Decoration|Align)|ags)|SQRT(1_2|2)|i(n(ner(Height|Width)|put)|ds|gnoreCase)|zIndex|o(scpu|n(readystatechange|Line)|uter(Height|Width)|p(sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(i(splay|alog(Height|Top|Width|Left|Arguments)|rectories)|e(scription|fault(Status|Ch(ecked|arset)|View)))|u(ser(Profile|Language|Agent)|n(iqueID|defined)|pdateInterval)|_content|p(ixelDepth|ort|ersonalbar|kcs11|l(ugins|atform)|a(thname|dding(Right|Bottom|Top|Left)|rent(Window|Layer)?|ge(X(Offset)?|Y(Offset)?))|r(o(to(col|type)|duct(Sub)?|mpter)|e(vious|fix)))|e(n(coding|abledPlugin)|x(ternal|pando)|mbeds)|v(isibility|endor(Sub)?|Linkcolor)|URLUnencoded|P(I|OSITIVE_INFINITY)|f(ilename|o(nt(Size|Family|Weight)|rmName)|rame(s|Element)|gColor)|E|whiteSpace|l(i(stStyleType|n(eHeight|kColor))|o(ca(tion(bar)?|lName)|wsrc)|e(ngth|ft(Context)?)|a(st(M(odified|atch)|Index|Paren)|yer(s|X)|nguage))|a(pp(MinorVersion|Name|Co(deName|re)|Version)|vail(Height|Top|Width|Left)|ll|r(ity|guments)|Linkcolor|bove)|r(ight(Context)?|e(sponse(XML|Text)|adyState))|global|x|m(imeTypes|ultiline|enubar|argin(Right|Bottom|Top|Left))|L(N(10|2)|OG(10E|2E))|b(o(ttom|rder(Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(Color|Image)))\b</string>
<key>name</key>
<string>support.constant.chai</string>
</dict>
<dict>
<key>match</key>
<string>(?&lt;=\.)(s(hape|ystemId|c(heme|ope|rolling)|ta(ndby|rt)|ize|ummary|pecified|e(ctionRowIndex|lected(Index)?)|rc)|h(space|t(tpEquiv|mlFor)|e(ight|aders)|ref(lang)?)|n(o(Resize|tation(s|Name)|Shade|Href|de(Name|Type|Value)|Wrap)|extSibling|ame)|c(h(ildNodes|Off|ecked|arset)?|ite|o(ntent|o(kie|rds)|de(Base|Type)?|l(s|Span|or)|mpact)|ell(s|Spacing|Padding)|l(ear|assName)|aption)|t(ype|Bodies|itle|Head|ext|a(rget|gName)|Foot)|i(sMap|ndex|d|m(plementation|ages))|o(ptions|wnerDocument|bject)|d(i(sabled|r)|o(c(type|umentElement)|main)|e(clare|f(er|ault(Selected|Checked|Value)))|at(eTime|a))|useMap|p(ublicId|arentNode|r(o(file|mpt)|eviousSibling))|e(n(ctype|tities)|vent|lements)|v(space|ersion|alue(Type)?|Link|Align)|URL|f(irstChild|orm(s)?|ace|rame(Border)?)|width|l(ink(s)?|o(ngDesc|wSrc)|a(stChild|ng|bel))|a(nchors|c(ce(ssKey|pt(Charset)?)|tion)|ttributes|pplets|l(t|ign)|r(chive|eas)|xis|Link|bbr)|r(ow(s|Span|Index)|ules|e(v|ferrer|l|adOnly))|m(ultiple|e(thod|dia)|a(rgin(Height|Width)|xLength))|b(o(dy|rder)|ackground|gColor))\b</string>
<key>name</key>
<string>support.constant.dom.chai</string>
</dict>
<dict>
<key>match</key>
<string>\b(ELEMENT_NODE|ATTRIBUTE_NODE|TEXT_NODE|CDATA_SECTION_NODE|ENTITY_REFERENCE_NODE|ENTITY_NODE|PROCESSING_INSTRUCTION_NODE|COMMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE|DOCUMENT_FRAGMENT_NODE|NOTATION_NODE|INDEX_SIZE_ERR|DOMSTRING_SIZE_ERR|HIERARCHY_REQUEST_ERR|WRONG_DOCUMENT_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR|NOT_SUPPORTED_ERR|INUSE_ATTRIBUTE_ERR)\b</string>
<key>name</key>
<string>support.constant.dom.chai</string>
</dict>
<dict>
<key>match</key>
<string>\bon(R(ow(s(inserted|delete)|e(nter|xit))|e(s(ize(start|end)?|et)|adystatechange))|Mouse(o(ut|ver)|down|up|move)|B(efore(cut|deactivate|u(nload|pdate)|p(aste|rint)|editfocus|activate)|lur)|S(croll|top|ubmit|elect(start|ionchange)?)|H(over|elp)|C(hange|ont(extmenu|rolselect)|ut|ellchange|l(ick|ose))|D(eactivate|ata(setc(hanged|omplete)|available)|r(op|ag(start|over|drop|en(ter|d)|leave)?)|blclick)|Unload|P(aste|ropertychange)|Error(update)?|Key(down|up|press)|Focus|Load|A(ctivate|fter(update|print)|bort))\b</string>
<key>name</key>
<string>support.function.event-handler.chai</string>
</dict>
<dict>
<key>match</key>
<string>!|\$|%|&amp;|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|&lt;=|&gt;=|&lt;&lt;=|&gt;&gt;=|&gt;&gt;&gt;=|&lt;&gt;|&lt;|&gt;|!|&amp;&amp;|\|\||\?\:|\*=|(?&lt;!\()/=|%=|\+=|\-=|&amp;=|\^=|\b(in|instanceof|new|delete|typeof|void)\b</string>
<key>name</key>
<string>keyword.operator.chai</string>
</dict>
<dict>
<key>match</key>
<string>\b(Infinity|NaN|undefined)\b</string>
<key>name</key>
<string>constant.language.chai</string>
</dict>
<dict>
<key>begin</key>
<string>(?&lt;=[=(:]|^|return|&amp;&amp;|\|\||!)\s*(/)(?![/*+{}?])</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.chai</string>
</dict>
</dict>
<key>end</key>
<string>(/)[igm]*</string>
<key>endCaptures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.chai</string>
</dict>
</dict>
<key>name</key>
<string>string.regexp.chai</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\.</string>
<key>name</key>
<string>constant.character.escape.chai</string>
</dict>
</array>
</dict>
<dict>
<key>match</key>
<string>\;</string>
<key>name</key>
<string>punctuation.terminator.statement.chai</string>
</dict>
<dict>
<key>match</key>
<string>,[ |\t]*</string>
<key>name</key>
<string>meta.delimiter.object.comma.chai</string>
</dict>
<dict>
<key>match</key>
<string>\.</string>
<key>name</key>
<string>meta.delimiter.method.period.chai</string>
</dict>
<dict>
<key>match</key>
<string>\{|\}</string>
<key>name</key>
<string>meta.brace.curly.chai</string>
</dict>
<dict>
<key>match</key>
<string>\(|\)</string>
<key>name</key>
<string>meta.brace.round.chai</string>
</dict>
<dict>
<key>match</key>
<string>\[|\]</string>
<key>name</key>
<string>meta.brace.square.chai</string>
</dict>
</array>
<key>scopeName</key>
<string>source.chai</string>
<key>uuid</key>
<string>93E017CC-6F27-11D9-90EB-000D93589AF6</string>
</dict>
</plist>

View File

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

7
contrib/vim/README.txt Normal file
View File

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

View File

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

View File

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

View File

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

View File

@ -1,40 +0,0 @@
# Emscripten/WebAssembly build for ChaiScript
# Based on work by Rob Loach: https://github.com/RobLoach/ChaiScript.js
#
# Usage:
# emcmake cmake -B build-em -S emscripten
# cmake --build build-em
cmake_minimum_required(VERSION 3.12)
project(chaiscript_em)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Emscripten-specific compiler/linker flags
add_definitions(-DCHAISCRIPT_NO_THREADS -DCHAISCRIPT_NO_DYNLOAD)
add_executable(chaiscript chaiscript_em.cpp)
target_include_directories(chaiscript PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include)
# Enable WASM exception handling — ChaiScript relies on C++ exceptions for
# error propagation; without this flag exceptions cause an abort in WASM.
target_compile_options(chaiscript PRIVATE -fwasm-exceptions)
# Emscripten link flags: enable embind, allow memory growth, export as ES module-compatible
target_link_options(chaiscript PRIVATE
--bind
-fwasm-exceptions
-sALLOW_MEMORY_GROWTH=1
-sEXPORT_ES6=0
-sMODULARIZE=0
-sINVOKE_RUN=0
)
# Copy the HTML shell to the build output directory
add_custom_command(TARGET chaiscript POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/chaiscript.html
${CMAKE_CURRENT_BINARY_DIR}/chaiscript.html
COMMENT "Copying HTML frontend to build directory"
)

View File

@ -1,240 +0,0 @@
<!doctype html>
<!--
ChaiScript Emscripten Frontend
Based on work by Rob Loach: https://github.com/RobLoach/ChaiScript.js
Copyright 2019, Rob Loach
Copyright 2009-2018, Jason Turner
Licensed under the BSD License. See "license.txt" for details.
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ChaiScript</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: #1a1a2e;
color: #e0e0e0;
min-height: 100vh;
display: flex;
flex-direction: column;
}
header {
background: #16213e;
padding: 1rem 2rem;
border-bottom: 2px solid #0f3460;
display: flex;
align-items: center;
gap: 1rem;
}
header h1 {
font-size: 1.5rem;
color: #e94560;
font-weight: 700;
}
header span {
font-size: 0.85rem;
color: #888;
}
#status {
margin-left: auto;
font-size: 0.85rem;
padding: 0.3rem 0.8rem;
border-radius: 4px;
background: #0f3460;
}
#status.ready { color: #4ecca3; }
#status.loading { color: #e9c46a; }
#status.error { color: #e94560; }
main {
flex: 1;
display: flex;
gap: 0;
}
.panel {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
}
.panel-header {
background: #16213e;
padding: 0.5rem 1rem;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #888;
border-bottom: 1px solid #0f3460;
}
#input {
flex: 1;
background: #0d1117;
color: #c9d1d9;
border: none;
padding: 1rem;
font-family: "SF Mono", "Fira Code", "Fira Mono", Menlo, Consolas, monospace;
font-size: 0.9rem;
line-height: 1.6;
resize: none;
outline: none;
tab-size: 2;
}
.divider {
width: 2px;
background: #0f3460;
}
#output {
flex: 1;
background: #0d1117;
padding: 1rem;
font-family: "SF Mono", "Fira Code", "Fira Mono", Menlo, Consolas, monospace;
font-size: 0.9rem;
line-height: 1.6;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-all;
}
.output-line { color: #c9d1d9; }
.output-error { color: #e94560; }
footer {
background: #16213e;
padding: 0.5rem 1rem;
display: flex;
gap: 0.5rem;
align-items: center;
border-top: 2px solid #0f3460;
}
button {
background: #e94560;
color: #fff;
border: none;
padding: 0.4rem 1.2rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.85rem;
font-weight: 600;
}
button:hover { background: #c73652; }
button:disabled { background: #555; cursor: not-allowed; }
#btn-clear {
background: #0f3460;
}
#btn-clear:hover { background: #1a4a8a; }
.hint {
margin-left: auto;
font-size: 0.75rem;
color: #555;
}
</style>
</head>
<body>
<header>
<h1>ChaiScript</h1>
<span>Interactive Playground</span>
<div id="status" class="loading">Loading...</div>
</header>
<main>
<div class="panel">
<div class="panel-header">Input</div>
<textarea id="input" spellcheck="false">// Welcome to ChaiScript!
// Write your code here and click Run (or press Ctrl+Enter).
def greet(name) {
return "Hello, " + name + "!"
}
print(greet("World"))
print(greet("ChaiScript"))
// Math example
def factorial(n) {
if (n <= 1) { return 1 }
return n * factorial(n - 1)
}
print("5! = " + to_string(factorial(5)))
print("10! = " + to_string(factorial(10)))
</textarea>
</div>
<div class="divider"></div>
<div class="panel">
<div class="panel-header">Output</div>
<div id="output"></div>
</div>
</main>
<footer>
<button id="btn-run" disabled>Run</button>
<button id="btn-clear">Clear</button>
<span class="hint">Ctrl+Enter to run</span>
</footer>
<script>
var outputEl = document.getElementById('output');
var inputEl = document.getElementById('input');
var btnRun = document.getElementById('btn-run');
var btnClear = document.getElementById('btn-clear');
var statusEl = document.getElementById('status');
function appendOutput(text, className) {
var line = document.createElement('div');
line.className = className || 'output-line';
line.textContent = text;
outputEl.appendChild(line);
outputEl.scrollTop = outputEl.scrollHeight;
}
var chaiHandle = 0;
var Module = {
print: function(text) {
appendOutput(text);
},
printErr: function(text) {
appendOutput(text, 'output-error');
},
onRuntimeInitialized: function() {
chaiHandle = Module.create();
statusEl.textContent = 'Ready';
statusEl.className = 'ready';
btnRun.disabled = false;
}
};
function runCode() {
var code = inputEl.value;
if (!code.trim()) return;
appendOutput('> Running...', 'output-line');
try {
Module.eval(chaiHandle, code);
} catch (e) {
appendOutput('Error: ' + e.message, 'output-error');
}
appendOutput('', 'output-line');
}
btnRun.addEventListener('click', runCode);
btnClear.addEventListener('click', function() {
outputEl.innerHTML = '';
});
inputEl.addEventListener('keydown', function(e) {
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
runCode();
}
// Tab key inserts spaces
if (e.key === 'Tab') {
e.preventDefault();
var start = this.selectionStart;
var end = this.selectionEnd;
this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
this.selectionStart = this.selectionEnd = start + 2;
}
});
</script>
<script src="chaiscript.js"></script>
</body>
</html>

View File

@ -1,28 +0,0 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2019, Rob Loach (https://github.com/RobLoach/ChaiScript.js)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
//
// Emscripten/WebAssembly wrapper for ChaiScript.
// Based on work by Rob Loach: https://github.com/RobLoach/ChaiScript.js
#include "chaiscript_eval.hpp"
#ifdef __EMSCRIPTEN__
#include <emscripten/bind.h>
EMSCRIPTEN_BINDINGS(chaiscript) {
emscripten::function("create", &chaiscript_create);
emscripten::function("destroy", &chaiscript_destroy);
emscripten::function("eval", &chaiscript_eval);
emscripten::function("evalString", &chaiscript_eval_string);
emscripten::function("evalBool", &chaiscript_eval_bool);
emscripten::function("evalInt", &chaiscript_eval_int);
emscripten::function("evalFloat", &chaiscript_eval_float);
emscripten::function("evalDouble", &chaiscript_eval_double);
emscripten::function("saveState", &chaiscript_save_state);
emscripten::function("restoreState", &chaiscript_restore_state);
emscripten::function("releaseState", &chaiscript_release_state);
}
#endif

View File

@ -1,124 +0,0 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2019, Rob Loach (https://github.com/RobLoach/ChaiScript.js)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// Shared eval helper functions for the ChaiScript Emscripten wrapper.
// These functions provide typed evaluation of ChaiScript expressions,
// used by both the Emscripten/WebAssembly build and native tests.
//
// The interface is opaque and handle-based: JS callers create one or more
// engines via chaiscript_create(), pass the resulting handle to the eval and
// state helpers, and release the engine with chaiscript_destroy() when done.
// State snapshots are themselves opaque handles produced by
// chaiscript_save_state() and consumed by chaiscript_restore_state() /
// chaiscript_release_state(). Hiding ChaiScript and ChaiScript::State behind
// integer handles keeps embind from having to manage their lifetimes and
// avoids forcing a static singleton on the C++ side.
#ifndef CHAISCRIPT_EMSCRIPTEN_EVAL_HPP_
#define CHAISCRIPT_EMSCRIPTEN_EVAL_HPP_
#include <chaiscript/chaiscript.hpp>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
namespace detail {
// ChaiScript::State captures globals/functions/types but not the top-level
// scripting locals (variables created by `var x = ...` at the script's
// outermost scope). The playground's reset-between-runs use case needs both,
// so the snapshot pairs the engine state with the locals map.
struct Snapshot {
chaiscript::ChaiScript::State engine_state;
std::map<std::string, chaiscript::Boxed_Value> locals;
};
inline std::unordered_map<int, std::unique_ptr<chaiscript::ChaiScript>> &chai_registry() {
static std::unordered_map<int, std::unique_ptr<chaiscript::ChaiScript>> registry;
return registry;
}
inline std::unordered_map<int, Snapshot> &state_registry() {
static std::unordered_map<int, Snapshot> registry;
return registry;
}
inline int next_handle() {
static int handle = 0;
return ++handle;
}
inline chaiscript::ChaiScript &get_chai(const int handle) {
return *chai_registry().at(handle);
}
} // namespace detail
// Construct a fresh ChaiScript engine and return an opaque handle. The handle
// is owned by the caller and must be released with chaiscript_destroy().
inline int chaiscript_create() {
const int handle = detail::next_handle();
detail::chai_registry().emplace(handle, std::make_unique<chaiscript::ChaiScript>());
return handle;
}
// Destroy an engine handle. Unknown handles are silently ignored so JS callers
// do not need to track validity defensively.
inline void chaiscript_destroy(const int handle) {
detail::chai_registry().erase(handle);
}
inline void chaiscript_eval(const int handle, const std::string &input) {
detail::get_chai(handle).eval(input);
}
inline std::string chaiscript_eval_string(const int handle, const std::string &input) {
return detail::get_chai(handle).eval<std::string>(input);
}
inline bool chaiscript_eval_bool(const int handle, const std::string &input) {
return detail::get_chai(handle).eval<bool>(input);
}
inline int chaiscript_eval_int(const int handle, const std::string &input) {
return detail::get_chai(handle).eval<int>(input);
}
inline float chaiscript_eval_float(const int handle, const std::string &input) {
return detail::get_chai(handle).eval<float>(input);
}
inline double chaiscript_eval_double(const int handle, const std::string &input) {
return detail::get_chai(handle).eval<double>(input);
}
// Snapshot the engine identified by chai_handle and return a fresh opaque
// state handle. Release it with chaiscript_release_state() when no longer
// needed.
inline int chaiscript_save_state(const int chai_handle) {
const int state_handle = detail::next_handle();
auto &chai = detail::get_chai(chai_handle);
detail::state_registry().emplace(state_handle, detail::Snapshot{chai.get_state(), chai.get_locals()});
return state_handle;
}
// Restore a previously snapshotted state onto the engine identified by
// chai_handle. Unknown state handles are silently ignored.
inline void chaiscript_restore_state(const int chai_handle, const int state_handle) {
const auto it = detail::state_registry().find(state_handle);
if (it != detail::state_registry().end()) {
auto &chai = detail::get_chai(chai_handle);
chai.set_state(it->second.engine_state);
chai.set_locals(it->second.locals);
}
}
// Release a state handle returned by chaiscript_save_state. Releasing an
// unknown handle is a no-op.
inline void chaiscript_release_state(const int state_handle) {
detail::state_registry().erase(state_handle);
}
#endif /* CHAISCRIPT_EMSCRIPTEN_EVAL_HPP_ */

View File

@ -1,189 +0,0 @@
/*
* ChaiScript Grammar — EBNF for Railroad Diagram Generation
*
* View as navigable railroad diagrams at:
* https://www.bottlecaps.de/rr/ui (IPv6)
* https://rr.red-dove.com/ui (IPv4)
*
* Copy and paste this file into the 'Edit Grammar' tab, then
* click 'View Diagram'.
*
* This grammar uses the notation accepted by
* https://github.com/GuntherRademacher/rr :
* - "::=" as rule separator
* - no semicolon at end of rule
* - "?" "+" "*" for repetition
* - C comments
*/
/* ---- Top-level ---- */
statements ::= ( def | try | if | while | class | enum
| for | switch | return | break | continue
| equation | block | eol )+
/* ---- Functions ---- */
def ::= "def" id ( "::" id )? "(" decl_arg_list ")" eol*
( ":" guard )? eol* block
lambda ::= "fun" ( "[" id_arg_list "]" )? "(" decl_arg_list ")" eol* block
guard ::= operator
/* ---- Exception handling ---- */
try ::= "try" eol* block catch* finally?
catch ::= "catch" ( "(" arg ")" )? eol* block
finally ::= "finally" eol* block
/* ---- Control flow ---- */
if ::= "if" "(" equation ( eol equation )? ")" eol* block
( "else" ( if | eol* block ) )*
while ::= "while" "(" operator ")" eol* block
for ::= "for" "(" ( for_guards | equation ":" equation ) ")" eol* block
for_guards ::= equation eol equation eol equation
switch ::= "switch" "(" operator ")" eol* "{" ( case | default )+ "}"
case ::= "case" "(" operator ")" eol* block
default ::= "default" eol* block
/* ---- Classes ---- */
class ::= "class" id ( ":" id )? eol* class_block
class_block ::= "{" class_statements* "}"
class_statements ::= def | var_decl | eol
/* ---- Enums ---- */
enum ::= "enum" ( "class" | "struct" ) id ( ":" underlying_type )?
"{" enum_entries? "}"
enum_entries ::= enum_entry ( "," enum_entry )*
enum_entry ::= id ( "=" integer )?
underlying_type ::= id
/* ---- Blocks & flow keywords ---- */
block ::= "{" statements* "}"
return ::= "return" operator?
break ::= "break"
continue ::= "continue"
/* ---- Line termination ---- */
eol ::= "\n" | "\r\n" | ";"
/* ---- Equations & operators ---- */
equation ::= operator ( ( "=" | ":=" | "+=" | "-=" | "*=" | "/="
| "%=" | "<<=" | ">>=" | "&=" | "^=" | "|=" )
equation )?
operator ::= prefix
| value
| operator binary_operator operator
| operator "?" operator ":" operator
prefix ::= ( "++" | "--" | "-" | "+" | "!" | "~" ) operator
binary_operator ::= "||" | "&&"
| "|" | "^" | "&"
| "==" | "!="
| "<" | "<=" | ">" | ">="
| "<<" | ">>"
| "+" | "-"
| "*" | "/" | "%"
/* ---- Values & access ---- */
value ::= var_decl | dot_fun_array | prefix
dot_fun_array ::= ( lambda | num | quoted_string
| single_quoted_string | raw_string
| paren_expression | inline_container
| id )
( fun_call | array_call | dot_access )*
fun_call ::= "(" arg_list ")"
array_call ::= "[" operator "]"
dot_access ::= "." id
/* ---- Variable declarations ---- */
var_decl ::= ( "auto" | "var" | "const" ) ( reference | id )
| "global" ( reference | id )
| "attr" id ( "::" id )?
reference ::= "&" id
/* ---- Parenthesised & inline containers ---- */
paren_expression ::= "(" operator ")"
inline_container ::= "[" container_arg_list "]"
container_arg_list ::= value_range
| map_pair ( "," map_pair )*
| operator ( "," operator )*
value_range ::= operator ".." operator
map_pair ::= operator ":" operator
/* ---- String literals ---- */
quoted_string ::= '"' ( char | escape | interpolation )* '"'
single_quoted_string ::= "'" ( char | escape ) "'"
raw_string ::= 'R"' delimiter? "(" char* ")" delimiter? '"'
delimiter ::= [a-zA-Z0-9_]+
interpolation ::= "${" equation "}"
/* ---- Escape sequences ---- */
escape ::= "\" ( "'" | '"' | "?" | "\" | "a" | "b"
| "f" | "n" | "r" | "t" | "v" | "$"
| "0"
| "x" hex_digit+
| "u" hex_digit hex_digit hex_digit hex_digit
| "U" hex_digit hex_digit hex_digit hex_digit
hex_digit hex_digit hex_digit hex_digit
| octal_digit+ )
/* ---- Argument lists ---- */
id_arg_list ::= id ( "," id )*
decl_arg_list ::= ( arg ( "," arg )* )?
arg_list ::= ( equation ( "," equation )* )?
arg ::= id id?
/* ---- Identifiers ---- */
id ::= ( [a-zA-Z_] [a-zA-Z0-9_]* )
| ( "`" [^`]+ "`" )
| "true" | "false"
| "Infinity" | "NaN"
| "_"
| "__LINE__" | "__FILE__" | "__FUNC__" | "__CLASS__"
/* ---- Numeric literals ---- */
num ::= hex | binary | float | integer
hex ::= "0" ( "x" | "X" ) [0-9a-fA-F]+ int_suffix*
binary ::= "0" ( "b" | "B" ) [01]+ int_suffix*
float ::= [0-9]+ "." [0-9]+ ( ( "e" | "E" ) ( "+" | "-" )? [0-9]+ )? float_suffix?
integer ::= [0-9]+ int_suffix*
int_suffix ::= "l" | "L" | "ll" | "LL" | "u" | "U"
float_suffix ::= "l" | "L" | "f" | "F"
/* ---- Character classes ---- */
octal_digit ::= [0-7]
hex_digit ::= [0-9a-fA-F]
char ::= [^"\]

View File

@ -1,15 +1,14 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_HPP_ #ifndef CHAISCRIPT_HPP_
#define CHAISCRIPT_HPP_ #define CHAISCRIPT_HPP_
/// @mainpage /// @mainpage
/// [ChaiScript](http://www.chaiscript.com") is a scripting language designed specifically for integration with C++. It provides /// [ChaiScript](http://www.chaiscript.com") is a scripting language designed specifically for integration with C++. It provides
/// seamless integration with C++ on all levels, including shared_ptr objects, functors and exceptions. /// seamless integration with C++ on all levels, including shared_ptr objects, functors and exceptions.
@ -19,8 +18,7 @@
/// ///
/// The end user parts of the API are extremely simple both in size and ease of use. /// The end user parts of the API are extremely simple both in size and ease of use.
/// ///
/// Currently, all source control and project management aspects of ChaiScript occur on /// Currently, all source control and project management aspects of ChaiScript occur on [github](http://www.github.com/ChaiScript/ChaiScript").
/// [github](http://www.github.com/ChaiScript/ChaiScript").
/// ///
/// ------------------------------------------------------------ /// ------------------------------------------------------------
/// ///
@ -67,7 +65,7 @@
/// int main() /// int main()
/// { /// {
/// chaiscript::ChaiScript chai; /// chaiscript::ChaiScript chai;
/// chai.add(chaiscript::fun(&function), "function"); /// chai.add(&function, "function");
/// ///
/// double d = chai.eval<double>("function(3, 4.75);"); /// double d = chai.eval<double>("function(3, 4.75);");
/// } /// }
@ -313,8 +311,8 @@
/// As much as possible, ChaiScript attempts to convert between &, *, const &, const *, std::shared_ptr<T>, /// As much as possible, ChaiScript attempts to convert between &, *, const &, const *, std::shared_ptr<T>,
/// std::shared_ptr<const T>, std::reference_wrapper<T>, std::reference_wrapper<const T> and value types automatically. /// std::shared_ptr<const T>, std::reference_wrapper<T>, std::reference_wrapper<const T> and value types automatically.
/// ///
/// If a chaiscript::var object was created in C++ from a pointer, it cannot be converted to a shared_ptr (this would add invalid reference /// If a chaiscript::var object was created in C++ from a pointer, it cannot be converted to a shared_ptr (this would add invalid reference counting).
/// counting). Const may be added, but never removed. /// Const may be added, but never removed.
/// ///
/// The take away is that you can pretty much expect function calls to Just Work when you need them to. /// The take away is that you can pretty much expect function calls to Just Work when you need them to.
/// ///
@ -468,8 +466,7 @@
/// chaiscript::ChaiScript chai; /// chaiscript::ChaiScript chai;
/// ///
/// try { /// try {
/// chai.eval("throw(runtime_error(@"error@"))", chaiscript::exception_specification<int, double, float, const std::string &, const /// chai.eval("throw(runtime_error(@"error@"))", chaiscript::exception_specification<int, double, float, const std::string &, const std::exception &>());
/// std::exception &>());
/// } catch (const double e) { /// } catch (const double e) {
/// } catch (int) { /// } catch (int) {
/// } catch (float) { /// } catch (float) {
@ -482,6 +479,8 @@
/// @sa chaiscript::Exception_Handler for details on automatic exception unboxing /// @sa chaiscript::Exception_Handler for details on automatic exception unboxing
/// @sa chaiscript::exception_specification /// @sa chaiscript::exception_specification
/// @page LangObjectSystemRef ChaiScript Language Object Model Reference /// @page LangObjectSystemRef ChaiScript Language Object Model Reference
/// ///
/// ///
@ -649,6 +648,7 @@
/// @sa @ref LangKeywordRef /// @sa @ref LangKeywordRef
/// @sa ChaiScript_Language for Built in Functions /// @sa ChaiScript_Language for Built in Functions
/// @page LangKeywordRef ChaiScript Language Keyword Reference /// @page LangKeywordRef ChaiScript Language Keyword Reference
/// ///
/// ///
@ -695,10 +695,11 @@
/// Begins a function or method definition /// Begins a function or method definition
/// ///
/// ~~~~~~~~ /// ~~~~~~~~
/// Function Definition ::= "def" identifier "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block /// Function Definition ::= [annotation + CR/LF] "def" identifier "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block
/// Method Definition ::= "def" class_name "::" method_name "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block /// Method Definition ::= [annotation + CR/LF] "def" class_name "::" method_name "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block
/// ~~~~~~~~ /// ~~~~~~~~
/// ///
/// annotation: meta-annotation on function, currently used as documentation. Optional.
/// identifier: name of function. Required. /// identifier: name of function. Required.
/// args: comma-delimited list of parameter names with optional type specifiers. Optional. /// args: comma-delimited list of parameter names with optional type specifiers. Optional.
/// guards: guarding statement that act as a prerequisite for the function. Optional. /// guards: guarding statement that act as a prerequisite for the function. Optional.
@ -809,31 +810,23 @@
/// ///
/// Synonym for @ref keywordauto /// Synonym for @ref keywordauto
/// @namespace chaiscript /// @namespace chaiscript
/// @brief Namespace chaiscript contains every API call that the average user will be concerned with. /// @brief Namespace chaiscript contains every API call that the average user will be concerned with.
/// @namespace chaiscript::detail /// @namespace chaiscript::detail
/// @brief Classes and functions reserved for internal use. Items in this namespace are not supported. /// @brief Classes and functions reserved for internal use. Items in this namespace are not supported.
#include "chaiscript_basic.hpp" #include "chaiscript_defines.hpp"
#include "chaiscript_stdlib.hpp"
#include "language/chaiscript_parser.hpp" #include "dispatchkit/dispatchkit.hpp"
#include "dispatchkit/function_call.hpp"
#include "dispatchkit/dynamic_object.hpp"
#include "dispatchkit/boxed_number.hpp"
#include "language/chaiscript_eval.hpp"
#include "language/chaiscript_engine.hpp"
namespace chaiscript {
class ChaiScript : public ChaiScript_Basic {
public:
ChaiScript(std::vector<std::string> t_modulepaths = {},
std::vector<std::string> t_usepaths = {},
std::vector<Options> t_opts = chaiscript::default_options(),
std::vector<Library_Options> t_lib_opts = {})
: ChaiScript_Basic(chaiscript::Std_Lib::library(t_lib_opts),
std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default>>(),
std::move(t_modulepaths),
std::move(t_usepaths),
std::move(t_opts),
std::find(t_lib_opts.begin(), t_lib_opts.end(), Library_Options::No_IO) != t_lib_opts.end()) {
}
};
} // namespace chaiscript
#endif /* CHAISCRIPT_HPP_ */ #endif /* CHAISCRIPT_HPP_ */

View File

@ -1,37 +0,0 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_BASIC_HPP_
#define CHAISCRIPT_BASIC_HPP_
#include "chaiscript_defines.hpp"
#include "dispatchkit/boxed_number.hpp"
#include "dispatchkit/dispatchkit.hpp"
#include "dispatchkit/dynamic_object.hpp"
#include "dispatchkit/function_call.hpp"
#include "language/chaiscript_engine.hpp"
#include "language/chaiscript_eval.hpp"
// This file includes all of the basic requirements for ChaiScript,
// to use, you might do something like:
//
/*
#include "chaiscript_stdlib.hpp"
#include "language/chaiscript_parser.hpp"
ChaiScript_Basic chai(
chaiscript::Std_Lib::library(),
std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default>>());
*/
// If you want a fully packaged ready to go ChaiScript, use chaiscript.hpp
#endif /* CHAISCRIPT_BASIC_HPP_ */

View File

@ -1,36 +1,24 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com // http://www.chaiscript.com
#ifndef CHAISCRIPT_DEFINES_HPP_ #ifndef CHAISCRIPT_DEFINES_HPP_
#define CHAISCRIPT_DEFINES_HPP_ #define CHAISCRIPT_DEFINES_HPP_
// MacOSX Fix: std::get/std::get_if/std::visit for std::variant are annotated
// __attribute__((availability(macos,strict,introduced=10.14))) in Apple libc++.
// Defining this macro before any libc++ header strips those annotations so
// ChaiScript can be built against older MacOSX deployment targets.
#if defined(__APPLE__) && defined(__clang__)
#ifndef _LIBCPP_DISABLE_AVAILABILITY
#define _LIBCPP_DISABLE_AVAILABILITY
#endif
#endif
#ifdef _MSC_VER #ifdef _MSC_VER
#define CHAISCRIPT_STRINGIZE(x) "" #x
#define CHAISCRIPT_STRINGIZE_EXPANDED(x) CHAISCRIPT_STRINGIZE(x)
#define CHAISCRIPT_COMPILER_VERSION CHAISCRIPT_STRINGIZE_EXPANDED(_MSC_FULL_VER)
#define CHAISCRIPT_MSVC _MSC_VER #define CHAISCRIPT_MSVC _MSC_VER
#define CHAISCRIPT_HAS_DECLSPEC #define CHAISCRIPT_HAS_DECLSPEC
#if _MSC_VER <= 1800
static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later required"); #define CHAISCRIPT_MSVC_12
#endif
#else #endif
#define CHAISCRIPT_COMPILER_VERSION __VERSION__
#ifndef CHAISCRIPT_MSVC_12
#define CHAISCRIPT_HAS_MAGIC_STATICS
#endif #endif
#include <string_view>
#include <vector> #include <vector>
#if defined( _LIBCPP_VERSION ) #if defined( _LIBCPP_VERSION )
@ -41,76 +29,47 @@ static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later req
#define CHAISCRIPT_WINDOWS #define CHAISCRIPT_WINDOWS
#endif #endif
#if defined(_WIN32) #if (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || (defined(__llvm__) && !defined(CHAISCRIPT_LIBCPP))
#if defined(__llvm__) /// Currently only g++>=4.8 supports this natively
#define CHAISCRIPT_COMPILER_NAME "clang(windows)" /// \todo Make this support other compilers when possible
#elif defined(__GNUC__) #define CHAISCRIPT_HAS_THREAD_LOCAL
#define CHAISCRIPT_COMPILER_NAME "gcc(mingw)"
#else
#define CHAISCRIPT_COMPILER_NAME "msvc"
#endif
#else
#if defined(__llvm__)
#define CHAISCRIPT_COMPILER_NAME "clang"
#elif defined(__GNUC__)
#define CHAISCRIPT_COMPILER_NAME "gcc"
#else
#define CHAISCRIPT_COMPILER_NAME "unknown"
#endif
#endif #endif
#if defined(__llvm__) #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 6)
#define CHAISCRIPT_CLANG #define CHAISCRIPT_GCC_4_6
#endif #endif
#if (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || defined(CHAISCRIPT_MSVC) || defined(__llvm__)
#define CHAISCRIPT_OVERRIDE override
#else
#define CHAISCRIPT_OVERRIDE
#endif
#ifdef CHAISCRIPT_HAS_DECLSPEC #ifdef CHAISCRIPT_HAS_DECLSPEC
#define CHAISCRIPT_MODULE_EXPORT extern "C" __declspec(dllexport) #define CHAISCRIPT_MODULE_EXPORT extern "C" __declspec(dllexport)
#else #else
#define CHAISCRIPT_MODULE_EXPORT extern "C" #define CHAISCRIPT_MODULE_EXPORT extern "C"
#endif #endif
#ifdef _DEBUG #ifdef CHAISCRIPT_MSVC_12
#define CHAISCRIPT_DEBUG true #define CHAISCRIPT_NOEXCEPT throw()
#define CHAISCRIPT_CONSTEXPR
#else #else
#define CHAISCRIPT_DEBUG false #define CHAISCRIPT_NOEXCEPT noexcept
#define CHAISCRIPT_CONSTEXPR constexpr
#endif #endif
// Upper bound on the depth of nested ChaiScript function calls. Hitting it
// causes the dispatcher to throw chaiscript::exception::stack_overflow_error
// instead of letting the native call stack overflow. Defining the macro on
// the command line overrides the default.
//
// MSVC Debug builds emit very large per-frame native stack usage (no inlining,
// /RTC, buffer security checks) and Windows defaults to a 1 MiB thread stack,
// so the same ChaiScript depth that fits comfortably on Linux/macOS or in an
// MSVC Release build overflows the native stack before the depth check fires.
// We pick a tighter default in that configuration to keep the throw reachable.
#ifndef CHAISCRIPT_MAX_CALL_DEPTH
#if defined(CHAISCRIPT_MSVC) && CHAISCRIPT_DEBUG
#define CHAISCRIPT_MAX_CALL_DEPTH 32
#else
#define CHAISCRIPT_MAX_CALL_DEPTH 256
#endif
#endif
#include <cmath>
#include <memory> #include <memory>
#include <string>
namespace chaiscript { namespace chaiscript {
constexpr static const int version_major = 7; static const int version_major = 5;
constexpr static const int version_minor = 0; static const int version_minor = 7;
constexpr static const int version_patch = 0; static const int version_patch = 2;
constexpr static const char *compiler_version = CHAISCRIPT_COMPILER_VERSION;
constexpr static const char *compiler_name = CHAISCRIPT_COMPILER_NAME;
constexpr static const bool debug_build = CHAISCRIPT_DEBUG;
constexpr static const int max_call_depth = CHAISCRIPT_MAX_CALL_DEPTH;
static_assert(max_call_depth > 0, "CHAISCRIPT_MAX_CALL_DEPTH must be a positive integer");
template<typename B, typename D, typename ...Arg> template<typename B, typename D, typename ...Arg>
inline std::shared_ptr<B> make_shared(Arg &&...arg) { inline std::shared_ptr<B> make_shared(Arg && ... arg)
{
#ifdef CHAISCRIPT_USE_STD_MAKE_SHARED #ifdef CHAISCRIPT_USE_STD_MAKE_SHARED
return std::make_shared<D>(std::forward<Arg>(arg)...); return std::make_shared<D>(std::forward<Arg>(arg)...);
#else #else
@ -118,161 +77,6 @@ namespace chaiscript {
#endif #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 #endif
}
struct Build_Info {
[[nodiscard]] constexpr static int version_major() noexcept { return chaiscript::version_major; }
[[nodiscard]] constexpr static int version_minor() noexcept { return chaiscript::version_minor; }
[[nodiscard]] constexpr static int version_patch() noexcept { return chaiscript::version_patch; }
[[nodiscard]] static std::string version() {
return std::to_string(version_major()) + '.' + std::to_string(version_minor()) + '.' + std::to_string(version_patch());
}
[[nodiscard]] static std::string compiler_id() { return compiler_name() + '-' + compiler_version(); }
[[nodiscard]] static std::string build_id() { return compiler_id() + (debug_build() ? "-Debug" : "-Release"); }
[[nodiscard]] static std::string compiler_version() { return chaiscript::compiler_version; }
[[nodiscard]] static std::string compiler_name() { return chaiscript::compiler_name; }
[[nodiscard]] constexpr static bool debug_build() noexcept { return chaiscript::debug_build; }
};
template<typename T>
[[nodiscard]] constexpr auto parse_num(const std::string_view t_str) noexcept -> typename std::enable_if<std::is_integral<T>::value, T>::type {
T t = 0;
for (const auto c : t_str) {
if (c < '0' || c > '9') {
return t;
}
t *= 10;
t += c - '0';
}
return t;
}
template<typename T>
[[nodiscard]] auto parse_num(const std::string_view t_str) -> typename std::enable_if<!std::is_integral<T>::value, T>::type {
T t = 0;
T base{};
T decimal_place = 0;
int exponent = 0;
for (const auto 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:
break;
}
}
return exponent ? base * std::pow(T(10), t * static_cast<T>(exponent)) : t;
}
struct str_equal {
[[nodiscard]] bool operator()(const std::string &t_lhs, const std::string &t_rhs) const noexcept { return t_lhs == t_rhs; }
template<typename LHS, typename RHS>
[[nodiscard]] constexpr bool operator()(const LHS &t_lhs, const RHS &t_rhs) const noexcept {
return std::equal(t_lhs.begin(), t_lhs.end(), t_rhs.begin(), t_rhs.end());
}
struct is_transparent {
};
};
struct str_less {
[[nodiscard]] bool operator()(const std::string &t_lhs, const std::string &t_rhs) const noexcept { return t_lhs < t_rhs; }
template<typename LHS, typename RHS>
[[nodiscard]] constexpr bool operator()(const LHS &t_lhs, const RHS &t_rhs) const noexcept {
return std::lexicographical_compare(t_lhs.begin(), t_lhs.end(), t_rhs.begin(), t_rhs.end());
}
struct is_transparent {
};
};
enum class Options {
No_Load_Modules,
Load_Modules,
No_External_Scripts,
External_Scripts
};
enum class Library_Options {
No_Stdlib,
No_IO,
No_Prelude,
No_JSON
};
template<typename From, typename To>
struct is_nothrow_forward_constructible : std::bool_constant<noexcept(To{std::declval<From>()})> {
};
template<class From, class To>
static inline constexpr bool is_nothrow_forward_constructible_v = is_nothrow_forward_constructible<From, To>::value;
template<typename Container, typename... T>
[[nodiscard]] constexpr auto make_container(T &&...t) {
Container c;
c.reserve(sizeof...(t));
(c.push_back(std::forward<T>(t)), ...);
return c;
}
template<typename... T>
[[nodiscard]] auto make_vector(T &&...t) -> std::vector<std::common_type_t<std::decay_t<T>...>> {
using container_type = std::vector<std::common_type_t<std::decay_t<T>...>>;
return make_container<container_type>(std::forward<T>(t)...);
}
[[nodiscard]] 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
}
} // namespace chaiscript
#endif

View File

@ -10,74 +10,57 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <string_view>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "chaiscript_defines.hpp" #include "chaiscript_defines.hpp"
#include "language/chaiscript_common.hpp" #include "dispatchkit/dispatchkit.hpp"
#include "dispatchkit/function_call.hpp"
// #include "dispatchkit/dispatchkit.hpp"
#include "dispatchkit/bootstrap.hpp" #include "dispatchkit/bootstrap.hpp"
#include "dispatchkit/bootstrap_stl.hpp" #include "dispatchkit/bootstrap_stl.hpp"
#include "dispatchkit/operators.hpp" #include "dispatchkit/boxed_value.hpp"
// #include "dispatchkit/boxed_value.hpp" #include "language/chaiscript_prelude.chai"
#include "dispatchkit/register_function.hpp"
#include "language/chaiscript_prelude.hpp"
#include "utility/json_wrap.hpp" #include "utility/json_wrap.hpp"
#ifndef CHAISCRIPT_NO_THREADS #ifndef CHAISCRIPT_NO_THREADS
#include <future> #include <future>
#endif #endif
/// @file /// @file
/// ///
/// This file generates the standard library that normal ChaiScript usage requires. /// This file generates the standard library that normal ChaiScript usage requires.
namespace chaiscript { namespace chaiscript
class Std_Lib { {
class Std_Lib
{
public: public:
[[nodiscard]] static ModulePtr library(const std::vector<Library_Options> &t_opts = {}) {
if (std::find(t_opts.begin(), t_opts.end(), Library_Options::No_Stdlib) != t_opts.end()) {
return std::make_shared<Module>();
}
auto lib = std::make_shared<Module>(); static ModulePtr library()
{
using namespace bootstrap;
const bool no_prelude = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_Prelude) != t_opts.end(); ModulePtr lib = Bootstrap::bootstrap();
const bool no_json = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_JSON) != t_opts.end();
bootstrap::Bootstrap::bootstrap(*lib); lib->add(standard_library::vector_type<std::vector<Boxed_Value> >("Vector"));
lib->add(standard_library::string_type<std::string>("string"));
bootstrap::standard_library::vector_type<std::vector<Boxed_Value>>("Vector", *lib); lib->add(standard_library::map_type<std::map<std::string, Boxed_Value> >("Map"));
bootstrap::standard_library::string_type<std::string>("string", *lib); lib->add(standard_library::pair_type<std::pair<Boxed_Value, Boxed_Value > >("Pair"));
bootstrap::standard_library::string_view_type<std::string_view, std::string>("string_view", *lib);
bootstrap::standard_library::map_type<std::map<std::string, Boxed_Value>>("Map", *lib);
bootstrap::standard_library::pair_type<std::pair<Boxed_Value, Boxed_Value>>("Pair", *lib);
// Allow explicit conversion from std::string_view back to std::string,
// e.g. `string(sv)` in ChaiScript.
lib->add(fun([](const std::string_view sv) { return std::string{sv}; }), "string");
#ifndef CHAISCRIPT_NO_THREADS #ifndef CHAISCRIPT_NO_THREADS
bootstrap::standard_library::future_type<std::future<chaiscript::Boxed_Value>>("future", *lib); lib->add(standard_library::future_type<std::future<chaiscript::Boxed_Value>>("future"));
// Note: async() is registered in ChaiScript_Basic::build_eval_system() lib->add(chaiscript::fun([](const std::function<chaiscript::Boxed_Value ()> &t_func){ return std::async(std::launch::async, t_func);}), "async");
// with thread tracking to prevent heap-use-after-free on engine destruction.
#endif #endif
if (!no_json) { lib->add(json_wrap::library());
json_wrap::library(*lib);
}
if (!no_prelude) {
lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ ); lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ );
}
return lib; return lib;
} }
}; };
} // namespace chaiscript }
#endif #endif

View File

@ -1,21 +1,18 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_THREADING_HPP_ #ifndef CHAISCRIPT_THREADING_HPP_
#define CHAISCRIPT_THREADING_HPP_ #define CHAISCRIPT_THREADING_HPP_
#include <unordered_map> #include <unordered_map>
#ifndef CHAISCRIPT_NO_THREADS #ifndef CHAISCRIPT_NO_THREADS
#include <mutex>
#include <shared_mutex>
#include <thread> #include <thread>
#include <mutex>
#else #else
#ifndef CHAISCRIPT_NO_THREADS_WARNING #ifndef CHAISCRIPT_NO_THREADS_WARNING
#pragma message ("ChaiScript is compiling without thread safety.") #pragma message ("ChaiScript is compiling without thread safety.")
@ -32,102 +29,213 @@
/// It also has the side effect that the chaiscript::ChaiScript object may not be accessed from more than /// It also has the side effect that the chaiscript::ChaiScript object may not be accessed from more than
/// one thread simultaneously. /// one thread simultaneously.
namespace chaiscript
{
namespace detail
{
/// If threading is enabled, then this namespace contains std thread classes. /// If threading is enabled, then this namespace contains std thread classes.
/// If threading is not enabled, then stubbed in wrappers that do nothing are provided. /// If threading is not enabled, then stubbed in wrappers that do nothing are provided.
/// This allows us to avoid \#ifdef code in the sections that need thread safety. /// This allows us to avoid \#ifdef code in the sections that need thread safety.
namespace chaiscript::detail::threading { namespace threading
{
#ifndef CHAISCRIPT_NO_THREADS #ifndef CHAISCRIPT_NO_THREADS
template<typename T> template<typename T>
using unique_lock = std::unique_lock<T>; class unique_lock : public std::unique_lock<T>
{
public:
unique_lock(T &t) : std::unique_lock<T>(t) {}
};
template<typename T> template<typename T>
using shared_lock = std::shared_lock<T>; class shared_lock : public std::unique_lock<T>
{
public:
shared_lock(T &t) : std::unique_lock<T>(t) {}
void unlock() {}
};
template<typename T> template<typename T>
using lock_guard = std::lock_guard<T>; class lock_guard : public std::lock_guard<T>
{
public:
lock_guard(T &t) : std::lock_guard<T>(t) {}
};
using std::shared_mutex; class shared_mutex : public std::mutex { };
using std::mutex; using std::mutex;
using std::recursive_mutex; using std::recursive_mutex;
#ifdef CHAISCRIPT_HAS_THREAD_LOCAL
/// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If /// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If
/// threading is not enabled, the class always returns the same data, regardless of which thread it is called from. /// threading is not enabled, the class always returns the same data, regardless of which thread it is called from.
template<typename T> template<typename T>
class Thread_Storage { class Thread_Storage
{
public: public:
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(this); } Thread_Storage(void *t_key)
: m_key(t_key)
{
}
inline const T *operator->() const noexcept { return &(t()[this]); } ~Thread_Storage()
{
t().erase(m_key);
}
inline const T &operator*() const noexcept { return t()[this]; } inline const T *operator->() const
{
return &(t()[m_key]);
}
inline T *operator->() noexcept { return &(t()[this]); } inline const T &operator*() const
{
return t()[m_key];
}
inline T *operator->()
{
return &(t()[m_key]);
}
inline T &operator*()
{
return t()[m_key];
}
inline T &operator*() noexcept { return t()[this]; }
void *m_key; void *m_key;
private: private:
/// todo: is it valid to make this noexcept? The allocation could fail, but if it static std::unordered_map<void*, T> &t()
/// does there is no possible way to recover {
static std::unordered_map<const void *, T> &t() noexcept { thread_local static std::unordered_map<void *, T> my_t;
static thread_local std::unordered_map<const void *, T> my_t;
return my_t; return my_t;
} }
}; };
#else
/// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If
/// threading is not enabled, the class always returns the same data, regardless of which thread it is called from.
///
/// This version is used if the compiler does not support thread_local
template<typename T>
class Thread_Storage
{
public:
Thread_Storage(void *)
{
}
inline const T *operator->() const
{
return get_tls().get();
}
inline const T &operator*() const
{
return *get_tls();
}
inline T *operator->()
{
return get_tls().get();
}
inline T &operator*()
{
return *get_tls();
}
private:
/// \todo this leaks thread instances. It needs to be culled from time to time
std::shared_ptr<T> get_tls() const
{
unique_lock<mutex> lock(m_mutex);
auto itr = m_instances.find(std::this_thread::get_id());
if (itr != m_instances.end()) { return itr->second; }
std::shared_ptr<T> new_instance(std::make_shared<T>());
m_instances.insert(std::make_pair(std::this_thread::get_id(), new_instance));
return new_instance;
}
mutable mutex m_mutex;
mutable std::unordered_map<std::thread::id, std::shared_ptr<T> > m_instances;
};
#endif // threading enabled but no tls
#else // threading disabled #else // threading disabled
template<typename T> template<typename T>
class unique_lock { class unique_lock
{
public: public:
constexpr explicit unique_lock(T &) noexcept {} unique_lock(T &) {}
constexpr void lock() noexcept {} void lock() {}
constexpr void unlock() noexcept {} void unlock() {}
}; };
template<typename T> template<typename T>
class shared_lock { class shared_lock
{
public: public:
constexpr explicit shared_lock(T &) noexcept {} shared_lock(T &) {}
constexpr void lock() noexcept {} void lock() {}
constexpr void unlock() noexcept {} void unlock() {}
}; };
template<typename T> template<typename T>
class lock_guard { class lock_guard
{
public: public:
constexpr explicit lock_guard(T &) noexcept {} lock_guard(T &) {}
}; };
class shared_mutex { class shared_mutex { };
};
class recursive_mutex {};
class recursive_mutex {
};
template<typename T> template<typename T>
class Thread_Storage { class Thread_Storage
{
public: public:
constexpr explicit Thread_Storage() noexcept {} Thread_Storage(void *)
{
}
constexpr inline T *operator->() const noexcept { return &obj; } inline T *operator->() const
{
return &obj;
}
constexpr inline T &operator*() const noexcept { return obj; } inline T &operator*() const
{
return obj;
}
private: private:
mutable T obj; mutable T obj;
}; };
#endif #endif
} // namespace chaiscript::detail::threading }
}
}
#endif #endif

View File

@ -11,48 +11,80 @@
namespace chaiscript { namespace chaiscript {
namespace detail { namespace detail {
namespace exception { namespace exception
{
/// \brief Thrown in the event that an Any cannot be cast to the desired type /// \brief Thrown in the event that an Any cannot be cast to the desired type
/// ///
/// It is used internally during function dispatch. /// It is used internally during function dispatch.
/// ///
/// \sa chaiscript::detail::Any /// \sa chaiscript::detail::Any
class bad_any_cast : public std::bad_cast { class bad_any_cast : public std::bad_cast
{
public: public:
bad_any_cast() CHAISCRIPT_NOEXCEPT
: m_what("bad any cast")
{
}
bad_any_cast(const bad_any_cast &) = default;
virtual ~bad_any_cast() CHAISCRIPT_NOEXCEPT {}
/// \brief Description of what error occurred /// \brief Description of what error occurred
const char *what() const noexcept override { return "bad any cast"; } virtual const char * what() const CHAISCRIPT_NOEXCEPT CHAISCRIPT_OVERRIDE
{
return m_what.c_str();
}
private:
std::string m_what;
}; };
} // namespace exception }
class Any { class Any {
private: private:
struct Data { struct Data
constexpr explicit Data(const std::type_info &t_type) noexcept {
: m_type(t_type) { Data(const std::type_info &t_type)
: m_type(t_type)
{
} }
Data &operator=(const Data &) = delete; Data &operator=(const Data &) = delete;
virtual ~Data() noexcept = default; virtual ~Data() {}
virtual void *data() noexcept = 0; virtual void *data() = 0;
const std::type_info &type() const
const std::type_info &type() const noexcept { return m_type; } {
return m_type;
}
virtual std::unique_ptr<Data> clone() const = 0; virtual std::unique_ptr<Data> clone() const = 0;
const std::type_info &m_type; const std::type_info &m_type;
}; };
template<typename T> template<typename T>
struct Data_Impl : Data { struct Data_Impl : Data
{
explicit Data_Impl(T t_type) explicit Data_Impl(T t_type)
: Data(typeid(T)) : Data(typeid(T)),
, m_data(std::move(t_type)) { m_data(std::move(t_type))
{
} }
void *data() noexcept override { return &m_data; } virtual ~Data_Impl() {}
std::unique_ptr<Data> clone() const override { return std::make_unique<Data_Impl<T>>(m_data); } virtual void *data() CHAISCRIPT_OVERRIDE
{
return &m_data;
}
std::unique_ptr<Data> clone() const CHAISCRIPT_OVERRIDE
{
return std::unique_ptr<Data>(new Data_Impl<T>(m_data));
}
Data_Impl &operator=(const Data_Impl&) = delete; Data_Impl &operator=(const Data_Impl&) = delete;
@ -63,45 +95,71 @@ namespace chaiscript {
public: public:
// construct/copy/destruct // construct/copy/destruct
constexpr Any() noexcept = default; Any() = default;
Any(Any &&) noexcept = default;
Any &operator=(Any &&t_any) = default;
Any(const Any &t_any) Any(const Any &t_any)
: m_data(t_any.empty() ? nullptr : t_any.m_data->clone()) { {
if (!t_any.empty())
{
m_data = t_any.m_data->clone();
} else {
m_data.reset();
}
} }
template<typename ValueType, typename = std::enable_if_t<!std::is_same_v<Any, std::decay_t<ValueType>>>> #if !defined(_MSC_VER) || _MSC_VER != 1800
Any(Any &&) = default;
Any &operator=(Any &&t_any) = default;
#endif
template<typename ValueType,
typename = typename std::enable_if<!std::is_same<Any, typename std::decay<ValueType>::type>::value>::type>
explicit Any(ValueType &&t_value) explicit Any(ValueType &&t_value)
: m_data(std::make_unique<Data_Impl<std::decay_t<ValueType>>>(std::forward<ValueType>(t_value))) { : m_data(std::unique_ptr<Data>(new Data_Impl<typename std::decay<ValueType>::type>(std::forward<ValueType>(t_value))))
{
} }
Any &operator=(const Any &t_any) {
Any & operator=(const Any &t_any)
{
Any copy(t_any); Any copy(t_any);
swap(copy); swap(copy);
return *this; return *this;
} }
template<typename ToType> template<typename ToType>
ToType &cast() const { ToType &cast() const
if (m_data && typeid(ToType) == m_data->type()) { {
if (m_data && typeid(ToType) == m_data->type())
{
return *static_cast<ToType *>(m_data->data()); return *static_cast<ToType *>(m_data->data());
} else { } else {
throw chaiscript::detail::exception::bad_any_cast(); throw chaiscript::detail::exception::bad_any_cast();
} }
} }
~Any()
{
}
// modifiers // modifiers
Any &swap(Any &t_other) { Any & swap(Any &t_other)
{
std::swap(t_other.m_data, m_data); std::swap(t_other.m_data, m_data);
return *this; return *this;
} }
// queries // queries
bool empty() const noexcept { return !static_cast<bool>(m_data); } bool empty() const
{
return !bool(m_data);
}
const std::type_info &type() const noexcept { const std::type_info & type() const
if (m_data) { {
if (m_data)
{
return m_data->type(); return m_data->type();
} else { } else {
return typeid(void); return typeid(void);
@ -109,7 +167,9 @@ namespace chaiscript {
} }
}; };
} // namespace detail }
} // namespace chaiscript }
#endif #endif

View File

@ -1,12 +1,9 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_BAD_BOXED_CAST_HPP_ #ifndef CHAISCRIPT_BAD_BOXED_CAST_HPP_
#define CHAISCRIPT_BAD_BOXED_CAST_HPP_ #define CHAISCRIPT_BAD_BOXED_CAST_HPP_
@ -14,51 +11,59 @@
#include <typeinfo> #include <typeinfo>
#include "../chaiscript_defines.hpp" #include "../chaiscript_defines.hpp"
#include "../utility/static_string.hpp"
#include "type_info.hpp" #include "type_info.hpp"
namespace chaiscript { namespace chaiscript {
class Type_Info; class Type_Info;
} // namespace chaiscript } // namespace chaiscript
namespace chaiscript { namespace chaiscript
namespace exception { {
namespace exception
{
/// \brief Thrown in the event that a Boxed_Value cannot be cast to the desired type /// \brief Thrown in the event that a Boxed_Value cannot be cast to the desired type
/// ///
/// It is used internally during function dispatch and may be used by the end user. /// It is used internally during function dispatch and may be used by the end user.
/// ///
/// \sa chaiscript::boxed_cast /// \sa chaiscript::boxed_cast
class bad_boxed_cast : public std::bad_cast { class bad_boxed_cast : public std::bad_cast
{
public: public:
bad_boxed_cast(Type_Info t_from, const std::type_info &t_to, utility::Static_String t_what) noexcept bad_boxed_cast(Type_Info t_from, const std::type_info &t_to,
: from(t_from) std::string t_what) CHAISCRIPT_NOEXCEPT
, to(&t_to) : from(std::move(t_from)), to(&t_to), m_what(std::move(t_what))
, m_what(std::move(t_what)) { {
} }
bad_boxed_cast(Type_Info t_from, const std::type_info &t_to) noexcept bad_boxed_cast(Type_Info t_from, const std::type_info &t_to)
: from(t_from) : from(std::move(t_from)), to(&t_to), m_what("Cannot perform boxed_cast: " + t_from.name() + " to: " + t_to.name())
, to(&t_to) {
, m_what("Cannot perform boxed_cast") {
} }
explicit bad_boxed_cast(utility::Static_String t_what) noexcept bad_boxed_cast(std::string t_what) CHAISCRIPT_NOEXCEPT
: m_what(std::move(t_what)) { : to(nullptr), m_what(std::move(t_what))
{
} }
bad_boxed_cast(const bad_boxed_cast &) noexcept = default; bad_boxed_cast(const bad_boxed_cast &) = default;
~bad_boxed_cast() noexcept override = default; virtual ~bad_boxed_cast() CHAISCRIPT_NOEXCEPT {}
/// \brief Description of what error occurred /// \brief Description of what error occurred
const char *what() const noexcept override { return m_what.c_str(); } virtual const char * what() const CHAISCRIPT_NOEXCEPT CHAISCRIPT_OVERRIDE
{
return m_what.c_str();
}
Type_Info from; ///< Type_Info contained in the Boxed_Value Type_Info from; ///< Type_Info contained in the Boxed_Value
const std::type_info *to = nullptr; ///< std::type_info of the desired (but failed) result type const std::type_info *to; ///< std::type_info of the desired (but failed) result type
private: private:
utility::Static_String m_what; std::string m_what;
}; };
} // namespace exception }
} // namespace chaiscript }
#endif #endif

View File

@ -1,60 +1,74 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_BIND_FIRST_HPP_ #ifndef CHAISCRIPT_BIND_FIRST_HPP_
#define CHAISCRIPT_BIND_FIRST_HPP_ #define CHAISCRIPT_BIND_FIRST_HPP_
#include <functional> #include <functional>
namespace chaiscript { namespace chaiscript
namespace detail { {
namespace detail
{
template<typename T> template<typename T>
constexpr T *get_pointer(T *t) noexcept { T* get_pointer(T *t)
{
return t; return t;
} }
template<typename T> template<typename T>
T *get_pointer(const std::reference_wrapper<T> &t) noexcept { T* get_pointer(const std::reference_wrapper<T> &t)
{
return &t.get(); return &t.get();
} }
template<typename O, typename Ret, typename P1, typename ... Param> template<typename O, typename Ret, typename P1, typename ... Param>
constexpr auto bind_first(Ret (*f)(P1, Param...), O &&o) { std::function<Ret (Param...)> bind_first(Ret (*f)(P1, Param...), O&& o)
return [f, o = std::forward<O>(o)](Param... param) -> Ret { return f(o, std::forward<Param>(param)...); }; {
return std::function<Ret (Param...)>(
[f, o](Param...param) -> Ret {
return f(std::forward<O>(o), std::forward<Param>(param)...);
}
);
} }
template<typename O, typename Ret, typename Class, typename ... Param> template<typename O, typename Ret, typename Class, typename ... Param>
constexpr auto bind_first(Ret (Class::*f)(Param...), O &&o) { std::function<Ret (Param...)> bind_first(Ret (Class::*f)(Param...), O&& o)
return [f, o = std::forward<O>(o)](Param... param) -> Ret { return (get_pointer(o)->*f)(std::forward<Param>(param)...); }; {
return std::function<Ret (Param...)>(
[f, o](Param...param) -> Ret {
return (get_pointer(o)->*f)(std::forward<Param>(param)...);
}
);
} }
template<typename O, typename Ret, typename Class, typename ... Param> template<typename O, typename Ret, typename Class, typename ... Param>
constexpr auto bind_first(Ret (Class::*f)(Param...) const, O &&o) { std::function<Ret (Param...)> bind_first(Ret (Class::*f)(Param...) const, O&& o)
return [f, o = std::forward<O>(o)](Param... param) -> Ret { return (get_pointer(o)->*f)(std::forward<Param>(param)...); }; {
return std::function<Ret (Param...)>(
[f, o](Param...param) -> Ret {
return (get_pointer(o)->*f)(std::forward<Param>(param)...);
}
);
} }
template<typename O, typename Ret, typename P1, typename ... Param> template<typename O, typename Ret, typename P1, typename ... Param>
auto bind_first(const std::function<Ret(P1, Param...)> &f, O &&o) { std::function<Ret (Param...)> bind_first(const std::function<Ret (P1, Param...)> &f, O&& o)
return [f, o = std::forward<O>(o)](Param... param) -> Ret { return f(o, std::forward<Param>(param)...); }; {
return std::function<Ret (Param...)>(
[f, o](Param...param) -> Ret {
return f(o, std::forward<Param>(param)...);
});
} }
template<typename F, typename O, typename Ret, typename Class, typename P1, typename... Param>
constexpr auto bind_first(const F &fo, O &&o, Ret (Class::*f)(P1, Param...) const) { }
return [fo, o = std::forward<O>(o), f](Param... param) -> Ret { return (fo.*f)(o, std::forward<Param>(param)...); };
} }
template<typename F, typename O>
constexpr auto bind_first(const F &f, O &&o) {
return bind_first(f, std::forward<O>(o), &F::operator());
}
} // namespace detail
} // namespace chaiscript
#endif #endif

View File

@ -1,85 +1,136 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_BOOTSTRAP_HPP_ #ifndef CHAISCRIPT_BOOTSTRAP_HPP_
#define CHAISCRIPT_BOOTSTRAP_HPP_ #define CHAISCRIPT_BOOTSTRAP_HPP_
#include "../utility/utility.hpp" #include <cstdint>
#include <exception>
#include <functional>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>
#include <iterator>
#include "bad_boxed_cast.hpp"
#include "boxed_cast.hpp"
#include "boxed_number.hpp"
#include "boxed_value.hpp"
#include "dispatchkit.hpp"
#include "type_conversions.hpp"
#include "dynamic_object.hpp"
#include "operators.hpp"
#include "proxy_constructors.hpp"
#include "proxy_functions.hpp"
#include "proxy_functions_detail.hpp"
#include "register_function.hpp" #include "register_function.hpp"
#include "type_info.hpp"
#include "../utility/utility.hpp"
#include "../language/chaiscript_common.hpp"
namespace chaiscript
{
/// \brief Classes and functions useful for bootstrapping of ChaiScript and adding of new types /// \brief Classes and functions useful for bootstrapping of ChaiScript and adding of new types
namespace chaiscript::bootstrap { namespace bootstrap
{
namespace detail
{
/// \brief Constructs a new POD value object from a Boxed_Number
/// \param[in] v Boxed_Number to copy into the new object
/// \returns The newly created object.
template<typename P1>
std::shared_ptr<P1> construct_pod(const Boxed_Number &v)
{
return std::make_shared<P1>(v.get_as<P1>());
}
}
template<typename T, typename = typename std::enable_if<std::is_array<T>::value>::type > template<typename T, typename = typename std::enable_if<std::is_array<T>::value>::type >
void array(const std::string &type, Module &m) { ModulePtr array(const std::string &type, ModulePtr m = std::make_shared<Module>())
using ReturnType = typename std::remove_extent<T>::type; {
typedef typename std::remove_extent<T>::type ReturnType;
m.add(user_type<T>(), type); const auto extent = std::extent<T>::value;
m.add(fun([](T &t, size_t index) -> ReturnType & { m->add(user_type<T>(), type);
constexpr const auto extent = std::extent<T>::value; m->add(fun(
[extent](T& t, size_t index)->ReturnType &{
if (extent > 0 && index >= extent) { if (extent > 0 && index >= extent) {
throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent));
+ std::to_string(extent));
} else { } else {
return *std::next(t, static_cast<std::ptrdiff_t>(index)); return t[index];
} }
}), }
"[]"); ), "[]"
);
m.add(fun([](const T &t, size_t index) -> const ReturnType & { m->add(fun(
constexpr const auto extent = std::extent<T>::value; [extent](const T &t, size_t index)->const ReturnType &{
if (extent > 0 && index >= extent) { if (extent > 0 && index >= extent) {
throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent));
+ std::to_string(extent));
} else { } else {
return *std::next(t, static_cast<std::ptrdiff_t>(index)); return t[index];
} }
}), }
"[]"); ), "[]"
);
m.add(fun([](const T &) { return std::extent<T>::value; }), "size"); m->add(fun(
[extent](const T &) {
return extent;
}), "size");
return m;
} }
/// \brief Adds a copy constructor for the given type to the given Model /// \brief Adds a copy constructor for the given type to the given Model
/// \param[in] type The name of the type. The copy constructor will be named "type". /// \param[in] type The name of the type. The copy constructor will be named "type".
/// \param[in,out] m The Module to add the copy constructor to /// \param[in,out] m The Module to add the copy constructor to
/// \tparam T The type to add a copy constructor for /// \tparam T The type to add a copy constructor for
/// \returns The passed in Module /// \returns The passed in ModulePtr, or the newly constructed one if the default param is used
template<typename T> template<typename T>
void copy_constructor(const std::string &type, Module &m) { ModulePtr copy_constructor(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(constructor<T(const T &)>(), type); {
m->add(constructor<T (const T &)>(), type);
return m;
} }
/// \brief Add all comparison operators for the templated type. Used during bootstrap, also available to users. /// \brief Add all comparison operators for the templated type. Used during bootstrap, also available to users.
/// \tparam T Type to create comparison operators for /// \tparam T Type to create comparison operators for
/// \param[in,out] m module to add comparison operators to /// \param[in,out] m module to add comparison operators to
/// \returns the passed in Module. /// \returns the passed in ModulePtr or the newly constructed one if the default params are used.
template<typename T> template<typename T>
void opers_comparison(Module &m) { ModulePtr opers_comparison(ModulePtr m = std::make_shared<Module>())
{
operators::equal<T>(m); operators::equal<T>(m);
operators::greater_than<T>(m); operators::greater_than<T>(m);
operators::greater_than_equal<T>(m); operators::greater_than_equal<T>(m);
operators::less_than<T>(m); operators::less_than<T>(m);
operators::less_than_equal<T>(m); operators::less_than_equal<T>(m);
operators::not_equal<T>(m); operators::not_equal<T>(m);
return m;
} }
/// \brief Adds default and copy constructors for the given type /// \brief Adds default and copy constructors for the given type
/// \param[in] type The name of the type to add the constructors for. /// \param[in] type The name of the type to add the constructors for.
/// \param[in,out] m The Module to add the basic constructors to /// \param[in,out] m The Module to add the basic constructors to
/// \tparam T Type to generate basic constructors for /// \tparam T Type to generate basic constructors for
/// \returns The passed in Module /// \returns The passed in ModulePtr, or the newly constructed one if the default param is used
/// \sa copy_constructor /// \sa copy_constructor
/// \sa constructor /// \sa constructor
template<typename T> template<typename T>
void basic_constructors(const std::string &type, Module &m) { ModulePtr basic_constructors(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(constructor<T()>(), type); {
m->add(constructor<T ()>(), type);
copy_constructor<T>(type, m); copy_constructor<T>(type, m);
return m;
} }
/// \brief Adds a constructor for a POD type /// \brief Adds a constructor for a POD type
@ -87,60 +138,94 @@ namespace chaiscript::bootstrap {
/// \param[in] type The name of the type /// \param[in] type The name of the type
/// \param[in,out] m The Module to add the constructor to /// \param[in,out] m The Module to add the constructor to
template<typename T> template<typename T>
void construct_pod(const std::string &type, Module &m) { ModulePtr construct_pod(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(fun([](const Boxed_Number &bn) { return bn.get_as<T>(); }), type); {
m->add(fun(&detail::construct_pod<T>), type);
return m;
}
/// to_string function for internal use. Uses ostream operator<<
template<typename Input>
std::string to_string(Input i)
{
std::stringstream ss;
ss << i;
return ss.str();
} }
/// Internal function for converting from a string to a value /// Internal function for converting from a string to a value
/// uses ostream operator >> to perform the conversion /// uses ostream operator >> to perform the conversion
template<typename Input> template<typename Input>
Input parse_string(const std::string &i) { auto parse_string(const std::string &i)
if constexpr (!std::is_same<Input, wchar_t>::value && !std::is_same<Input, char16_t>::value && !std::is_same<Input, char32_t>::value) { -> typename std::enable_if<
!std::is_same<Input, wchar_t>::value
&& !std::is_same<Input, char16_t>::value
&& !std::is_same<Input, char32_t>::value,
Input>::type
{
std::stringstream ss(i); std::stringstream ss(i);
Input t; Input t;
ss >> t; ss >> t;
return t; return t;
} else { }
template<typename Input>
auto parse_string(const std::string &)
-> typename std::enable_if<
std::is_same<Input, wchar_t>::value
|| std::is_same<Input, char16_t>::value
|| std::is_same<Input, char32_t>::value,
Input>::type
{
throw std::runtime_error("Parsing of wide characters is not yet supported"); throw std::runtime_error("Parsing of wide characters is not yet supported");
} }
}
/// Add all common functions for a POD type. All operators, and /// Add all common functions for a POD type. All operators, and
/// common conversions /// common conversions
template<typename T> template<typename T>
void bootstrap_pod_type(const std::string &name, Module &m) { ModulePtr bootstrap_pod_type(const std::string &name, ModulePtr m = std::make_shared<Module>())
m.add(user_type<T>(), name); {
m.add(constructor<T()>(), name); m->add(user_type<T>(), name);
m->add(constructor<T()>(), name);
construct_pod<T>(name, m); construct_pod<T>(name, m);
m.add(fun(&parse_string<T>), "to_" + name); m->add(fun(&parse_string<T>), "to_" + name);
m.add(fun([](const T t) { return t; }), "to_" + name); return m;
m.add(fun([](const Boxed_Number &bn) { return bn.get_as<T>(); }), "to_" + name);
m.add(fun(&parse_string<T>), name);
} }
/// "clone" function for a shared_ptr type. This is used in the case /// "clone" function for a shared_ptr type. This is used in the case
/// where you do not want to make a deep copy of an object during cloning /// where you do not want to make a deep copy of an object during cloning
/// but want to instead maintain the shared_ptr. It is needed internally /// but want to instead maintain the shared_ptr. It is needed internally
/// for handling of Proxy_Function object (that is, /// for handling of Proxy_Function object (that is,
/// function variables. /// function variables.
template<typename Type> template<typename Type>
auto shared_ptr_clone(const std::shared_ptr<Type> &p) { std::shared_ptr<Type> shared_ptr_clone(const std::shared_ptr<Type> &p)
{
return p; return p;
} }
/// Specific version of shared_ptr_clone just for Proxy_Functions /// Specific version of shared_ptr_clone just for Proxy_Functions
template<typename Type> template<typename Type>
std::shared_ptr<std::remove_const_t<Type>> shared_ptr_unconst_clone(const std::shared_ptr<std::add_const_t<Type>> &p) { std::shared_ptr<typename std::remove_const<Type>::type>
shared_ptr_unconst_clone(const std::shared_ptr<typename std::add_const<Type>::type> &p)
{
return std::const_pointer_cast<typename std::remove_const<Type>::type>(p); return std::const_pointer_cast<typename std::remove_const<Type>::type>(p);
} }
/// Assignment function for shared_ptr objects, does not perform a copy of the /// Assignment function for shared_ptr objects, does not perform a copy of the
/// object pointed to, instead maintains the shared_ptr concept. /// object pointed to, instead maintains the shared_ptr concept.
/// Similar to shared_ptr_clone. Used for Proxy_Function. /// Similar to shared_ptr_clone. Used for Proxy_Function.
template<typename Type> template<typename Type>
Boxed_Value ptr_assign(Boxed_Value lhs, const std::shared_ptr<Type> &rhs) { Boxed_Value ptr_assign(Boxed_Value lhs, const std::shared_ptr<Type> &rhs)
if (lhs.is_undef() || (!lhs.get_type_info().is_const() && lhs.get_type_info().bare_equal(chaiscript::detail::Get_Type_Info<Type>::get()))) { {
if (lhs.is_undef()
|| (!lhs.get_type_info().is_const() && lhs.get_type_info().bare_equal(chaiscript::detail::Get_Type_Info<Type>::get())))
{
lhs.assign(Boxed_Value(rhs)); lhs.assign(Boxed_Value(rhs));
return lhs; return lhs;
} else { } else {
@ -150,110 +235,156 @@ namespace chaiscript::bootstrap {
/// Class consisting of only static functions. All default bootstrapping occurs /// Class consisting of only static functions. All default bootstrapping occurs
/// from this class. /// from this class.
class Bootstrap { class Bootstrap
{
private: private:
/// Function allowing for assignment of an unknown type to any other value /// Function allowing for assignment of an unknown type to any other value
static Boxed_Value unknown_assign(Boxed_Value lhs, Boxed_Value rhs) { static Boxed_Value unknown_assign(Boxed_Value lhs, Boxed_Value rhs)
if (lhs.is_undef()) { {
if (lhs.is_undef())
{
return (lhs.assign(rhs)); return (lhs.assign(rhs));
} else { } else {
throw exception::bad_boxed_cast("boxed_value has a set type already"); throw exception::bad_boxed_cast("boxed_value has a set type already");
} }
} }
static void print(const std::string &s) noexcept { fwrite(s.c_str(), 1, s.size(), stdout); } static void print(const std::string &s)
{
fwrite(s.c_str(), 1, s.size(), stdout);
}
static void println(const std::string &s)
{
puts(s.c_str());
}
static void println(const std::string &s) noexcept { puts(s.c_str()); }
/// Add all arithmetic operators for PODs /// Add all arithmetic operators for PODs
static void opers_arithmetic_pod(Module &m) { static void opers_arithmetic_pod(ModulePtr m = std::make_shared<Module>())
m.add(fun(&Boxed_Number::equals), "=="); {
m.add(fun(&Boxed_Number::less_than), "<"); m->add(fun(&Boxed_Number::equals), "==");
m.add(fun(&Boxed_Number::greater_than), ">"); m->add(fun(&Boxed_Number::less_than), "<");
m.add(fun(&Boxed_Number::greater_than_equal), ">="); m->add(fun(&Boxed_Number::greater_than), ">");
m.add(fun(&Boxed_Number::less_than_equal), "<="); m->add(fun(&Boxed_Number::greater_than_equal), ">=");
m.add(fun(&Boxed_Number::not_equal), "!="); m->add(fun(&Boxed_Number::less_than_equal), "<=");
m->add(fun(&Boxed_Number::not_equal), "!=");
m->add(fun(&Boxed_Number::pre_decrement), "--");
m->add(fun(&Boxed_Number::pre_increment), "++");
m->add(fun(&Boxed_Number::sum), "+");
m->add(fun(&Boxed_Number::unary_plus), "+");
m->add(fun(&Boxed_Number::unary_minus), "-");
m->add(fun(&Boxed_Number::difference), "-");
m->add(fun(&Boxed_Number::assign_bitwise_and), "&=");
m->add(fun(&Boxed_Number::assign), "=");
m->add(fun(&Boxed_Number::assign_bitwise_or), "|=");
m->add(fun(&Boxed_Number::assign_bitwise_xor), "^=");
m->add(fun(&Boxed_Number::assign_remainder), "%=");
m->add(fun(&Boxed_Number::assign_shift_left), "<<=");
m->add(fun(&Boxed_Number::assign_shift_right), ">>=");
m->add(fun(&Boxed_Number::bitwise_and), "&");
m->add(fun(&Boxed_Number::bitwise_complement), "~");
m->add(fun(&Boxed_Number::bitwise_xor), "^");
m->add(fun(&Boxed_Number::bitwise_or), "|");
m->add(fun(&Boxed_Number::assign_product), "*=");
m->add(fun(&Boxed_Number::assign_quotient), "/=");
m->add(fun(&Boxed_Number::assign_sum), "+=");
m->add(fun(&Boxed_Number::assign_difference), "-=");
m->add(fun(&Boxed_Number::quotient), "/");
m->add(fun(&Boxed_Number::shift_left), "<<");
m->add(fun(&Boxed_Number::product), "*");
m->add(fun(&Boxed_Number::remainder), "%");
m->add(fun(&Boxed_Number::shift_right), ">>");
m.add(fun(&Boxed_Number::pre_decrement), "--");
m.add(fun(&Boxed_Number::pre_increment), "++");
m.add(fun(&Boxed_Number::sum), "+");
m.add(fun(&Boxed_Number::unary_plus), "+");
m.add(fun(&Boxed_Number::unary_minus), "-");
m.add(fun(&Boxed_Number::difference), "-");
m.add(fun(&Boxed_Number::assign_bitwise_and), "&=");
m.add(fun(&Boxed_Number::assign), "=");
m.add(fun(&Boxed_Number::assign_bitwise_or), "|=");
m.add(fun(&Boxed_Number::assign_bitwise_xor), "^=");
m.add(fun(&Boxed_Number::assign_remainder), "%=");
m.add(fun(&Boxed_Number::assign_shift_left), "<<=");
m.add(fun(&Boxed_Number::assign_shift_right), ">>=");
m.add(fun(&Boxed_Number::bitwise_and), "&");
m.add(fun(&Boxed_Number::bitwise_complement), "~");
m.add(fun(&Boxed_Number::bitwise_xor), "^");
m.add(fun(&Boxed_Number::bitwise_or), "|");
m.add(fun(&Boxed_Number::assign_product), "*=");
m.add(fun(&Boxed_Number::assign_quotient), "/=");
m.add(fun(&Boxed_Number::assign_sum), "+=");
m.add(fun(&Boxed_Number::assign_difference), "-=");
m.add(fun(&Boxed_Number::quotient), "/");
m.add(fun(&Boxed_Number::shift_left), "<<");
m.add(fun(&Boxed_Number::product), "*");
m.add(fun(&Boxed_Number::remainder), "%");
m.add(fun(&Boxed_Number::shift_right), ">>");
} }
/// Create a bound function object. The first param is the function to bind /// Create a bound function object. The first param is the function to bind
/// the remaining parameters are the args to bind into the result /// the remaining parameters are the args to bind into the result
static Boxed_Value bind_function(const Function_Params &params) { static Boxed_Value bind_function(const std::vector<Boxed_Value> &params)
{
if (params.empty()) { if (params.empty()) {
throw exception::arity_error(0, 1); throw exception::arity_error(0, 1);
} }
Const_Proxy_Function f = boxed_cast<Const_Proxy_Function>(params[0]); Const_Proxy_Function f = boxed_cast<Const_Proxy_Function>(params[0]);
if (f->get_arity() != -1 && size_t(f->get_arity()) != params.size() - 1) { if (f->get_arity() != -1 && size_t(f->get_arity()) != params.size() - 1)
{
throw exception::arity_error(static_cast<int>(params.size()), f->get_arity()); throw exception::arity_error(static_cast<int>(params.size()), f->get_arity());
} }
return Boxed_Value(Const_Proxy_Function( return Boxed_Value(Const_Proxy_Function(std::make_shared<dispatch::Bound_Function>(std::move(f),
std::make_shared<dispatch::Bound_Function>(std::move(f), std::vector<Boxed_Value>(params.begin() + 1, params.end())))); std::vector<Boxed_Value>(params.begin() + 1, params.end()))));
} }
static bool has_guard(const Const_Proxy_Function &t_pf) noexcept {
static bool has_guard(const Const_Proxy_Function &t_pf)
{
auto pf = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_pf); auto pf = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_pf);
return pf && pf->has_guard(); return pf && pf->get_guard();
} }
static Const_Proxy_Function get_guard(const Const_Proxy_Function &t_pf) { static Const_Proxy_Function get_guard(const Const_Proxy_Function &t_pf)
{
const auto pf = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_pf); const auto pf = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_pf);
if (pf && pf->get_guard()) { if (pf && pf->get_guard())
{
return pf->get_guard(); return pf->get_guard();
} else { } else {
throw std::runtime_error("Function does not have a guard"); throw std::runtime_error("Function does not have a guard");
} }
} }
static void throw_exception(const Boxed_Value &bv) {
throw bv;
}
static std::string what(const std::exception &e)
{
return e.what();
}
/// Boolean specialization of internal to_string function
static std::string bool_to_string(bool b)
{
if (b)
{
return "true";
} else {
return "false";
}
}
template<typename FunctionType> template<typename FunctionType>
static std::vector<Boxed_Value> do_return_boxed_value_vector(FunctionType f, const dispatch::Proxy_Function_Base *b) { static std::vector<Boxed_Value> do_return_boxed_value_vector(FunctionType f,
const dispatch::Proxy_Function_Base *b)
{
auto v = (b->*f)(); auto v = (b->*f)();
std::vector<Boxed_Value> vbv; std::vector<Boxed_Value> vbv;
for (const auto &o : v) { for (const auto &o: v)
{
vbv.push_back(const_var(o)); vbv.push_back(const_var(o));
} }
return vbv; return vbv;
} }
static bool has_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) noexcept {
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); const auto pf = std::dynamic_pointer_cast<const chaiscript::dispatch::Dynamic_Proxy_Function>(t_pf);
return bool(pf); return pf && pf->get_parse_tree();
} }
static const chaiscript::AST_Node &get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) { static chaiscript::AST_NodePtr 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); const auto pf = std::dynamic_pointer_cast<const chaiscript::dispatch::Dynamic_Proxy_Function>(t_pf);
if (pf) { if (pf && pf->get_parse_tree())
{
return pf->get_parse_tree(); return pf->get_parse_tree();
} else { } else {
throw std::runtime_error("Function does not have a parse tree"); throw std::runtime_error("Function does not have a parse tree");
@ -261,145 +392,120 @@ namespace chaiscript::bootstrap {
} }
template<typename Function> template<typename Function>
static auto return_boxed_value_vector(const Function &f) { static std::function<std::vector<Boxed_Value> (const dispatch::Proxy_Function_Base*)> return_boxed_value_vector(const Function &f)
return [f](const dispatch::Proxy_Function_Base *b) { return do_return_boxed_value_vector(f, b); }; {
return [f](const dispatch::Proxy_Function_Base *b) {
return do_return_boxed_value_vector(f, b);
};
} }
public: public:
/// \brief perform all common bootstrap functions for std::string, void and POD types /// \brief perform all common bootstrap functions for std::string, void and POD types
/// \param[in,out] m Module to add bootstrapped functions to /// \param[in,out] m Module to add bootstrapped functions to
/// \param[in] t_no_io If true, skip registering print_string and println_string /// \returns passed in ModulePtr, or newly created one if default argument is used
static void bootstrap(Module &m) { static ModulePtr bootstrap(ModulePtr m = std::make_shared<Module>())
m.add(user_type<void>(), "void"); {
m.add(user_type<bool>(), "bool"); m->add(user_type<void>(), "void");
m.add(user_type<Boxed_Value>(), "Object"); m->add(user_type<bool>(), "bool");
m.add(user_type<Boxed_Number>(), "Number"); m->add(user_type<Boxed_Value>(), "Object");
m.add(user_type<Proxy_Function>(), "Function"); m->add(user_type<Boxed_Number>(), "Number");
m.add(user_type<dispatch::Assignable_Proxy_Function>(), "Assignable_Function"); m->add(user_type<Proxy_Function>(), "Function");
m.add(user_type<std::exception>(), "exception"); m->add(user_type<dispatch::Assignable_Proxy_Function>(), "Assignable_Function");
m->add(user_type<std::exception>(), "exception");
m.add(fun(&dispatch::Proxy_Function_Base::get_arity), "get_arity"); m->add(fun(&dispatch::Proxy_Function_Base::get_arity), "get_arity");
m.add(fun(&dispatch::Proxy_Function_Base::operator==), "=="); m->add(fun(&dispatch::Proxy_Function_Base::annotation), "get_annotation");
m->add(fun(&dispatch::Proxy_Function_Base::operator==), "==");
m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_param_types)), "get_param_types");
m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_contained_functions)), "get_contained_functions");
m.add(fun([](const std::exception &e) { return std::string(e.what()); }), "what"); m->add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_param_types)), "get_param_types");
m->add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_contained_functions)), "get_contained_functions");
m.add(user_type<std::out_of_range>(), "out_of_range");
m.add(user_type<std::logic_error>(), "logic_error");
m.add(chaiscript::base_class<std::exception, std::logic_error>());
m.add(chaiscript::base_class<std::logic_error, std::out_of_range>());
m.add(chaiscript::base_class<std::exception, std::out_of_range>());
m.add(user_type<std::runtime_error>(), "runtime_error"); m->add(user_type<std::out_of_range>(), "out_of_range");
m.add(chaiscript::base_class<std::exception, std::runtime_error>()); m->add(user_type<std::logic_error>(), "logic_error");
m->add(chaiscript::base_class<std::exception, std::logic_error>());
m->add(chaiscript::base_class<std::logic_error, std::out_of_range>());
m->add(chaiscript::base_class<std::exception, std::out_of_range>());
m.add(constructor<std::runtime_error(const std::string &)>(), "runtime_error"); m->add(user_type<std::runtime_error>(), "runtime_error");
m->add(chaiscript::base_class<std::exception, std::runtime_error>());
m.add(user_type<dispatch::Dynamic_Object>(), "Dynamic_Object"); m->add(constructor<std::runtime_error (const std::string &)>(), "runtime_error");
m.add(constructor<dispatch::Dynamic_Object(const std::string &)>(), "Dynamic_Object"); m->add(fun(std::function<std::string (const std::runtime_error &)>(&what)), "what");
m.add(constructor<dispatch::Dynamic_Object()>(), "Dynamic_Object");
m.add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name");
m.add(fun(&dispatch::Dynamic_Object::get_attrs), "get_attrs");
m.add(fun(&dispatch::Dynamic_Object::set_explicit), "set_explicit");
m.add(fun(&dispatch::Dynamic_Object::is_explicit), "is_explicit");
m.add(fun(&dispatch::Dynamic_Object::has_attr), "has_attr");
m.add(fun(static_cast<Boxed_Value &(dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::get_attr)), "get_attr"); m->add(user_type<dispatch::Dynamic_Object>(), "Dynamic_Object");
m.add(fun(static_cast<const Boxed_Value &(dispatch::Dynamic_Object::*)(const std::string &) const>(&dispatch::Dynamic_Object::get_attr)), m->add(constructor<dispatch::Dynamic_Object (const std::string &)>(), "Dynamic_Object");
"get_attr"); m->add(constructor<dispatch::Dynamic_Object ()>(), "Dynamic_Object");
m->add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name");
m->add(fun(&dispatch::Dynamic_Object::get_attrs), "get_attrs");
m->add(fun(&dispatch::Dynamic_Object::set_explicit), "set_explicit");
m->add(fun(&dispatch::Dynamic_Object::is_explicit), "is_explicit");
m.add(fun(static_cast<Boxed_Value &(dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::method_missing)), m->add(fun(static_cast<Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::get_attr)), "get_attr");
"method_missing"); m->add(fun(static_cast<const Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &) const>(&dispatch::Dynamic_Object::get_attr)), "get_attr");
m.add(fun(static_cast<const Boxed_Value &(dispatch::Dynamic_Object::*)(const std::string &) const>(
&dispatch::Dynamic_Object::method_missing)),
"method_missing");
m.add(fun(static_cast<Boxed_Value &(dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::get_attr)), "[]"); m->add(fun(static_cast<Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::method_missing)), "method_missing");
m.add(fun(static_cast<const Boxed_Value &(dispatch::Dynamic_Object::*)(const std::string &) const>(&dispatch::Dynamic_Object::get_attr)), m->add(fun(static_cast<const Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &) const>(&dispatch::Dynamic_Object::method_missing)), "method_missing");
"[]");
m.eval(R"chaiscript( m->add(fun(static_cast<Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::get_attr)), "[]");
m->add(fun(static_cast<const Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &) const>(&dispatch::Dynamic_Object::get_attr)), "[]");
m->eval(R""(
def Dynamic_Object::clone() { def Dynamic_Object::clone() {
auto &new_o = Dynamic_Object(this.get_type_name()); auto &new_o = Dynamic_Object(this.get_type_name());
for_each(this.get_attrs(), fun[new_o](x) { new_o.get_attr(x.first) = x.second; } ); for_each(this.get_attrs(), fun[new_o](x) { new_o.get_attr(x.first) = x.second; } );
new_o; new_o;
} }
)"");
def `=`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name() m->add(fun(&has_guard), "has_guard");
{ m->add(fun(&get_guard), "get_guard");
for_each(rhs.get_attrs(), fun[lhs](x) { lhs.get_attr(x.first) = clone(x.second); } );
}
def `!=`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name() m->add(fun(&Boxed_Value::is_undef), "is_var_undef");
{ m->add(fun(&Boxed_Value::is_null), "is_var_null");
var rhs_attrs := rhs.get_attrs(); m->add(fun(&Boxed_Value::is_const), "is_var_const");
var lhs_attrs := lhs.get_attrs(); m->add(fun(&Boxed_Value::is_ref), "is_var_reference");
m->add(fun(&Boxed_Value::is_pointer), "is_var_pointer");
m->add(fun(&Boxed_Value::is_return_value), "is_var_return_value");
m->add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value");
m->add(fun(&Boxed_Value::is_type), "is_type");
m->add(fun(&Boxed_Value::get_attr), "get_var_attr");
m->add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs");
m->add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs");
if (rhs_attrs.size() != lhs_attrs.size()) { m->add(fun(&Boxed_Value::get_type_info), "get_type_info");
true; m->add(user_type<Type_Info>(), "Type_Info");
} else { m->add(constructor<Type_Info (const Type_Info &)>(), "Type_Info");
return any_of(rhs_attrs, fun[lhs](x) { !lhs.has_attr(x.first) || lhs.get_attr(x.first) != x.second; } );
}
}
def `==`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name()
{
var rhs_attrs := rhs.get_attrs();
var lhs_attrs := lhs.get_attrs();
if (rhs_attrs.size() != lhs_attrs.size()) {
false;
} else {
return all_of(rhs_attrs, fun[lhs](x) { lhs.has_attr(x.first) && lhs.get_attr(x.first) == x.second; } );
}
}
)chaiscript");
m.add(fun(&has_guard), "has_guard");
m.add(fun(&get_guard), "get_guard");
m.add(fun(&Boxed_Value::is_undef), "is_var_undef");
m.add(fun(&Boxed_Value::is_null), "is_var_null");
m.add(fun(&Boxed_Value::is_const), "is_var_const");
m.add(fun(&Boxed_Value::is_ref), "is_var_reference");
m.add(fun(&Boxed_Value::is_pointer), "is_var_pointer");
m.add(fun(&Boxed_Value::is_return_value), "is_var_return_value");
m.add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value");
m.add(fun(&Boxed_Value::is_type), "is_type");
m.add(fun(&Boxed_Value::get_attr), "get_var_attr");
m.add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs");
m.add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs");
m.add(fun(&Boxed_Value::get_type_info), "get_type_info");
m.add(user_type<Type_Info>(), "Type_Info");
m.add(constructor<Type_Info(const Type_Info &)>(), "Type_Info");
operators::equal<Type_Info>(m); operators::equal<Type_Info>(m);
m.add(fun(&Type_Info::is_const), "is_type_const"); m->add(fun(&Type_Info::is_const), "is_type_const");
m.add(fun(&Type_Info::is_reference), "is_type_reference"); m->add(fun(&Type_Info::is_reference), "is_type_reference");
m.add(fun(&Type_Info::is_void), "is_type_void"); m->add(fun(&Type_Info::is_void), "is_type_void");
m.add(fun(&Type_Info::is_undef), "is_type_undef"); m->add(fun(&Type_Info::is_undef), "is_type_undef");
m.add(fun(&Type_Info::is_pointer), "is_type_pointer"); m->add(fun(&Type_Info::is_pointer), "is_type_pointer");
m.add(fun(&Type_Info::is_arithmetic), "is_type_arithmetic"); m->add(fun(&Type_Info::is_arithmetic), "is_type_arithmetic");
m.add(fun(&Type_Info::name), "cpp_name"); m->add(fun(&Type_Info::name), "cpp_name");
m.add(fun(&Type_Info::bare_name), "cpp_bare_name"); m->add(fun(&Type_Info::bare_name), "cpp_bare_name");
m.add(fun(&Type_Info::bare_equal), "bare_equal"); m->add(fun(&Type_Info::bare_equal), "bare_equal");
basic_constructors<bool>("bool", m); basic_constructors<bool>("bool", m);
operators::assign<bool>(m); operators::assign<bool>(m);
operators::equal<bool>(m); operators::equal<bool>(m);
operators::not_equal<bool>(m); operators::not_equal<bool>(m);
m.add(fun([](const std::string &s) { return s; }), "to_string"); m->add(fun([](const std::string &s) -> std::string { return s; }), "to_string");
m.add(fun([](const bool b) { return std::string(b ? "true" : "false"); }), "to_string"); m->add(fun(&Bootstrap::bool_to_string), "to_string");
m.add(fun(&unknown_assign), "="); m->add(fun(&unknown_assign), "=");
m.add(fun([](const Boxed_Value &bv) { throw bv; }), "throw"); m->add(fun(&throw_exception), "throw");
m->add(fun(&what), "what");
m.add(fun([](const char c) { return std::string(1, c); }), "to_string"); m->add(fun(&to_string<char>), "to_string");
m.add(fun(&Boxed_Number::to_string), "to_string"); m->add(fun(&Boxed_Number::to_string), "to_string");
bootstrap_pod_type<double>("double", m); bootstrap_pod_type<double>("double", m);
bootstrap_pod_type<long double>("long_double", m); bootstrap_pod_type<long double>("long_double", m);
@ -428,63 +534,63 @@ namespace chaiscript::bootstrap {
opers_arithmetic_pod(m); 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");
m.add(fun(&Build_Info::version), "version");
m.add(fun(&Build_Info::compiler_version), "compiler_version");
m.add(fun(&Build_Info::compiler_name), "compiler_name");
m.add(fun(&Build_Info::compiler_id), "compiler_id");
m.add(fun(&Build_Info::debug_build), "debug_build");
m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind"); m->add(fun(&print), "print_string");
m->add(fun(&println), "println_string");
m.add(fun(&shared_ptr_unconst_clone<dispatch::Proxy_Function_Base>), "clone"); m->add(dispatch::make_dynamic_proxy_function(&bind_function), "bind");
m.add(fun(&ptr_assign<std::remove_const<dispatch::Proxy_Function_Base>::type>), "=");
m.add(fun(&ptr_assign<std::add_const<dispatch::Proxy_Function_Base>::type>), "="); m->add(fun(&shared_ptr_unconst_clone<dispatch::Proxy_Function_Base>), "clone");
m.add(chaiscript::base_class<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function>()); m->add(fun(&ptr_assign<std::remove_const<dispatch::Proxy_Function_Base>::type>), "=");
m.add(fun([](dispatch::Assignable_Proxy_Function &t_lhs, const std::shared_ptr<const dispatch::Proxy_Function_Base> &t_rhs) { m->add(fun(&ptr_assign<std::add_const<dispatch::Proxy_Function_Base>::type>), "=");
m->add(chaiscript::base_class<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function>());
m->add(fun(
[](dispatch::Assignable_Proxy_Function &t_lhs, const std::shared_ptr<const dispatch::Proxy_Function_Base> &t_rhs) {
t_lhs.assign(t_rhs); t_lhs.assign(t_rhs);
}), }
"="); ), "="
);
m.add(fun(&Boxed_Value::type_match), "type_match"); m->add(fun(&Boxed_Value::type_match), "type_match");
m.add(chaiscript::fun(&has_parse_tree), "has_parse_tree");
m.add(chaiscript::fun(&get_parse_tree), "get_parse_tree");
m.add(chaiscript::base_class<std::runtime_error, chaiscript::exception::eval_error>()); m->add(chaiscript::fun(&has_parse_tree), "has_parse_tree");
m.add(chaiscript::base_class<std::exception, chaiscript::exception::eval_error>()); m->add(chaiscript::fun(&get_parse_tree), "get_parse_tree");
m.add(chaiscript::user_type<chaiscript::exception::arithmetic_error>(), "arithmetic_error"); m->add(chaiscript::base_class<std::runtime_error, chaiscript::exception::eval_error>());
m.add(chaiscript::base_class<std::runtime_error, chaiscript::exception::arithmetic_error>());
m.add(chaiscript::base_class<std::exception, chaiscript::exception::arithmetic_error>());
// chaiscript::bootstrap::standard_library::vector_type<std::vector<std::shared_ptr<chaiscript::AST_Node> > m->add(chaiscript::user_type<chaiscript::exception::arithmetic_error>(), "arithmetic_error");
// >("AST_NodeVector", m); m->add(chaiscript::base_class<std::runtime_error, chaiscript::exception::arithmetic_error>());
chaiscript::utility::add_class<chaiscript::exception::eval_error>(m,
// chaiscript::bootstrap::standard_library::vector_type<std::vector<std::shared_ptr<chaiscript::AST_Node> > >("AST_NodeVector", m);
chaiscript::utility::add_class<chaiscript::exception::eval_error>(*m,
"eval_error", "eval_error",
{ }, { },
{ {fun(&chaiscript::exception::eval_error::reason), "reason"}, { {fun(&chaiscript::exception::eval_error::reason), "reason"},
{fun(&chaiscript::exception::eval_error::pretty_print), "pretty_print"}, {fun(&chaiscript::exception::eval_error::pretty_print), "pretty_print"},
{fun([](const chaiscript::exception::eval_error &t_eval_error) { {fun(std::function<std::vector<Boxed_Value> (const chaiscript::exception::eval_error &t_eval_error)>([](const chaiscript::exception::eval_error &t_eval_error) -> std::vector<Boxed_Value> {
std::vector<Boxed_Value> retval; std::vector<Boxed_Value> retval;
std::transform(t_eval_error.call_stack.begin(), std::transform(t_eval_error.call_stack.begin(), t_eval_error.call_stack.end(),
t_eval_error.call_stack.end(),
std::back_inserter(retval), std::back_inserter(retval),
&chaiscript::var<const chaiscript::AST_Node_Trace &>); &chaiscript::var<const std::shared_ptr<const chaiscript::AST_Node> &>);
return retval; return retval;
}), })), "call_stack"} }
"call_stack"}}); );
chaiscript::utility::add_class<chaiscript::File_Position>(m,
chaiscript::utility::add_class<chaiscript::File_Position>(*m,
"File_Position", "File_Position",
{constructor<File_Position()>(), constructor<File_Position(int, int)>()}, { constructor<File_Position()>(),
constructor<File_Position(int, int)>() },
{ {fun(&File_Position::line), "line"}, { {fun(&File_Position::line), "line"},
{fun(&File_Position::column), "column"}}); {fun(&File_Position::column), "column"} }
);
chaiscript::utility::add_class<AST_Node>(m,
chaiscript::utility::add_class<AST_Node>(*m,
"AST_Node", "AST_Node",
{ }, { },
{ {fun(&AST_Node::text), "text"}, { {fun(&AST_Node::text), "text"},
@ -493,18 +599,32 @@ namespace chaiscript::bootstrap {
{fun(&AST_Node::start), "start"}, {fun(&AST_Node::start), "start"},
{fun(&AST_Node::end), "end"}, {fun(&AST_Node::end), "end"},
{fun(&AST_Node::to_string), "to_string"}, {fun(&AST_Node::to_string), "to_string"},
{fun([](const chaiscript::AST_Node &t_node) -> std::vector<Boxed_Value> { {fun(std::function<std::vector<Boxed_Value> (const chaiscript::AST_Node &t_node)>([](const chaiscript::AST_Node &t_node) -> std::vector<Boxed_Value> {
std::vector<Boxed_Value> retval; std::vector<Boxed_Value> retval;
const auto children = t_node.get_children(); std::transform(t_node.children.begin(), t_node.children.end(),
std::transform(children.begin(),
children.end(),
std::back_inserter(retval), std::back_inserter(retval),
&chaiscript::var<const std::reference_wrapper<chaiscript::AST_Node> &>); &chaiscript::var<const std::shared_ptr<chaiscript::AST_Node> &>);
return retval; return retval;
}), })), "children"},
"children"}}); {fun(&AST_Node::replace_child), "replace_child"}
}
);
chaiscript::utility::add_class<parser::ChaiScript_Parser>(*m,
"ChaiScript_Parser",
{ constructor<parser::ChaiScript_Parser ()>() },
{ {fun(&parser::ChaiScript_Parser::parse), "parse"},
{fun(&parser::ChaiScript_Parser::ast), "ast"} }
);
return m;
} }
}; };
} // namespace chaiscript::bootstrap }
}
#endif #endif

View File

@ -1,24 +1,21 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
/// \file /// \file
/// This file contains utility functions for registration of STL container /// This file contains utility functions for registration of STL container
/// classes. The methodology used is based on the SGI STL concepts. /// classes. The methodology used is based on the SGI STL concepts.
/// http://www.sgi.com/tech/stl/table_of_contents.html /// http://www.sgi.com/tech/stl/table_of_contents.html
#ifndef CHAISCRIPT_BOOTSTRAP_STL_HPP_ #ifndef CHAISCRIPT_BOOTSTRAP_STL_HPP_
#define CHAISCRIPT_BOOTSTRAP_STL_HPP_ #define CHAISCRIPT_BOOTSTRAP_STL_HPP_
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <type_traits>
#include <typeinfo> #include <typeinfo>
#include <vector> #include <vector>
@ -30,103 +27,183 @@
#include "register_function.hpp" #include "register_function.hpp"
#include "type_info.hpp" #include "type_info.hpp"
namespace chaiscript::bootstrap::standard_library { namespace chaiscript
{
namespace bootstrap
{
namespace standard_library
{
/// Bidir_Range, based on the D concept of ranges. /// Bidir_Range, based on the D concept of ranges.
/// \todo Update the Range code to base its capabilities on /// \todo Update the Range code to base its capabilities on
/// the user_typetraits of the iterator passed in /// the user_typetraits of the iterator passed in
template<typename Container, typename IterType> template<typename Container>
struct Bidir_Range { struct Bidir_Range
using container_type = Container; {
typedef Container container_type;
typedef typename std::iterator_traits<typename Container::iterator>::reference reference_type;
constexpr Bidir_Range(Container &c) Bidir_Range(Container &c)
: m_begin(c.begin()) : m_begin(c.begin()), m_end(c.end())
, m_end(c.end()) { {
} }
constexpr bool empty() const noexcept { return m_begin == m_end; } bool empty() const
{
return m_begin == m_end;
}
constexpr void pop_front() { void pop_front()
if (empty()) { {
if (empty())
{
throw std::range_error("Range empty"); throw std::range_error("Range empty");
} }
++m_begin; ++m_begin;
} }
constexpr void pop_back() { void pop_back()
if (empty()) { {
if (empty())
{
throw std::range_error("Range empty"); throw std::range_error("Range empty");
} }
--m_end; --m_end;
} }
constexpr decltype(auto) front() const { reference_type front() const
if (empty()) { {
if (empty())
{
throw std::range_error("Range empty"); throw std::range_error("Range empty");
} }
return (*m_begin); return *m_begin;
} }
constexpr decltype(auto) back() const { reference_type back() const
if (empty()) { {
if (empty())
{
throw std::range_error("Range empty"); throw std::range_error("Range empty");
} }
auto pos = m_end; typename Container::iterator pos = m_end;
--pos; --pos;
return (*(pos)); return *(pos);
} }
IterType m_begin; typename Container::iterator m_begin;
IterType m_end; typename Container::iterator m_end;
};
template<typename Container>
struct Const_Bidir_Range
{
typedef const Container container_type;
typedef typename std::iterator_traits<typename Container::const_iterator>::reference const_reference_type;
Const_Bidir_Range(const Container &c)
: m_begin(c.begin()), m_end(c.end())
{
}
bool empty() const
{
return m_begin == m_end;
}
void pop_front()
{
if (empty())
{
throw std::range_error("Range empty");
}
++m_begin;
}
void pop_back()
{
if (empty())
{
throw std::range_error("Range empty");
}
--m_end;
}
const_reference_type front() const
{
if (empty())
{
throw std::range_error("Range empty");
}
return *m_begin;
}
const_reference_type back() const
{
if (empty())
{
throw std::range_error("Range empty");
}
typename Container::const_iterator pos = m_end;
--pos;
return *(pos);
}
typename Container::const_iterator m_begin;
typename Container::const_iterator m_end;
}; };
namespace detail { namespace detail {
template<typename T> template<typename T>
size_t count(const T &t_target, const typename T::key_type &t_key) { size_t count(const T &t_target, const typename T::key_type &t_key)
{
return t_target.count(t_key); return t_target.count(t_key);
} }
template<typename T> template<typename T>
Boxed_Value find(const T &t_target, const typename T::key_type &t_key) { void insert(T &t_target, const T &t_other)
const auto itr = t_target.find(t_key); {
if (itr != t_target.end()) {
return Boxed_Value(itr->second);
}
return Boxed_Value();
}
template<typename T>
void insert(T &t_target, const T &t_other) {
t_target.insert(t_other.begin(), t_other.end()); t_target.insert(t_other.begin(), t_other.end());
} }
template<typename T> template<typename T>
void insert_ref(T &t_target, const typename T::value_type &t_val) { void insert_ref(T &t_target, const typename T::value_type &t_val)
{
t_target.insert(t_val); t_target.insert(t_val);
} }
/// Add Bidir_Range support for the given ContainerType /// Add Bidir_Range support for the given ContainerType
template<typename Bidir_Type> template<typename Bidir_Type>
void input_range_type_impl(const std::string &type, Module &m) { ModulePtr input_range_type_impl(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(user_type<Bidir_Type>(), type + "_Range"); {
m->add(user_type<Bidir_Type>(), type + "_Range");
copy_constructor<Bidir_Type>(type + "_Range", m); copy_constructor<Bidir_Type>(type + "_Range", m);
m.add(constructor<Bidir_Type(typename Bidir_Type::container_type &)>(), "range_internal"); m->add(constructor<Bidir_Type (typename Bidir_Type::container_type &)>(), "range_internal");
m.add(fun(&Bidir_Type::empty), "empty"); m->add(fun(&Bidir_Type::empty), "empty");
m.add(fun(&Bidir_Type::pop_front), "pop_front"); m->add(fun(&Bidir_Type::pop_front), "pop_front");
m.add(fun(&Bidir_Type::front), "front"); m->add(fun(&Bidir_Type::front), "front");
m.add(fun(&Bidir_Type::pop_back), "pop_back"); m->add(fun(&Bidir_Type::pop_back), "pop_back");
m.add(fun(&Bidir_Type::back), "back"); m->add(fun(&Bidir_Type::back), "back");
return m;
} }
/// Algorithm for inserting at a specific position into a container /// Algorithm for inserting at a specific position into a container
template<typename Type> template<typename Type>
void insert_at(Type &container, int pos, const typename Type::value_type &v) { void insert_at(Type &container, int pos, const typename Type::value_type &v)
{
auto itr = container.begin(); auto itr = container.begin();
auto end = container.end(); auto end = container.end();
if (pos < 0 || std::distance(itr, end) < pos) { if (pos < 0 || std::distance(itr, end) < pos)
{
throw std::range_error("Cannot insert past end of range"); throw std::range_error("Cannot insert past end of range");
} }
@ -134,141 +211,128 @@ namespace chaiscript::bootstrap::standard_library {
container.insert(itr, v); container.insert(itr, v);
} }
/// Algorithm for erasing a specific position from a container /// Algorithm for erasing a specific position from a container
template<typename Type> template<typename Type>
void erase_at(Type &container, int pos) { void erase_at(Type &container, int pos)
{
auto itr = container.begin(); auto itr = container.begin();
auto end = container.end(); auto end = container.end();
if (pos < 0 || std::distance(itr, end) <= pos) { if (pos < 0 || std::distance(itr, end) < (pos-1))
{
throw std::range_error("Cannot erase past end of range"); throw std::range_error("Cannot erase past end of range");
} }
std::advance(itr, pos); std::advance(itr, pos);
container.erase(itr); container.erase(itr);
} }
} // namespace detail }
template<typename ContainerType> template<typename ContainerType>
void input_range_type(const std::string &type, Module &m) { ModulePtr input_range_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
detail::input_range_type_impl<Bidir_Range<ContainerType, typename ContainerType::iterator>>(type, m); {
detail::input_range_type_impl<Bidir_Range<const ContainerType, typename ContainerType::const_iterator>>("Const_" + type, m); detail::input_range_type_impl<Bidir_Range<ContainerType> >(type,m);
detail::input_range_type_impl<Const_Bidir_Range<ContainerType> >("Const_" + type, m);
return m;
} }
/// Add random_access_container concept to the given ContainerType /// Add random_access_container concept to the given ContainerType
/// http://www.sgi.com/tech/stl/RandomAccessContainer.html /// http://www.sgi.com/tech/stl/RandomAccessContainer.html
template<typename ContainerType> template<typename ContainerType>
void random_access_container_type(const std::string & /*type*/, Module &m) { ModulePtr random_access_container_type(const std::string &/*type*/, ModulePtr m = std::make_shared<Module>())
{
//In the interest of runtime safety for the m, we prefer the at() method for [] access, //In the interest of runtime safety for the m, we prefer the at() method for [] access,
//to throw an exception in an out of bounds condition. //to throw an exception in an out of bounds condition.
m.add(fun([](ContainerType &c, int index) -> typename ContainerType::reference { m->add(
/// \todo we are preferring to keep the key as 'int' to avoid runtime conversions fun(
/// during dispatch. reevaluate [](ContainerType &c, int index) -> typename ContainerType::reference {
return c.at(static_cast<typename ContainerType::size_type>(index)); return c.at(index);
}), }), "[]");
"[]");
m.add(fun([](const ContainerType &c, int index) -> typename ContainerType::const_reference { m->add(
/// \todo we are preferring to keep the key as 'int' to avoid runtime conversions fun(
/// during dispatch. reevaluate [](const ContainerType &c, int index) -> typename ContainerType::const_reference {
return c.at(static_cast<typename ContainerType::size_type>(index)); return c.at(index);
}), }), "[]");
"[]");
return m;
} }
/// Add assignable concept to the given ContainerType /// Add assignable concept to the given ContainerType
/// http://www.sgi.com/tech/stl/Assignable.html /// http://www.sgi.com/tech/stl/Assignable.html
template<typename ContainerType> template<typename ContainerType>
void assignable_type(const std::string &type, Module &m) { ModulePtr assignable_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) { {
copy_constructor<ContainerType>(type, m); copy_constructor<ContainerType>(type, m);
operators::assign<ContainerType>(m); operators::assign<ContainerType>(m);
} return m;
} }
/// Add container resize concept to the given ContainerType
/// http://www.cplusplus.com/reference/stl/
template<typename ContainerType>
void resizable_type(const std::string & /*type*/, Module &m) {
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type &val) {
return a->resize(n, val);
}),
"resize");
}
m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->resize(n); }), "resize");
}
/// Add container reserve concept to the given ContainerType
/// http://www.cplusplus.com/reference/stl/
template<typename ContainerType>
void reservable_type(const std::string & /*type*/, Module &m) {
m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->reserve(n); }), "reserve");
m.add(fun([](const ContainerType *a) { return a->capacity(); }), "capacity");
}
/// Add container concept to the given ContainerType /// Add container concept to the given ContainerType
/// http://www.sgi.com/tech/stl/Container.html /// http://www.sgi.com/tech/stl/Container.html
template<typename ContainerType> template<typename ContainerType>
void container_type(const std::string & /*type*/, Module &m) { ModulePtr container_type(const std::string &/*type*/, ModulePtr m = std::make_shared<Module>())
m.add(fun([](const ContainerType *a) { return a->size(); }), "size"); {
m.add(fun([](const ContainerType *a) { return a->empty(); }), "empty"); m->add(fun([](const ContainerType *a) { return a->size(); } ), "size");
m.add(fun([](ContainerType *a) { a->clear(); }), "clear"); m->add(fun([](const ContainerType *a) { return a->empty(); } ), "empty");
m->add(fun([](ContainerType *a) { a->clear(); } ), "clear");
return m;
} }
/// Add default constructable concept to the given Type /// Add default constructable concept to the given Type
/// http://www.sgi.com/tech/stl/DefaultConstructible.html /// http://www.sgi.com/tech/stl/DefaultConstructible.html
template<typename Type> template<typename Type>
void default_constructible_type(const std::string &type, Module &m) { ModulePtr default_constructible_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(constructor<Type()>(), type); {
m->add(constructor<Type ()>(), type);
return m;
} }
/// Add sequence concept to the given ContainerType /// Add sequence concept to the given ContainerType
/// http://www.sgi.com/tech/stl/Sequence.html /// http://www.sgi.com/tech/stl/Sequence.html
template<typename ContainerType> template<typename ContainerType>
void sequence_type(const std::string & /*type*/, Module &m) { ModulePtr sequence_type(const std::string &/*type*/, ModulePtr m = std::make_shared<Module>())
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) { {
m.add(fun(&detail::insert_at<ContainerType>), []() -> std::string { m->add(fun(&detail::insert_at<ContainerType>),
[]()->std::string{
if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
return "insert_ref_at"; return "insert_ref_at";
} else { } else {
return "insert_at"; return "insert_at";
} }
}()); }());
m->add(fun(&detail::erase_at<ContainerType>), "erase_at");
return m;
} }
m.add(fun(&detail::erase_at<ContainerType>), "erase_at");
}
/// Add back insertion sequence concept to the given ContainerType /// Add back insertion sequence concept to the given ContainerType
/// http://www.sgi.com/tech/stl/BackInsertionSequence.html /// http://www.sgi.com/tech/stl/BackInsertionSequence.html
template<typename ContainerType> template<typename ContainerType>
void back_insertion_sequence_type(const std::string &type, Module &m) { ModulePtr back_insertion_sequence_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(fun([](ContainerType &container) -> decltype(auto) { {
if (container.empty()) { typedef typename ContainerType::reference (ContainerType::*backptr)();
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");
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) { m->add(fun(static_cast<backptr>(&ContainerType::back)), "back");
using push_back = void (ContainerType::*)(const typename ContainerType::value_type &);
m.add(fun(static_cast<push_back>(&ContainerType::push_back)), [&]() -> std::string {
typedef void (ContainerType::*push_back)(const typename ContainerType::value_type &);
m->add(fun(static_cast<push_back>(&ContainerType::push_back)),
[&]()->std::string{
if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
m.eval("# Pushes the second value onto the container while making a clone of the value\n" m->eval(
"def push_back(" "# Pushes the second value onto the container while making a clone of the value\n"
+ type "def push_back(" + type + " container, x)\n"
+ " container, x)\n"
"{ \n" "{ \n"
" if (x.is_var_return_value()) {\n" " if (x.is_var_return_value()) {\n"
" x.reset_var_return_value() \n" " x.reset_var_return_value() \n"
@ -276,50 +340,40 @@ namespace chaiscript::bootstrap::standard_library {
" } else { \n" " } else { \n"
" container.push_back_ref(clone(x)); \n" " container.push_back_ref(clone(x)); \n"
" }\n" " }\n"
"} \n"); "} \n"
);
return "push_back_ref"; return "push_back_ref";
} else { } else {
return "push_back"; return "push_back";
} }
}()); }());
m->add(fun(&ContainerType::pop_back), "pop_back");
return m;
} }
m.add(fun(&ContainerType::pop_back), "pop_back");
}
/// Front insertion sequence /// Front insertion sequence
/// http://www.sgi.com/tech/stl/FrontInsertionSequence.html /// http://www.sgi.com/tech/stl/FrontInsertionSequence.html
template<typename ContainerType> template<typename ContainerType>
void front_insertion_sequence_type(const std::string &type, Module &m) { ModulePtr front_insertion_sequence_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
using push_ptr = void (ContainerType::*)(typename ContainerType::const_reference); {
using pop_ptr = void (ContainerType::*)(); 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([](ContainerType &container) -> decltype(auto) { m->add(fun(static_cast<front_ptr>(&ContainerType::front)), "front");
if (container.empty()) { m->add(fun(static_cast<const_front_ptr>(&ContainerType::front)), "front");
throw std::range_error("Container empty");
} else {
return (container.front());
}
}),
"front");
m.add(fun([](const ContainerType &container) -> decltype(auto) { m->add(fun(static_cast<push_ptr>(&ContainerType::push_front)),
if (container.empty()) { [&]()->std::string{
throw std::range_error("Container empty");
} else {
return (container.front());
}
}),
"front");
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
m.add(fun(static_cast<push_ptr>(&ContainerType::push_front)), [&]() -> std::string {
if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
m.eval("# Pushes the second value onto the front of container while making a clone of the value\n" m->eval(
"def push_front(" "# Pushes the second value onto the front of container while making a clone of the value\n"
+ type "def push_front(" + type + " container, x)\n"
+ " container, x)\n"
"{ \n" "{ \n"
" if (x.is_var_return_value()) {\n" " if (x.is_var_return_value()) {\n"
" x.reset_var_return_value() \n" " x.reset_var_return_value() \n"
@ -327,76 +381,98 @@ namespace chaiscript::bootstrap::standard_library {
" } else { \n" " } else { \n"
" container.push_front_ref(clone(x)); \n" " container.push_front_ref(clone(x)); \n"
" }\n" " }\n"
"} \n"); "} \n"
);
return "push_front_ref"; return "push_front_ref";
} else { } else {
return "push_front"; return "push_front";
} }
}()); }());
m->add(fun(static_cast<pop_ptr>(&ContainerType::pop_front)), "pop_front");
return m;
} }
m.add(fun(static_cast<pop_ptr>(&ContainerType::pop_front)), "pop_front");
}
/// bootstrap a given PairType /// bootstrap a given PairType
/// http://www.sgi.com/tech/stl/pair.html /// http://www.sgi.com/tech/stl/pair.html
template<typename PairType> template<typename PairType>
void pair_type(const std::string &type, Module &m) { ModulePtr pair_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(user_type<PairType>(), type); {
m->add(user_type<PairType>(), type);
m.add(fun(&PairType::first), "first");
m.add(fun(&PairType::second), "second"); typename PairType::first_type PairType::* f = &PairType::first;
typename PairType::second_type PairType::* s = &PairType::second;
m->add(fun(f), "first");
m->add(fun(s), "second");
basic_constructors<PairType>(type, m); basic_constructors<PairType>(type, m);
m.add(constructor<PairType(const typename PairType::first_type &, const typename PairType::second_type &)>(), type); m->add(constructor<PairType (const typename PairType::first_type &, const typename PairType::second_type &)>(), type);
return m;
} }
/// Add pair associative container concept to the given ContainerType /// Add pair associative container concept to the given ContainerType
/// http://www.sgi.com/tech/stl/PairAssociativeContainer.html /// http://www.sgi.com/tech/stl/PairAssociativeContainer.html
template<typename ContainerType> template<typename ContainerType>
void pair_associative_container_type(const std::string &type, Module &m) { ModulePtr pair_associative_container_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
{
pair_type<typename ContainerType::value_type>(type + "_Pair", m); pair_type<typename ContainerType::value_type>(type + "_Pair", m);
return m;
} }
/// Add unique associative container concept to the given ContainerType /// Add unique associative container concept to the given ContainerType
/// http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html /// http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html
template<typename ContainerType> template<typename ContainerType>
void unique_associative_container_type(const std::string & /*type*/, Module &m) { ModulePtr unique_associative_container_type(const std::string &/*type*/, ModulePtr m = std::make_shared<Module>())
m.add(fun(detail::count<ContainerType>), "count"); {
m.add(fun(detail::find<ContainerType>), "find"); m->add(fun(detail::count<ContainerType>), "count");
using erase_ptr = size_t (ContainerType::*)(const typename ContainerType::key_type &); typedef size_t (ContainerType::*erase_ptr)(const typename ContainerType::key_type &);
m.add(fun(static_cast<erase_ptr>(&ContainerType::erase)), "erase"); m->add(fun(static_cast<erase_ptr>(&ContainerType::erase)), "erase");
m.add(fun(&detail::insert<ContainerType>), "insert"); m->add(fun(&detail::insert<ContainerType>), "insert");
m.add(fun(&detail::insert_ref<ContainerType>), []() -> std::string { m->add(fun(&detail::insert_ref<ContainerType>),
[]()->std::string{
if (typeid(typename ContainerType::mapped_type) == typeid(Boxed_Value)) { if (typeid(typename ContainerType::mapped_type) == typeid(Boxed_Value)) {
return "insert_ref"; return "insert_ref";
} else { } else {
return "insert"; return "insert";
} }
}()); }());
return m;
} }
/// Add a MapType container /// Add a MapType container
/// http://www.sgi.com/tech/stl/Map.html /// http://www.sgi.com/tech/stl/Map.html
template<typename MapType> template<typename MapType>
void map_type(const std::string &type, Module &m) { ModulePtr map_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(user_type<MapType>(), type); {
m->add(user_type<MapType>(), type);
using elem_access = typename MapType::mapped_type &(MapType::*)(const typename MapType::key_type &); typedef typename MapType::mapped_type &(MapType::*elem_access)(const typename MapType::key_type &);
using const_elem_access = const typename MapType::mapped_type &(MapType::*)(const typename MapType::key_type &) const; typedef const typename MapType::mapped_type &(MapType::*const_elem_access)(const typename MapType::key_type &) const;
m.add(fun(static_cast<elem_access>(&MapType::operator[])), "[]"); m->add(fun(static_cast<elem_access>(&MapType::operator[])), "[]");
m.add(fun(static_cast<elem_access>(&MapType::at)), "at"); m->add(fun(static_cast<elem_access>(&MapType::at)), "at");
m.add(fun(static_cast<const_elem_access>(&MapType::at)), "at"); m->add(fun(static_cast<const_elem_access>(&MapType::at)), "at");
if (typeid(MapType) == typeid(std::map<std::string, Boxed_Value>)) { if (typeid(MapType) == typeid(std::map<std::string, Boxed_Value>))
m.eval(R"( {
m->eval(R"(
def Map::`==`(Map rhs) { def Map::`==`(Map rhs) {
if ( rhs.size() != this.size() ) { if ( rhs.size() != this.size() ) {
return false; return false;
@ -414,7 +490,8 @@ namespace chaiscript::bootstrap::standard_library {
} }
true; true;
} }
} )"); } )"
);
} }
container_type<MapType>(type, m); container_type<MapType>(type, m);
@ -423,59 +500,55 @@ namespace chaiscript::bootstrap::standard_library {
unique_associative_container_type<MapType>(type, m); unique_associative_container_type<MapType>(type, m);
pair_associative_container_type<MapType>(type, m); pair_associative_container_type<MapType>(type, m);
input_range_type<MapType>(type, m); input_range_type<MapType>(type, m);
return m;
} }
/// hopefully working List type
/// http://www.sgi.com/tech/stl/List.html /// http://www.sgi.com/tech/stl/List.html
template<typename ListType> template<typename ListType>
void list_type(const std::string &type, Module &m) { ModulePtr list_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(user_type<ListType>(), type); {
m->add(user_type<ListType>(), type);
front_insertion_sequence_type<ListType>(type, m); front_insertion_sequence_type<ListType>(type, m);
back_insertion_sequence_type<ListType>(type, m); back_insertion_sequence_type<ListType>(type, m);
sequence_type<ListType>(type, m); sequence_type<ListType>(type, m);
resizable_type<ListType>(type, m);
container_type<ListType>(type, m); container_type<ListType>(type, m);
default_constructible_type<ListType>(type, m); default_constructible_type<ListType>(type, m);
assignable_type<ListType>(type, m); assignable_type<ListType>(type, m);
input_range_type<ListType>(type, m); input_range_type<ListType>(type, m);
return m;
} }
/// Create a vector type with associated concepts /// Create a vector type with associated concepts
/// http://www.sgi.com/tech/stl/Vector.html /// http://www.sgi.com/tech/stl/Vector.html
template<typename VectorType> template<typename VectorType>
void vector_type(const std::string &type, Module &m) { ModulePtr vector_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(user_type<VectorType>(), type); {
m->add(user_type<VectorType>(), type);
m.add(fun([](VectorType &container) -> decltype(auto) { typedef typename VectorType::reference (VectorType::*frontptr)();
if (container.empty()) { typedef typename VectorType::const_reference (VectorType::*constfrontptr)() const;
throw std::range_error("Container empty");
} else { m->add(fun(static_cast<frontptr>(&VectorType::front)), "front");
return (container.front()); m->add(fun(static_cast<constfrontptr>(&VectorType::front)), "front");
}
}),
"front");
m.add(fun([](const VectorType &container) -> decltype(auto) {
if (container.empty()) {
throw std::range_error("Container empty");
} else {
return (container.front());
}
}),
"front");
back_insertion_sequence_type<VectorType>(type, m); back_insertion_sequence_type<VectorType>(type, m);
sequence_type<VectorType>(type, m); sequence_type<VectorType>(type, m);
random_access_container_type<VectorType>(type, m); random_access_container_type<VectorType>(type, m);
resizable_type<VectorType>(type, m);
reservable_type<VectorType>(type, m);
container_type<VectorType>(type, m); container_type<VectorType>(type, m);
default_constructible_type<VectorType>(type, m); default_constructible_type<VectorType>(type, m);
assignable_type<VectorType>(type, m); assignable_type<VectorType>(type, m);
input_range_type<VectorType>(type, m); input_range_type<VectorType>(type, m);
if (typeid(VectorType) == typeid(std::vector<Boxed_Value>)) { if (typeid(VectorType) == typeid(std::vector<Boxed_Value>))
m.eval(R"( {
m->eval(R"(
def Vector::`==`(Vector rhs) { def Vector::`==`(Vector rhs) {
if ( rhs.size() != this.size() ) { if ( rhs.size() != this.size() ) {
return false; return false;
@ -493,15 +566,19 @@ namespace chaiscript::bootstrap::standard_library {
} }
true; true;
} }
} )"); } )"
);
} }
return m;
} }
/// Add a String container /// Add a String container
/// http://www.sgi.com/tech/stl/basic_string.html /// http://www.sgi.com/tech/stl/basic_string.html
template<typename String> template<typename String>
void string_type(const std::string &type, Module &m) { ModulePtr string_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(user_type<String>(), type); {
m->add(user_type<String>(), type);
operators::addition<String>(m); operators::addition<String>(m);
operators::assign_sum<String>(m); operators::assign_sum<String>(m);
opers_comparison<String>(m); opers_comparison<String>(m);
@ -513,7 +590,8 @@ namespace chaiscript::bootstrap::standard_library {
input_range_type<String>(type, m); input_range_type<String>(type, m);
//Special case: add push_back to string (which doesn't support other back_insertion operations //Special case: add push_back to string (which doesn't support other back_insertion operations
m.add(fun(&String::push_back), []() -> std::string { m->add(fun(&String::push_back),
[]()->std::string{
if (typeid(typename String::value_type) == typeid(Boxed_Value)) { if (typeid(typename String::value_type) == typeid(Boxed_Value)) {
return "push_back_ref"; return "push_back_ref";
} else { } else {
@ -521,89 +599,45 @@ namespace chaiscript::bootstrap::standard_library {
} }
}()); }());
m.add(fun([](const String *s, const String &f, size_t pos) { return s->find(f, pos); }), "find");
m.add(fun([](const String *s, const String &f, size_t pos) { return s->rfind(f, pos); }), "rfind");
m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_of(f, pos); }), "find_first_of");
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([](const String *s, const String &f, size_t pos) { return s->find(f, pos); } ), "find");
m->add(fun([](const String *s, const String &f, size_t pos) { return s->rfind(f, pos); } ), "rfind");
m->add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_of(f, pos); } ), "find_first_of");
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) { s->clear(); }), "clear"); m->add(fun([](String *s) { s->clear(); } ), "clear");
m.add(fun([](const String *s) { return s->empty(); }), "empty"); m->add(fun([](const String *s) { return s->empty(); } ), "empty");
m.add(fun([](const String *s) { return s->size(); }), "size"); m->add(fun([](const String *s) { return s->size(); } ), "size");
m.add(fun([](const String *s) { return s->c_str(); }), "c_str"); m->add(fun([](const String *s) { return s->c_str(); } ), "c_str");
m.add(fun([](const String *s) { return s->data(); }), "data"); m->add(fun([](const String *s) { return s->data(); } ), "data");
m.add(fun([](const String *s, size_t pos, size_t len) { return s->substr(pos, len); }), "substr"); m->add(fun([](const String *s, size_t pos, size_t len) { return s->substr(pos, len); } ), "substr");
return m;
} }
/// Add a String_View type (e.g. std::string_view), with conversions to and from
/// the matching owning String type (e.g. std::string).
///
/// Mirrors the search/substring surface of string_type so that scripts can
/// traverse a buffer through StringView without allocating. Sharing method
/// names with the owning String type is safe: dispatch deprioritizes any
/// candidate whose first/receiver argument requires a type conversion (see
/// dispatch() in proxy_functions.hpp), so myString.substr(1, 2) still
/// resolves to String::substr while mySV.substr(1, 2) resolves to
/// StringView::substr (returning a StringView).
///
/// \note A String_View is a non-owning reference. Constructing one from a
/// temporary owning String yields a dangling reference once that temporary
/// is destroyed; the same lifetime caveats as in C++ apply here.
template<typename StringView, typename String>
void string_view_type(const std::string &type, Module &m) {
m.add(user_type<StringView>(), type);
m.add(constructor<StringView()>(), type);
m.add(constructor<StringView(const StringView &)>(), type);
m.add(fun([](const String &s) { return StringView{s}; }), type);
opers_comparison<StringView>(m);
m.add(fun([](const StringView *s) { return s->size(); }), "size");
m.add(fun([](const StringView *s) { return s->length(); }), "length");
m.add(fun([](const StringView *s) { return s->empty(); }), "empty");
m.add(fun([](const StringView *s) { return s->data(); }), "data");
// Random-access via [] / at, returning const_reference; StringView is read-only.
m.add(fun([](const StringView &sv, int index) -> typename StringView::const_reference {
return sv.at(static_cast<typename StringView::size_type>(index));
}),
"[]");
m.add(fun([](const StringView *s, const StringView &f, size_t pos) { return s->find(f, pos); }), "find");
m.add(fun([](const StringView *s, const StringView &f, size_t pos) { return s->rfind(f, pos); }), "rfind");
m.add(fun([](const StringView *s, const StringView &f, size_t pos) { return s->find_first_of(f, pos); }), "find_first_of");
m.add(fun([](const StringView *s, const StringView &f, size_t pos) { return s->find_last_of(f, pos); }), "find_last_of");
m.add(fun([](const StringView *s, const StringView &f, size_t pos) { return s->find_last_not_of(f, pos); }), "find_last_not_of");
m.add(fun([](const StringView *s, const StringView &f, size_t pos) { return s->find_first_not_of(f, pos); }), "find_first_not_of");
m.add(fun([](const StringView *s, const StringView &f) { return s->starts_with(f); }), "starts_with");
m.add(fun([](const StringView *s, const StringView &f) { return s->ends_with(f); }), "ends_with");
m.add(fun([](const StringView *s, size_t pos, size_t len) { return s->substr(pos, len); }), "substr");
// Built-in implicit conversion from owning String to non-owning StringView.
m.add(type_conversion<const String &, StringView>());
// Explicit conversion from StringView back to owning String, registered as
// to_string(sv); the call site can also register it under the owning type's
// name (e.g. string(sv)) when desired.
m.add(fun([](const StringView sv) { return String{sv}; }), "to_string");
}
/// Add a MapType container /// Add a MapType container
/// http://www.sgi.com/tech/stl/Map.html /// http://www.sgi.com/tech/stl/Map.html
template<typename FutureType> template<typename FutureType>
void future_type(const std::string &type, Module &m) { ModulePtr future_type(const std::string &type, ModulePtr m = std::make_shared<Module>())
m.add(user_type<FutureType>(), type); {
m->add(user_type<FutureType>(), type);
m.add(fun([](const FutureType &t) { return t.valid(); }), "valid"); m->add(fun([](const FutureType &t) { return t.valid(); }), "valid");
m.add(fun([](FutureType &t) { return t.get(); }), "get"); m->add(fun(&FutureType::get), "get");
m.add(fun(&FutureType::wait), "wait"); m->add(fun(&FutureType::wait), "wait");
return m;
} }
} // namespace chaiscript::bootstrap::standard_library }
}
}
#endif #endif

View File

@ -1,12 +1,9 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_BOXED_CAST_HPP_ #ifndef CHAISCRIPT_BOXED_CAST_HPP_
#define CHAISCRIPT_BOXED_CAST_HPP_ #define CHAISCRIPT_BOXED_CAST_HPP_
@ -19,12 +16,16 @@
namespace chaiscript { namespace chaiscript {
class Type_Conversions; class Type_Conversions;
} namespace detail {
namespace chaiscript::detail::exception { namespace exception {
class bad_any_cast; class bad_any_cast;
} // namespace chaiscript::detail::exception } // namespace exception
} // namespace detail
} // namespace chaiscript
namespace chaiscript
{
namespace chaiscript {
/// \brief Function for extracting a value stored in a Boxed_Value object /// \brief Function for extracting a value stored in a Boxed_Value object
/// \tparam Type The type to extract from the Boxed_Value /// \tparam Type The type to extract from the Boxed_Value
/// \param[in] bv The Boxed_Value to extract a typed value from /// \param[in] bv The Boxed_Value to extract a typed value from
@ -68,35 +69,43 @@ namespace chaiscript {
/// assert(i == 5); /// assert(i == 5);
/// \endcode /// \endcode
template<typename Type> template<typename Type>
decltype(auto) boxed_cast(const Boxed_Value &bv, const Type_Conversions_State *t_conversions = nullptr) { typename detail::Cast_Helper<Type>::Result_Type boxed_cast(const Boxed_Value &bv, const Type_Conversions *t_conversions = nullptr)
if (!t_conversions || bv.get_type_info().bare_equal(user_type<Type>()) || (t_conversions && !(*t_conversions)->convertable_type<Type>())) { {
if (!t_conversions || bv.get_type_info().bare_equal(user_type<Type>()) || (t_conversions && !t_conversions->convertable_type<Type>())) {
try { try {
return detail::Cast_Helper<Type>::cast(bv, t_conversions); return detail::Cast_Helper<Type>::cast(bv, t_conversions);
} catch (const chaiscript::detail::exception::bad_any_cast &) { } catch (const chaiscript::detail::exception::bad_any_cast &) {
} }
} }
if (t_conversions && (*t_conversions)->convertable_type<Type>()) {
if (t_conversions && t_conversions->convertable_type<Type>())
{
try { try {
// std::cout << "trying an up conversion " << typeid(Type).name() << '\n';
// We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it
// either way, we are not responsible if it doesn't work // either way, we are not responsible if it doesn't work
return (detail::Cast_Helper<Type>::cast((*t_conversions)->boxed_type_conversion<Type>(t_conversions->saves(), bv), t_conversions)); return detail::Cast_Helper<Type>::cast(t_conversions->boxed_type_conversion<Type>(bv), t_conversions);
} catch (...) { } catch (...) {
try { try {
// try going the other way // std::cout << "trying a down conversion " << typeid(Type).name() << '\n';
return (detail::Cast_Helper<Type>::cast((*t_conversions)->boxed_type_down_conversion<Type>(t_conversions->saves(), bv), // try going the other way - down the inheritance graph
t_conversions)); return detail::Cast_Helper<Type>::cast(t_conversions->boxed_type_down_conversion<Type>(bv), t_conversions);
} catch (const chaiscript::detail::exception::bad_any_cast &) { } catch (const chaiscript::detail::exception::bad_any_cast &) {
throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type));
} }
} }
} else { } else {
// If it's not convertable, just throw the error, don't waste the time on the // If it's not polymorphic, just throw the error, don't waste the time on the
// attempted dynamic_cast // attempted dynamic_cast
throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type));
} }
} }
} // namespace chaiscript }
#endif #endif

View File

@ -1,12 +1,9 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_BOXED_CAST_HELPER_HPP_ #ifndef CHAISCRIPT_BOXED_CAST_HELPER_HPP_
#define CHAISCRIPT_BOXED_CAST_HELPER_HPP_ #define CHAISCRIPT_BOXED_CAST_HELPER_HPP_
@ -16,154 +13,125 @@
#include "boxed_value.hpp" #include "boxed_value.hpp"
#include "type_info.hpp" #include "type_info.hpp"
namespace chaiscript {
class Type_Conversions_State;
namespace detail { namespace chaiscript
{
class Type_Conversions;
namespace detail
{
// Cast_Helper_Inner helper classes // Cast_Helper_Inner helper classes
template<typename T> template<typename T>
constexpr T *throw_if_null(T *t) { T* throw_if_null(T *t)
if (t) { {
return t; if (t) return t;
}
throw std::runtime_error("Attempted to dereference null Boxed_Value"); throw std::runtime_error("Attempted to dereference null Boxed_Value");
} }
template<typename T>
static const T *verify_type_no_throw(const Boxed_Value &ob, const std::type_info &ti, const T *ptr) {
if (ob.get_type_info() == ti) {
return ptr;
} else {
throw chaiscript::detail::exception::bad_any_cast();
}
}
template<typename T>
static T *verify_type_no_throw(const Boxed_Value &ob, const std::type_info &ti, T *ptr) {
if (!ob.is_const() && ob.get_type_info() == ti) {
return ptr;
} else {
throw chaiscript::detail::exception::bad_any_cast();
}
}
template<typename T>
static const T *verify_type(const Boxed_Value &ob, const std::type_info &ti, const T *ptr) {
if (ob.get_type_info().bare_equal_type_info(ti)) {
return throw_if_null(ptr);
} else {
throw chaiscript::detail::exception::bad_any_cast();
}
}
template<typename T>
static T *verify_type(const Boxed_Value &ob, const std::type_info &ti, T *ptr) {
if (!ob.is_const() && ob.get_type_info().bare_equal_type_info(ti)) {
return throw_if_null(ptr);
} else {
throw chaiscript::detail::exception::bad_any_cast();
}
}
/// Generic Cast_Helper_Inner, for casting to any type /// Generic Cast_Helper_Inner, for casting to any type
template<typename Result> template<typename Result>
struct Cast_Helper_Inner { struct Cast_Helper_Inner
static Result cast(const Boxed_Value &ob, const Type_Conversions_State *) { {
return *static_cast<const Result *>(verify_type(ob, typeid(Result), ob.get_const_ptr())); typedef std::reference_wrapper<typename std::add_const<Result>::type > Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *)
{
if (ob.get_type_info().bare_equal_type_info(typeid(Result)))
{
auto p = throw_if_null(ob.get_const_ptr());
return std::cref(*static_cast<const Result *>(p));
} else {
throw chaiscript::detail::exception::bad_any_cast();
}
} }
}; };
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const Result> : Cast_Helper_Inner<Result> { struct Cast_Helper_Inner<const Result> : Cast_Helper_Inner<Result>
{
}; };
/// Cast_Helper_Inner for casting to a const & type
template<typename Result>
struct Cast_Helper_Inner<const Result &> : Cast_Helper_Inner<Result>
{
};
/// Cast_Helper_Inner for casting to a const * type /// Cast_Helper_Inner for casting to a const * type
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const Result *> { struct Cast_Helper_Inner<const Result *>
static const Result *cast(const Boxed_Value &ob, const Type_Conversions_State *) { {
return static_cast<const Result *>(verify_type_no_throw(ob, typeid(Result), ob.get_const_ptr())); typedef const Result * Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *)
{
if (ob.get_type_info().bare_equal_type_info(typeid(Result)))
{
return static_cast<const Result *>(throw_if_null(ob.get_const_ptr()));
} else {
throw chaiscript::detail::exception::bad_any_cast();
}
} }
}; };
/// Cast_Helper_Inner for casting to a * type /// Cast_Helper_Inner for casting to a * type
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<Result *> { struct Cast_Helper_Inner<Result *>
static Result *cast(const Boxed_Value &ob, const Type_Conversions_State *) { {
return static_cast<Result *>(verify_type_no_throw(ob, typeid(Result), ob.get_ptr())); typedef Result * Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *)
{
if (!ob.get_type_info().is_const() && ob.get_type_info() == typeid(Result))
{
return static_cast<Result *>(throw_if_null(ob.get_ptr()));
} else {
throw chaiscript::detail::exception::bad_any_cast();
}
} }
}; };
template<typename Result>
struct Cast_Helper_Inner<Result *const &> : public Cast_Helper_Inner<Result *> {
};
template<typename Result>
struct Cast_Helper_Inner<const Result *const &> : public Cast_Helper_Inner<const Result *> {
};
/// Cast_Helper_Inner for casting to a & type /// Cast_Helper_Inner for casting to a & type
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const Result &> { struct Cast_Helper_Inner<Result &>
static const Result &cast(const Boxed_Value &ob, const Type_Conversions_State *) { {
return *static_cast<const Result *>(verify_type(ob, typeid(Result), ob.get_const_ptr())); typedef Result& Result_Type;
}
};
/// Cast_Helper_Inner for casting to a & type static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *)
template<typename Result> {
struct Cast_Helper_Inner<Result &> { if (!ob.get_type_info().is_const() && ob.get_type_info().bare_equal_type_info(typeid(Result)))
static Result &cast(const Boxed_Value &ob, const Type_Conversions_State *) { {
return *static_cast<Result *>(verify_type(ob, typeid(Result), ob.get_ptr())); return *(static_cast<Result *>(throw_if_null(ob.get_ptr())));
} else {
throw chaiscript::detail::exception::bad_any_cast();
} }
};
/// Cast_Helper_Inner for casting to a && type
template<typename Result>
struct Cast_Helper_Inner<Result &&> {
static Result &&cast(const Boxed_Value &ob, const Type_Conversions_State *) {
return std::move(*static_cast<Result *>(verify_type(ob, typeid(Result), ob.get_ptr())));
}
};
/// Cast_Helper_Inner for casting to a std::unique_ptr<> && type
/// \todo Fix the fact that this has to be in a shared_ptr for now
template<typename Result>
struct Cast_Helper_Inner<std::unique_ptr<Result> &&> {
static std::unique_ptr<Result> &&cast(const Boxed_Value &ob, const Type_Conversions_State *) {
return std::move(*(ob.get().cast<std::shared_ptr<std::unique_ptr<Result>>>()));
}
};
/// Cast_Helper_Inner for casting to a std::unique_ptr<> & type
/// \todo Fix the fact that this has to be in a shared_ptr for now
template<typename Result>
struct Cast_Helper_Inner<std::unique_ptr<Result> &> {
static std::unique_ptr<Result> &cast(const Boxed_Value &ob, const Type_Conversions_State *) {
return *(ob.get().cast<std::shared_ptr<std::unique_ptr<Result>>>());
}
};
/// Cast_Helper_Inner for casting to a std::unique_ptr<> & type
/// \todo Fix the fact that this has to be in a shared_ptr for now
template<typename Result>
struct Cast_Helper_Inner<const std::unique_ptr<Result> &> {
static std::unique_ptr<Result> &cast(const Boxed_Value &ob, const Type_Conversions_State *) {
return *(ob.get().cast<std::shared_ptr<std::unique_ptr<Result>>>());
} }
}; };
/// Cast_Helper_Inner for casting to a std::shared_ptr<> type /// Cast_Helper_Inner for casting to a std::shared_ptr<> type
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<std::shared_ptr<Result>> { struct Cast_Helper_Inner<std::shared_ptr<Result> >
static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { return ob.get().cast<std::shared_ptr<Result>>(); } {
typedef std::shared_ptr<Result> Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *)
{
return ob.get().cast<std::shared_ptr<Result> >();
}
}; };
/// Cast_Helper_Inner for casting to a std::shared_ptr<const> type /// Cast_Helper_Inner for casting to a std::shared_ptr<const> type
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<std::shared_ptr<const Result>> { struct Cast_Helper_Inner<std::shared_ptr<const Result> >
static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { {
if (!ob.get_type_info().is_const()) { typedef std::shared_ptr<const Result> Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *)
{
if (!ob.get_type_info().is_const())
{
return std::const_pointer_cast<const Result>(ob.get().cast<std::shared_ptr<Result> >()); return std::const_pointer_cast<const Result>(ob.get().cast<std::shared_ptr<Result> >());
} else { } else {
return ob.get().cast<std::shared_ptr<const Result> >(); return ob.get().cast<std::shared_ptr<const Result> >();
@ -173,88 +141,108 @@ namespace chaiscript {
/// Cast_Helper_Inner for casting to a const std::shared_ptr<> & type /// Cast_Helper_Inner for casting to a const std::shared_ptr<> & type
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const std::shared_ptr<Result>> : Cast_Helper_Inner<std::shared_ptr<Result>> { struct Cast_Helper_Inner<const std::shared_ptr<Result> > : Cast_Helper_Inner<std::shared_ptr<Result> >
{
}; };
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const std::shared_ptr<Result> &> : Cast_Helper_Inner<std::shared_ptr<Result>> { struct Cast_Helper_Inner<const std::shared_ptr<Result> &> : Cast_Helper_Inner<std::shared_ptr<Result> >
}; {
template<typename Result>
struct Cast_Helper_Inner<std::shared_ptr<Result> &> {
static_assert(!std::is_const<Result>::value, "Non-const reference to std::shared_ptr<const T> is not supported");
static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) {
std::shared_ptr<Result> &res = ob.get().cast<std::shared_ptr<Result>>();
return ob.pointer_sentinel(res);
}
}; };
/// Cast_Helper_Inner for casting to a const std::shared_ptr<const> & type /// Cast_Helper_Inner for casting to a const std::shared_ptr<const> & type
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const std::shared_ptr<const Result>> : Cast_Helper_Inner<std::shared_ptr<const Result>> { struct Cast_Helper_Inner<const std::shared_ptr<const Result> > : Cast_Helper_Inner<std::shared_ptr<const Result> >
{
}; };
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const std::shared_ptr<const Result> &> : Cast_Helper_Inner<std::shared_ptr<const Result>> { struct Cast_Helper_Inner<const std::shared_ptr<const Result> &> : Cast_Helper_Inner<std::shared_ptr<const Result> >
{
}; };
/// Cast_Helper_Inner for casting to a Boxed_Value type /// Cast_Helper_Inner for casting to a Boxed_Value type
template<> template<>
struct Cast_Helper_Inner<Boxed_Value> { struct Cast_Helper_Inner<Boxed_Value>
static Boxed_Value cast(const Boxed_Value &ob, const Type_Conversions_State *) { return ob; } {
typedef Boxed_Value Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *)
{
return ob;
}
}; };
/// Cast_Helper_Inner for casting to a Boxed_Value & type /// Cast_Helper_Inner for casting to a Boxed_Value & type
template<> template<>
struct Cast_Helper_Inner<Boxed_Value &> { struct Cast_Helper_Inner<Boxed_Value &>
static std::reference_wrapper<Boxed_Value> cast(const Boxed_Value &ob, const Type_Conversions_State *) { {
typedef std::reference_wrapper<Boxed_Value> Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *)
{
return std::ref(const_cast<Boxed_Value &>(ob)); return std::ref(const_cast<Boxed_Value &>(ob));
} }
}; };
/// Cast_Helper_Inner for casting to a const Boxed_Value & type /// Cast_Helper_Inner for casting to a const Boxed_Value & type
template<> template<>
struct Cast_Helper_Inner<const Boxed_Value> : Cast_Helper_Inner<Boxed_Value> { struct Cast_Helper_Inner<const Boxed_Value> : Cast_Helper_Inner<Boxed_Value>
{
}; };
template<> template<>
struct Cast_Helper_Inner<const Boxed_Value &> : Cast_Helper_Inner<Boxed_Value> { struct Cast_Helper_Inner<const Boxed_Value &> : Cast_Helper_Inner<Boxed_Value>
{
}; };
/// Cast_Helper_Inner for casting to a std::reference_wrapper type /// Cast_Helper_Inner for casting to a std::reference_wrapper type
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<std::reference_wrapper<Result>> : Cast_Helper_Inner<Result &> { struct Cast_Helper_Inner<std::reference_wrapper<Result> > : Cast_Helper_Inner<Result &>
{
}; };
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const std::reference_wrapper<Result>> : Cast_Helper_Inner<Result &> { struct Cast_Helper_Inner<const std::reference_wrapper<Result> > : Cast_Helper_Inner<Result &>
{
}; };
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const std::reference_wrapper<Result> &> : Cast_Helper_Inner<Result &> { struct Cast_Helper_Inner<const std::reference_wrapper<Result> &> : Cast_Helper_Inner<Result &>
{
}; };
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<std::reference_wrapper<const Result>> : Cast_Helper_Inner<const Result &> { struct Cast_Helper_Inner<std::reference_wrapper<const Result> > : Cast_Helper_Inner<const Result &>
{
}; };
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const std::reference_wrapper<const Result>> : Cast_Helper_Inner<const Result &> { struct Cast_Helper_Inner<const std::reference_wrapper<const Result> > : Cast_Helper_Inner<const Result &>
{
}; };
template<typename Result> template<typename Result>
struct Cast_Helper_Inner<const std::reference_wrapper<const Result> &> : Cast_Helper_Inner<const Result &> { struct Cast_Helper_Inner<const std::reference_wrapper<const Result> & > : Cast_Helper_Inner<const Result &>
{
}; };
/// The exposed Cast_Helper object that by default just calls the Cast_Helper_Inner /// The exposed Cast_Helper object that by default just calls the Cast_Helper_Inner
template<typename T> template<typename T>
struct Cast_Helper { struct Cast_Helper
static decltype(auto) cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { {
return (Cast_Helper_Inner<T>::cast(ob, t_conversions)); typedef typename Cast_Helper_Inner<T>::Result_Type Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *t_conversions)
{
return Cast_Helper_Inner<T>::cast(ob, t_conversions);
} }
}; };
} // namespace detail }
} // namespace chaiscript }
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,9 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_BOXED_VALUE_HPP_ #ifndef CHAISCRIPT_BOXED_VALUE_HPP_
#define CHAISCRIPT_BOXED_VALUE_HPP_ #define CHAISCRIPT_BOXED_VALUE_HPP_
@ -18,29 +15,36 @@
#include "any.hpp" #include "any.hpp"
#include "type_info.hpp" #include "type_info.hpp"
namespace chaiscript { namespace chaiscript
{
/// \brief A wrapper for holding any valid C++ type. All types in ChaiScript are Boxed_Value objects /// \brief A wrapper for holding any valid C++ type. All types in ChaiScript are Boxed_Value objects
/// \sa chaiscript::boxed_cast /// \sa chaiscript::boxed_cast
class Boxed_Value { class Boxed_Value
{
public: public:
/// used for explicitly creating a "void" object /// used for explicitly creating a "void" object
struct Void_Type { struct Void_Type
{
}; };
private: private:
/// structure which holds the internal state of a Boxed_Value /// structure which holds the internal state of a Boxed_Value
/// \todo Get rid of Any and merge it with this, reducing an allocation in the process /// \todo Get rid of Any and merge it with this, reducing an allocation in the process
struct Data { struct Data
Data(const Type_Info &ti, chaiscript::detail::Any to, bool is_ref, const void *t_void_ptr, bool t_return_value) noexcept {
: m_type_info(ti) Data(const Type_Info &ti,
, m_obj(std::move(to)) chaiscript::detail::Any to,
, m_data_ptr(ti.is_const() ? nullptr : const_cast<void *>(t_void_ptr)) bool tr,
, m_const_data_ptr(t_void_ptr) const void *t_void_ptr,
, m_is_ref(is_ref) bool t_return_value)
, m_return_value(t_return_value) { : m_type_info(ti), m_obj(std::move(to)), m_data_ptr(ti.is_const()?nullptr:const_cast<void *>(t_void_ptr)), m_const_data_ptr(t_void_ptr),
m_is_ref(tr), m_return_value(t_return_value)
{
} }
Data &operator=(const Data &rhs) { Data &operator=(const Data &rhs)
{
m_type_info = rhs.m_type_info; m_type_info = rhs.m_type_info;
m_obj = rhs.m_obj; m_obj = rhs.m_obj;
m_is_ref = rhs.m_is_ref; m_is_ref = rhs.m_is_ref;
@ -48,8 +52,9 @@ namespace chaiscript {
m_const_data_ptr = rhs.m_const_data_ptr; m_const_data_ptr = rhs.m_const_data_ptr;
m_return_value = rhs.m_return_value; m_return_value = rhs.m_return_value;
if (rhs.m_attrs) { if (rhs.m_attrs)
m_attrs = std::make_unique<std::map<std::string, std::shared_ptr<Data>>>(*rhs.m_attrs); {
m_attrs = std::unique_ptr<std::map<std::string, std::shared_ptr<Data>>>(new std::map<std::string, std::shared_ptr<Data>>(*rhs.m_attrs));
} }
return *this; return *this;
@ -57,8 +62,11 @@ namespace chaiscript {
Data(const Data &) = delete; Data(const Data &) = delete;
#if !defined(__APPLE__) && (!defined(_MSC_VER) || _MSC_VER != 1800)
Data(Data &&) = default; Data(Data &&) = default;
Data &operator=(Data &&rhs) = default; Data &operator=(Data &&rhs) = default;
#endif
Type_Info m_type_info; Type_Info m_type_info;
chaiscript::detail::Any m_obj; chaiscript::detail::Any m_obj;
@ -69,151 +77,205 @@ namespace chaiscript {
bool m_return_value; bool m_return_value;
}; };
struct Object_Data { struct Object_Data
static auto get(Boxed_Value::Void_Type, bool t_return_value) { {
return std::make_shared<Data>(detail::Get_Type_Info<void>::get(), chaiscript::detail::Any(), false, nullptr, t_return_value); static std::shared_ptr<Data> get(Boxed_Value::Void_Type, bool t_return_value)
{
return std::make_shared<Data>(
detail::Get_Type_Info<void>::get(),
chaiscript::detail::Any(),
false,
nullptr,
t_return_value)
;
} }
template<typename T> template<typename T>
static auto get(const std::shared_ptr<T> *obj, bool t_return_value) { static std::shared_ptr<Data> get(const std::shared_ptr<T> *obj, bool t_return_value)
{
return get(*obj, t_return_value); return get(*obj, t_return_value);
} }
template<typename T> template<typename T>
static auto get(const std::shared_ptr<T> &obj, bool t_return_value) { static std::shared_ptr<Data> get(const std::shared_ptr<T> &obj, bool t_return_value)
return std::make_shared<Data>(detail::Get_Type_Info<T>::get(), chaiscript::detail::Any(obj), false, obj.get(), t_return_value); {
return std::make_shared<Data>(
detail::Get_Type_Info<T>::get(),
chaiscript::detail::Any(obj),
false,
obj.get(),
t_return_value
);
} }
template<typename T> template<typename T>
static auto get(std::shared_ptr<T> &&obj, bool t_return_value) { static std::shared_ptr<Data> get(std::shared_ptr<T> &&obj, bool t_return_value)
{
auto ptr = obj.get(); auto ptr = obj.get();
return std::make_shared<Data>(detail::Get_Type_Info<T>::get(), chaiscript::detail::Any(std::move(obj)), false, ptr, t_return_value); return std::make_shared<Data>(
detail::Get_Type_Info<T>::get(),
chaiscript::detail::Any(std::move(obj)),
false,
ptr,
t_return_value
);
} }
template<typename T> template<typename T>
static auto get(T *t, bool t_return_value) { static std::shared_ptr<Data> get(T *t, bool t_return_value)
{
return get(std::ref(*t), t_return_value); return get(std::ref(*t), t_return_value);
} }
template<typename T> template<typename T>
static auto get(const T *t, bool t_return_value) { static std::shared_ptr<Data> get(const T *t, bool t_return_value)
{
return get(std::cref(*t), t_return_value); return get(std::cref(*t), t_return_value);
} }
template<typename T> template<typename T>
static auto get(std::reference_wrapper<T> obj, bool t_return_value) { static std::shared_ptr<Data> get(std::reference_wrapper<T> obj, bool t_return_value)
{
auto p = &obj.get(); auto p = &obj.get();
return std::make_shared<Data>(detail::Get_Type_Info<T>::get(), chaiscript::detail::Any(std::move(obj)), true, p, t_return_value); return std::make_shared<Data>(
} detail::Get_Type_Info<T>::get(),
chaiscript::detail::Any(std::move(obj)),
template<typename T>
static auto get(std::unique_ptr<T> &&obj, bool t_return_value) {
auto ptr = obj.get();
return std::make_shared<Data>(detail::Get_Type_Info<T>::get(),
chaiscript::detail::Any(std::make_shared<std::unique_ptr<T>>(std::move(obj))),
true, true,
ptr, p,
t_return_value); t_return_value
);
} }
template<typename T> template<typename T>
static auto get(T t, bool t_return_value) { static std::shared_ptr<Data> get(T t, bool t_return_value)
{
auto p = std::make_shared<T>(std::move(t)); auto p = std::make_shared<T>(std::move(t));
auto ptr = p.get(); auto ptr = p.get();
return std::make_shared<Data>(detail::Get_Type_Info<T>::get(), chaiscript::detail::Any(std::move(p)), false, ptr, t_return_value); return std::make_shared<Data>(
detail::Get_Type_Info<T>::get(),
chaiscript::detail::Any(std::move(p)),
false,
ptr,
t_return_value
);
}
static std::shared_ptr<Data> get()
{
return std::make_shared<Data>(
Type_Info(),
chaiscript::detail::Any(),
false,
nullptr,
false
);
} }
static std::shared_ptr<Data> get() { return std::make_shared<Data>(Type_Info(), chaiscript::detail::Any(), false, nullptr, false); }
}; };
public: public:
/// Basic Boxed_Value constructor /// Basic Boxed_Value constructor
template<typename T, typename = std::enable_if_t<!std::is_same_v<Boxed_Value, std::decay_t<T>>>> template<typename T,
typename = typename std::enable_if<!std::is_same<Boxed_Value, typename std::decay<T>::type>::value>::type>
explicit Boxed_Value(T &&t, bool t_return_value = false) explicit Boxed_Value(T &&t, bool t_return_value = false)
: m_data(Object_Data::get(std::forward<T>(t), t_return_value)) { : m_data(Object_Data::get(std::forward<T>(t), t_return_value))
{
} }
/// Unknown-type constructor /// Unknown-type constructor
Boxed_Value() = default; Boxed_Value()
: m_data(Object_Data::get())
{
}
#if !defined(_MSC_VER) || _MSC_VER != 1800
Boxed_Value(Boxed_Value&&) = default; Boxed_Value(Boxed_Value&&) = default;
Boxed_Value& operator=(Boxed_Value&&) = default; Boxed_Value& operator=(Boxed_Value&&) = default;
#endif
Boxed_Value(const Boxed_Value&) = default; Boxed_Value(const Boxed_Value&) = default;
Boxed_Value& operator=(const Boxed_Value&) = default; Boxed_Value& operator=(const Boxed_Value&) = default;
void swap(Boxed_Value &rhs) noexcept { std::swap(m_data, rhs.m_data); } void swap(Boxed_Value &rhs)
{
std::swap(m_data, rhs.m_data);
}
/// Copy the values stored in rhs.m_data to m_data. /// Copy the values stored in rhs.m_data to m_data.
/// m_data pointers are not shared in this case /// m_data pointers are not shared in this case
Boxed_Value assign(const Boxed_Value &rhs) noexcept { Boxed_Value assign(const Boxed_Value &rhs)
{
(*m_data) = (*rhs.m_data); (*m_data) = (*rhs.m_data);
return *this; return *this;
} }
const Type_Info &get_type_info() const noexcept { return m_data->m_type_info; } const Type_Info &get_type_info() const CHAISCRIPT_NOEXCEPT
{
return m_data->m_type_info;
}
/// return true if the object is uninitialized /// return true if the object is uninitialized
bool is_undef() const noexcept { return m_data->m_type_info.is_undef(); } bool is_undef() const CHAISCRIPT_NOEXCEPT
{
bool is_const() const noexcept { return m_data->m_type_info.is_const(); } return m_data->m_type_info.is_undef();
/// Mark this Boxed_Value as const (used for script-level const declarations)
void make_const() noexcept {
m_data->m_type_info.make_const();
m_data->m_data_ptr = nullptr;
} }
bool is_type(const Type_Info &ti) const noexcept { return m_data->m_type_info.bare_equal(ti); } bool is_const() const CHAISCRIPT_NOEXCEPT
{
template<typename T> return m_data->m_type_info.is_const();
auto pointer_sentinel(std::shared_ptr<T> &ptr) const noexcept {
struct Sentinel {
Sentinel(std::shared_ptr<T> &t_ptr, Data &data)
: m_ptr(t_ptr)
, m_data(data) {
} }
~Sentinel() { bool is_type(const Type_Info &ti) const CHAISCRIPT_NOEXCEPT
// save new pointer data {
const auto ptr_ = m_ptr.get().get(); return m_data->m_type_info.bare_equal(ti);
m_data.get().m_data_ptr = ptr_;
m_data.get().m_const_data_ptr = ptr_;
} }
Sentinel &operator=(Sentinel &&s) = default; bool is_null() const CHAISCRIPT_NOEXCEPT
Sentinel(Sentinel &&s) = default; {
return (m_data->m_data_ptr == nullptr && m_data->m_const_data_ptr == nullptr);
operator std::shared_ptr<T> &() const noexcept { return m_ptr.get(); }
Sentinel &operator=(const Sentinel &) = delete;
Sentinel(Sentinel &) = delete;
std::reference_wrapper<std::shared_ptr<T>> m_ptr;
std::reference_wrapper<Data> m_data;
};
return Sentinel(ptr, *(m_data.get()));
} }
bool is_null() const noexcept { return (m_data->m_data_ptr == nullptr && m_data->m_const_data_ptr == nullptr); } const chaiscript::detail::Any & get() const CHAISCRIPT_NOEXCEPT
{
return m_data->m_obj;
}
const chaiscript::detail::Any &get() const noexcept { return m_data->m_obj; } bool is_ref() const CHAISCRIPT_NOEXCEPT
{
return m_data->m_is_ref;
}
bool is_ref() const noexcept { return m_data->m_is_ref; } bool is_return_value() const CHAISCRIPT_NOEXCEPT
{
return m_data->m_return_value;
}
bool is_return_value() const noexcept { return m_data->m_return_value; } void reset_return_value() const CHAISCRIPT_NOEXCEPT
{
m_data->m_return_value = false;
}
void reset_return_value() const noexcept { m_data->m_return_value = false; } bool is_pointer() const CHAISCRIPT_NOEXCEPT
{
return !is_ref();
}
bool is_pointer() const noexcept { return !is_ref(); } void *get_ptr() const CHAISCRIPT_NOEXCEPT
{
return m_data->m_data_ptr;
}
void *get_ptr() const noexcept { return m_data->m_data_ptr; } const void *get_const_ptr() const CHAISCRIPT_NOEXCEPT
{
return m_data->m_const_data_ptr;
}
const void *get_const_ptr() const noexcept { return m_data->m_const_data_ptr; } Boxed_Value get_attr(const std::string &t_name)
{
Boxed_Value get_attr(const std::string &t_name) { if (!m_data->m_attrs)
if (!m_data->m_attrs) { {
m_data->m_attrs = std::make_unique<std::map<std::string, std::shared_ptr<Data>>>(); m_data->m_attrs = std::unique_ptr<std::map<std::string, std::shared_ptr<Data>>>(new std::map<std::string, std::shared_ptr<Data>>());
} }
auto &attr = (*m_data->m_attrs)[t_name]; auto &attr = (*m_data->m_attrs)[t_name];
@ -226,36 +288,41 @@ namespace chaiscript {
} }
} }
Boxed_Value &copy_attrs(const Boxed_Value &t_obj) { Boxed_Value &copy_attrs(const Boxed_Value &t_obj)
if (t_obj.m_data->m_attrs) { {
m_data->m_attrs = std::make_unique<std::map<std::string, std::shared_ptr<Data>>>(*t_obj.m_data->m_attrs); if (t_obj.m_data->m_attrs)
{
m_data->m_attrs = std::unique_ptr<std::map<std::string, std::shared_ptr<Data>>>(new std::map<std::string, std::shared_ptr<Data>>(*t_obj.m_data->m_attrs));
} }
return *this; return *this;
} }
Boxed_Value &clone_attrs(const Boxed_Value &t_obj) { Boxed_Value &clone_attrs(const Boxed_Value &t_obj)
{
copy_attrs(t_obj); copy_attrs(t_obj);
reset_return_value(); reset_return_value();
return *this; return *this;
} }
/// \returns true if the two Boxed_Values share the same internal type /// \returns true if the two Boxed_Values share the same internal type
static bool type_match(const Boxed_Value &l, const Boxed_Value &r) noexcept { return l.get_type_info() == r.get_type_info(); } static bool type_match(const Boxed_Value &l, const Boxed_Value &r) CHAISCRIPT_NOEXCEPT
{
return l.get_type_info() == r.get_type_info();
}
private: private:
// necessary to avoid hitting the templated && constructor of Boxed_Value // necessary to avoid hitting the templated && constructor of Boxed_Value
struct Internal_Construction { struct Internal_Construction{};
};
Boxed_Value(std::shared_ptr<Data> t_data, Internal_Construction) Boxed_Value(const std::shared_ptr<Data> &t_data, Internal_Construction)
: m_data(std::move(t_data)) { : m_data(t_data) {
} }
std::shared_ptr<Data> m_data = Object_Data::get(); std::shared_ptr<Data> m_data;
}; };
/// @brief Creates a Boxed_Value. If the object passed in is a value type, it is copied. If it is a pointer, std::shared_ptr, or /// @brief Creates a Boxed_Value. If the object passed in is a value type, it is copied. If it is a pointer, std::shared_ptr, or std::reference_type
/// std::reference_type
/// a copy is not made. /// a copy is not made.
/// @param t The value to box /// @param t The value to box
/// ///
@ -270,7 +337,8 @@ namespace chaiscript {
/// ///
/// @sa @ref adding_objects /// @sa @ref adding_objects
template<typename T> template<typename T>
Boxed_Value var(T &&t) { Boxed_Value var(T &&t)
{
return Boxed_Value(std::forward<T>(t)); return Boxed_Value(std::forward<T>(t));
} }
@ -280,7 +348,8 @@ namespace chaiscript {
/// \returns Immutable Boxed_Value /// \returns Immutable Boxed_Value
/// \sa Boxed_Value::is_const /// \sa Boxed_Value::is_const
template<typename T> template<typename T>
Boxed_Value const_var_impl(const T &t) { Boxed_Value const_var_impl(const T &t)
{
return Boxed_Value(std::make_shared<typename std::add_const<T>::type >(t)); return Boxed_Value(std::make_shared<typename std::add_const<T>::type >(t));
} }
@ -290,7 +359,8 @@ namespace chaiscript {
/// \returns Immutable Boxed_Value /// \returns Immutable Boxed_Value
/// \sa Boxed_Value::is_const /// \sa Boxed_Value::is_const
template<typename T> template<typename T>
Boxed_Value const_var_impl(T *t) { Boxed_Value const_var_impl(T *t)
{
return Boxed_Value( const_cast<typename std::add_const<T>::type *>(t) ); return Boxed_Value( const_cast<typename std::add_const<T>::type *>(t) );
} }
@ -300,7 +370,8 @@ namespace chaiscript {
/// \returns Immutable Boxed_Value /// \returns Immutable Boxed_Value
/// \sa Boxed_Value::is_const /// \sa Boxed_Value::is_const
template<typename T> template<typename T>
Boxed_Value const_var_impl(const std::shared_ptr<T> &t) { Boxed_Value const_var_impl(const std::shared_ptr<T> &t)
{
return Boxed_Value( std::const_pointer_cast<typename std::add_const<T>::type>(t) ); return Boxed_Value( std::const_pointer_cast<typename std::add_const<T>::type>(t) );
} }
@ -310,10 +381,11 @@ namespace chaiscript {
/// \returns Immutable Boxed_Value /// \returns Immutable Boxed_Value
/// \sa Boxed_Value::is_const /// \sa Boxed_Value::is_const
template<typename T> template<typename T>
Boxed_Value const_var_impl(const std::reference_wrapper<T> &t) { Boxed_Value const_var_impl(const std::reference_wrapper<T> &t)
{
return Boxed_Value( std::cref(t.get()) ); return Boxed_Value( std::cref(t.get()) );
} }
} // namespace detail }
/// \brief Takes an object and returns an immutable Boxed_Value. If the object is a std::reference or pointer type /// \brief Takes an object and returns an immutable Boxed_Value. If the object is a std::reference or pointer type
/// the value is not copied. If it is an object type, it is copied. /// the value is not copied. If it is an object type, it is copied.
@ -339,18 +411,15 @@ namespace chaiscript {
/// \todo support C++11 strongly typed enums /// \todo support C++11 strongly typed enums
/// \sa \ref adding_objects /// \sa \ref adding_objects
template<typename T> template<typename T>
Boxed_Value const_var(const T &t) { Boxed_Value const_var(const T &t)
{
return detail::const_var_impl(t); return detail::const_var_impl(t);
} }
inline Boxed_Value void_var() { #ifdef CHAISCRIPT_HAS_MAGIC_STATICS
static const auto v = Boxed_Value(Boxed_Value::Void_Type());
return v;
}
inline Boxed_Value const_var(bool b) { inline Boxed_Value const_var(bool b) {
static const auto t = detail::const_var_impl(true); static auto t = detail::const_var_impl(true);
static const auto f = detail::const_var_impl(false); static auto f = detail::const_var_impl(false);
if (b) { if (b) {
return t; return t;
@ -358,7 +427,9 @@ namespace chaiscript {
return f; return f;
} }
} }
#endif
} // namespace chaiscript }
#endif #endif

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,9 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_DYNAMIC_OBJECT_HPP_ #ifndef CHAISCRIPT_DYNAMIC_OBJECT_HPP_
#define CHAISCRIPT_DYNAMIC_OBJECT_HPP_ #define CHAISCRIPT_DYNAMIC_OBJECT_HPP_
@ -23,59 +20,59 @@ namespace chaiscript {
} // namespace dispatch } // namespace dispatch
} // namespace chaiscript } // namespace chaiscript
namespace chaiscript { namespace chaiscript
namespace dispatch { {
namespace dispatch
{
struct option_explicit_set : std::runtime_error { struct option_explicit_set : std::runtime_error {
explicit option_explicit_set(const std::string &t_param_name) option_explicit_set(const std::string &t_param_name)
: std::runtime_error("option explicit set and parameter '" + t_param_name + "' does not exist") { : std::runtime_error("option explicit set and parameter '" + t_param_name + "' does not exist")
{
} }
option_explicit_set(const option_explicit_set &) = default; virtual ~option_explicit_set() CHAISCRIPT_NOEXCEPT {}
~option_explicit_set() noexcept override = default;
}; };
class Dynamic_Object { class Dynamic_Object
{
public: public:
explicit Dynamic_Object(std::string t_type_name) Dynamic_Object(std::string t_type_name)
: m_type_name(std::move(t_type_name)) : m_type_name(std::move(t_type_name)), m_option_explicit(false)
, m_option_explicit(false) { {
} }
Dynamic_Object() = default; Dynamic_Object() : m_type_name(""), m_option_explicit(false)
{
/// Register that derived_name inherits from base_name
static void register_inheritance(const std::string &derived_name, const std::string &base_name) {
inheritance_map()[derived_name] = base_name;
} }
/// Check if type_name is, or inherits from, base_name bool is_explicit() const
static bool type_matches(const std::string &type_name, const std::string &base_name) noexcept { {
if (type_name == base_name) { return m_option_explicit;
return true;
}
const auto &m = inheritance_map();
auto it = m.find(type_name);
while (it != m.end()) {
if (it->second == base_name) {
return true;
}
it = m.find(it->second);
}
return false;
} }
bool is_explicit() const noexcept { return m_option_explicit; } void set_explicit(const bool t_explicit)
{
m_option_explicit = t_explicit;
}
void set_explicit(const bool t_explicit) noexcept { m_option_explicit = t_explicit; } std::string get_type_name() const
{
return m_type_name;
}
const std::string &get_type_name() const noexcept { return m_type_name; } const Boxed_Value &operator[](const std::string &t_attr_name) const
{
return get_attr(t_attr_name);
}
const Boxed_Value &operator[](const std::string &t_attr_name) const { return get_attr(t_attr_name); } Boxed_Value &operator[](const std::string &t_attr_name)
{
return get_attr(t_attr_name);
}
Boxed_Value &operator[](const std::string &t_attr_name) { return get_attr(t_attr_name); } const Boxed_Value &get_attr(const std::string &t_attr_name) const
{
const Boxed_Value &get_attr(const std::string &t_attr_name) const {
auto a = m_attrs.find(t_attr_name); auto a = m_attrs.find(t_attr_name);
if (a != m_attrs.end()) { if (a != m_attrs.end()) {
@ -85,11 +82,13 @@ namespace chaiscript {
} }
} }
bool has_attr(const std::string &t_attr_name) const { return m_attrs.find(t_attr_name) != m_attrs.end(); } Boxed_Value &get_attr(const std::string &t_attr_name)
{
return m_attrs[t_attr_name];
}
Boxed_Value &get_attr(const std::string &t_attr_name) { return m_attrs[t_attr_name]; } Boxed_Value &method_missing(const std::string &t_method_name)
{
Boxed_Value &method_missing(const std::string &t_method_name) {
if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) {
throw option_explicit_set(t_method_name); throw option_explicit_set(t_method_name);
} }
@ -97,7 +96,8 @@ namespace chaiscript {
return get_attr(t_method_name); return get_attr(t_method_name);
} }
const Boxed_Value &method_missing(const std::string &t_method_name) const { const Boxed_Value &method_missing(const std::string &t_method_name) const
{
if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) {
throw option_explicit_set(t_method_name); throw option_explicit_set(t_method_name);
} }
@ -105,20 +105,20 @@ namespace chaiscript {
return get_attr(t_method_name); return get_attr(t_method_name);
} }
std::map<std::string, Boxed_Value> get_attrs() const { return m_attrs; }
std::map<std::string, Boxed_Value> get_attrs() const
{
return m_attrs;
}
private: private:
static std::map<std::string, std::string> &inheritance_map() { std::string m_type_name;
static std::map<std::string, std::string> s_map; bool m_option_explicit;
return s_map;
}
const std::string m_type_name = "";
bool m_option_explicit = false;
std::map<std::string, Boxed_Value> m_attrs; std::map<std::string, Boxed_Value> m_attrs;
}; };
} // namespace dispatch }
} // namespace chaiscript }
#endif #endif

View File

@ -1,7 +1,7 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com // http://www.chaiscript.com
#ifndef CHAISCRIPT_DYNAMIC_OBJECT_DETAIL_HPP_ #ifndef CHAISCRIPT_DYNAMIC_OBJECT_DETAIL_HPP_
@ -19,9 +19,9 @@
#include "boxed_cast.hpp" #include "boxed_cast.hpp"
#include "boxed_cast_helper.hpp" #include "boxed_cast_helper.hpp"
#include "boxed_value.hpp" #include "boxed_value.hpp"
#include "dynamic_object.hpp"
#include "proxy_functions.hpp" #include "proxy_functions.hpp"
#include "type_info.hpp" #include "type_info.hpp"
#include "dynamic_object.hpp"
namespace chaiscript { namespace chaiscript {
class Type_Conversions; class Type_Conversions;
@ -30,110 +30,136 @@ namespace chaiscript {
} // namespace dispatch } // namespace dispatch
} // namespace chaiscript } // namespace chaiscript
namespace chaiscript { namespace chaiscript
namespace dispatch { {
namespace detail { namespace dispatch
{
namespace detail
{
/// A Proxy_Function implementation designed for calling a function /// A Proxy_Function implementation designed for calling a function
/// that is automatically guarded based on the first param based on the /// that is automatically guarded based on the first param based on the
/// param's type name /// param's type name
class Dynamic_Object_Function final : public Proxy_Function_Base { class Dynamic_Object_Function : public Proxy_Function_Base
{
public: public:
Dynamic_Object_Function(std::string t_type_name, const Proxy_Function &t_func, bool t_is_attribute = false) Dynamic_Object_Function(
: Proxy_Function_Base(t_func->get_param_types(), t_func->get_arity()) std::string t_type_name,
, m_type_name(std::move(t_type_name)) const Proxy_Function &t_func,
, m_func(t_func) bool t_is_attribute = false)
, m_doti(user_type<Dynamic_Object>()) : Proxy_Function_Base(t_func->get_param_types(), t_func->get_arity()),
, m_is_attribute(t_is_attribute) { m_type_name(std::move(t_type_name)), m_func(t_func), m_doti(user_type<Dynamic_Object>()),
m_is_attribute(t_is_attribute)
{
assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0)
&& "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); && "Programming error, Dynamic_Object_Function must have at least one parameter (this)");
} }
Dynamic_Object_Function(std::string t_type_name, const Proxy_Function &t_func, const Type_Info &t_ti, bool t_is_attribute = false) Dynamic_Object_Function(
: Proxy_Function_Base(build_param_types(t_func->get_param_types(), t_ti), t_func->get_arity()) std::string t_type_name,
, m_type_name(std::move(t_type_name)) const Proxy_Function &t_func,
, m_func(t_func) const Type_Info &t_ti,
, m_ti(t_ti.is_undef() ? nullptr : new Type_Info(t_ti)) bool t_is_attribute = false)
, m_doti(user_type<Dynamic_Object>()) : Proxy_Function_Base(build_param_types(t_func->get_param_types(), t_ti), t_func->get_arity()),
, m_is_attribute(t_is_attribute) { m_type_name(std::move(t_type_name)), m_func(t_func), m_ti(t_ti.is_undef()?nullptr:new Type_Info(t_ti)), m_doti(user_type<Dynamic_Object>()),
m_is_attribute(t_is_attribute)
{
assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0)
&& "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); && "Programming error, Dynamic_Object_Function must have at least one parameter (this)");
} }
virtual ~Dynamic_Object_Function() {}
Dynamic_Object_Function &operator=(const Dynamic_Object_Function) = delete; Dynamic_Object_Function &operator=(const Dynamic_Object_Function) = delete;
Dynamic_Object_Function(Dynamic_Object_Function &) = delete; Dynamic_Object_Function(Dynamic_Object_Function &) = delete;
bool operator==(const Proxy_Function_Base &f) const noexcept override { virtual bool operator==(const Proxy_Function_Base &f) const CHAISCRIPT_OVERRIDE
if (const auto *df = dynamic_cast<const Dynamic_Object_Function *>(&f)) { {
if (const auto *df = dynamic_cast<const Dynamic_Object_Function *>(&f))
{
return df->m_type_name == m_type_name && (*df->m_func) == (*m_func); return df->m_type_name == m_type_name && (*df->m_func) == (*m_func);
} else { } else {
return false; return false;
} }
} }
bool is_attribute_function() const noexcept override { return m_is_attribute; } virtual bool is_attribute_function() const CHAISCRIPT_OVERRIDE { return m_is_attribute; }
const std::string &dynamic_object_type_name() const noexcept override { return m_type_name; } virtual bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE
{
bool call_match(const chaiscript::Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions))
if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions)) { {
return m_func->call_match(vals, t_conversions); return m_func->call_match(vals, t_conversions);
} else { } else {
return false; return false;
} }
} }
std::vector<Const_Proxy_Function> get_contained_functions() const override { return {m_func}; } virtual std::vector<Const_Proxy_Function> get_contained_functions() const CHAISCRIPT_OVERRIDE
{
return {m_func};
}
virtual std::string annotation() const CHAISCRIPT_OVERRIDE
{
return m_func->annotation();
}
protected: protected:
Boxed_Value do_call(const chaiscript::Function_Params &params, const Type_Conversions_State &t_conversions) const override { virtual Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE
if (dynamic_object_typename_match(params, m_type_name, m_ti, t_conversions)) { {
if (dynamic_object_typename_match(params, m_type_name, m_ti, t_conversions))
{
return (*m_func)(params, t_conversions); return (*m_func)(params, t_conversions);
} else { } else {
throw exception::guard_error(); throw exception::guard_error();
} }
} }
bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const noexcept override { virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE
{
return dynamic_object_typename_match(bv, m_type_name, m_ti, t_conversions); return dynamic_object_typename_match(bv, m_type_name, m_ti, t_conversions);
} }
private: private:
static std::vector<Type_Info> build_param_types(const std::vector<Type_Info> &t_inner_types, const Type_Info &t_objectti) { static std::vector<Type_Info> build_param_types(
const std::vector<Type_Info> &t_inner_types, const Type_Info& t_objectti)
{
std::vector<Type_Info> types(t_inner_types); std::vector<Type_Info> types(t_inner_types);
assert(types.size() > 1); assert(types.size() > 1);
//assert(types[1].bare_equal(user_type<Boxed_Value>())); //assert(types[1].bare_equal(user_type<Boxed_Value>()));
// When the object type_info is undefined (ChaiScript-defined class), use types[1] = t_objectti;
// Dynamic_Object so that dispatch priority scoring works correctly.
types[1] = t_objectti.is_undef() ? user_type<Dynamic_Object>() : t_objectti;
return types; return types;
} }
bool dynamic_object_typename_match(const Boxed_Value &bv, bool dynamic_object_typename_match(const Boxed_Value &bv, const std::string &name,
const std::string &name, const std::unique_ptr<Type_Info> &ti, const Type_Conversions &t_conversions) const
const std::unique_ptr<Type_Info> &ti, {
const Type_Conversions_State &t_conversions) const noexcept { if (bv.get_type_info().bare_equal(m_doti))
if (bv.get_type_info().bare_equal(m_doti)) { {
try { try {
const Dynamic_Object &d = boxed_cast<const Dynamic_Object &>(bv, &t_conversions); const Dynamic_Object &d = boxed_cast<const Dynamic_Object &>(bv, &t_conversions);
return name == "Dynamic_Object" || Dynamic_Object::type_matches(d.get_type_name(), name); return name == "Dynamic_Object" || d.get_type_name() == name;
} catch (const std::bad_cast &) { } catch (const std::bad_cast &) {
return false; return false;
} }
} else { } else {
if (ti) { if (ti)
{
return bv.get_type_info().bare_equal(*ti); return bv.get_type_info().bare_equal(*ti);
} else { } else {
return false; return false;
} }
} }
} }
bool dynamic_object_typename_match(const chaiscript::Function_Params &bvs, bool dynamic_object_typename_match(const std::vector<Boxed_Value> &bvs, const std::string &name,
const std::string &name, const std::unique_ptr<Type_Info> &ti, const Type_Conversions &t_conversions) const
const std::unique_ptr<Type_Info> &ti, {
const Type_Conversions_State &t_conversions) const noexcept { if (bvs.size() > 0)
if (!bvs.empty()) { {
return dynamic_object_typename_match(bvs[0], name, ti, t_conversions); return dynamic_object_typename_match(bvs[0], name, ti, t_conversions);
} else { } else {
return false; return false;
@ -144,64 +170,84 @@ namespace chaiscript {
Proxy_Function m_func; Proxy_Function m_func;
std::unique_ptr<Type_Info> m_ti; std::unique_ptr<Type_Info> m_ti;
const Type_Info m_doti; const Type_Info m_doti;
const bool m_is_attribute; bool m_is_attribute;
}; };
/** /**
* A Proxy_Function implementation designed for creating a new * A Proxy_Function implementation designed for creating a new
* Dynamic_Object * Dynamic_Object
* that is automatically guarded based on the first param based on the * that is automatically guarded based on the first param based on the
* param's type name * param's type name
*/ */
class Dynamic_Object_Constructor final : public Proxy_Function_Base { class Dynamic_Object_Constructor : public Proxy_Function_Base
{
public: public:
Dynamic_Object_Constructor(std::string t_type_name, const Proxy_Function &t_func) Dynamic_Object_Constructor(
: Proxy_Function_Base(build_type_list(t_func->get_param_types()), t_func->get_arity() - 1) std::string t_type_name,
, m_type_name(std::move(t_type_name)) const Proxy_Function &t_func)
, m_func(t_func) { : Proxy_Function_Base(build_type_list(t_func->get_param_types()), t_func->get_arity() - 1),
m_type_name(std::move(t_type_name)), m_func(t_func)
{
assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0)
&& "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); && "Programming error, Dynamic_Object_Function must have at least one parameter (this)");
} }
static std::vector<Type_Info> build_type_list(const std::vector<Type_Info> &tl) { static std::vector<Type_Info> build_type_list(const std::vector<Type_Info> &tl)
{
auto begin = tl.begin(); auto begin = tl.begin();
auto end = tl.end(); auto end = tl.end();
if (begin != end) { if (begin != end)
{
++begin; ++begin;
} }
return std::vector<Type_Info>(begin, end); return std::vector<Type_Info>(begin, end);
} }
bool operator==(const Proxy_Function_Base &f) const noexcept override { virtual ~Dynamic_Object_Constructor() {}
virtual bool operator==(const Proxy_Function_Base &f) const CHAISCRIPT_OVERRIDE
{
const Dynamic_Object_Constructor *dc = dynamic_cast<const Dynamic_Object_Constructor*>(&f); const Dynamic_Object_Constructor *dc = dynamic_cast<const Dynamic_Object_Constructor*>(&f);
return (dc != nullptr) && dc->m_type_name == m_type_name && (*dc->m_func) == (*m_func); return dc && dc->m_type_name == m_type_name && (*dc->m_func) == (*m_func);
} }
bool call_match(const chaiscript::Function_Params &vals, const Type_Conversions_State &t_conversions) const override { virtual bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE
{
std::vector<Boxed_Value> new_vals{Boxed_Value(Dynamic_Object(m_type_name))}; std::vector<Boxed_Value> new_vals{Boxed_Value(Dynamic_Object(m_type_name))};
new_vals.insert(new_vals.end(), vals.begin(), vals.end()); new_vals.insert(new_vals.end(), vals.begin(), vals.end());
return m_func->call_match(chaiscript::Function_Params{new_vals}, t_conversions); return m_func->call_match(new_vals, t_conversions);
}
virtual std::string annotation() const CHAISCRIPT_OVERRIDE
{
return m_func->annotation();
} }
protected: protected:
Boxed_Value do_call(const chaiscript::Function_Params &params, const Type_Conversions_State &t_conversions) const override { virtual Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE
{
auto bv = Boxed_Value(Dynamic_Object(m_type_name), true); auto bv = Boxed_Value(Dynamic_Object(m_type_name), true);
std::vector<Boxed_Value> new_params{bv}; std::vector<Boxed_Value> new_params{bv};
new_params.insert(new_params.end(), params.begin(), params.end()); new_params.insert(new_params.end(), params.begin(), params.end());
(*m_func)(chaiscript::Function_Params{new_params}, t_conversions); (*m_func)(new_params, t_conversions);
return bv; return bv;
} }
private: private:
const std::string m_type_name; std::string m_type_name;
const Proxy_Function m_func; Proxy_Function m_func;
}; };
} // namespace detail }
} // namespace dispatch }
} // namespace chaiscript }
#endif #endif

View File

@ -1,12 +1,9 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_ #ifndef CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_
#define CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_ #define CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_
@ -16,28 +13,93 @@
#include "boxed_cast.hpp" #include "boxed_cast.hpp"
namespace chaiscript { namespace chaiscript {
namespace detail { class Boxed_Value;
struct Exception_Handler_Base { namespace exception {
class bad_boxed_cast;
} // namespace exception
} // namespace chaiscript
namespace chaiscript
{
namespace detail
{
/// \todo make this a variadic template
struct Exception_Handler_Base
{
virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) = 0; virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) = 0;
virtual ~Exception_Handler_Base() = default; virtual ~Exception_Handler_Base() {}
protected: protected:
template<typename T> template<typename T>
static void throw_type(const Boxed_Value &bv, const Dispatch_Engine &t_engine) { void throw_type(const Boxed_Value &bv, const Dispatch_Engine &t_engine)
try { {
T t = t_engine.boxed_cast<T>(bv); try { T t = t_engine.boxed_cast<T>(bv); throw t; } catch (const chaiscript::exception::bad_boxed_cast &) {}
throw t;
} catch (const chaiscript::exception::bad_boxed_cast &) {
}
} }
}; };
template<typename... T> template<typename T1>
struct Exception_Handler_Impl : Exception_Handler_Base { struct Exception_Handler_Impl1 : Exception_Handler_Base
void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) override { (throw_type<T>(bv, t_engine), ...); } {
virtual ~Exception_Handler_Impl1() {}
virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) CHAISCRIPT_OVERRIDE
{
throw_type<T1>(bv, t_engine);
}
}; };
} // namespace detail template<typename T1, typename T2>
struct Exception_Handler_Impl2 : Exception_Handler_Base
{
virtual ~Exception_Handler_Impl2() {}
virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) CHAISCRIPT_OVERRIDE
{
throw_type<T1>(bv, t_engine);
throw_type<T2>(bv, t_engine);
}
};
template<typename T1, typename T2, typename T3>
struct Exception_Handler_Impl3 : Exception_Handler_Base
{
virtual ~Exception_Handler_Impl3() {}
virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) CHAISCRIPT_OVERRIDE
{
throw_type<T1>(bv, t_engine);
throw_type<T2>(bv, t_engine);
throw_type<T3>(bv, t_engine);
}
};
template<typename T1, typename T2, typename T3, typename T4>
struct Exception_Handler_Impl4 : Exception_Handler_Base
{
virtual ~Exception_Handler_Impl4() {}
virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) CHAISCRIPT_OVERRIDE
{
throw_type<T1>(bv, t_engine);
throw_type<T2>(bv, t_engine);
throw_type<T3>(bv, t_engine);
throw_type<T4>(bv, t_engine);
}
};
template<typename T1, typename T2, typename T3, typename T4, typename T5>
struct Exception_Handler_Impl5 : Exception_Handler_Base
{
virtual ~Exception_Handler_Impl5() {}
virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) CHAISCRIPT_OVERRIDE
{
throw_type<T1>(bv, t_engine);
throw_type<T2>(bv, t_engine);
throw_type<T3>(bv, t_engine);
throw_type<T4>(bv, t_engine);
throw_type<T5>(bv, t_engine);
}
};
}
/// \brief Used in the automatic unboxing of exceptions thrown during script evaluation /// \brief Used in the automatic unboxing of exceptions thrown during script evaluation
/// ///
@ -49,8 +111,7 @@ namespace chaiscript {
/// chaiscript::ChaiScript chai; /// chaiscript::ChaiScript chai;
/// ///
/// try { /// try {
/// chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification<int, double, float, const std::string &, const /// chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification<int, double, float, const std::string &, const std::exception &>());
/// std::exception &>());
/// } catch (const double e) { /// } catch (const double e) {
/// } catch (int) { /// } catch (int) {
/// } catch (float) { /// } catch (float) {
@ -90,14 +151,49 @@ namespace chaiscript {
/// ///
/// \sa chaiscript::exception_specification for creation of chaiscript::Exception_Handler objects /// \sa chaiscript::exception_specification for creation of chaiscript::Exception_Handler objects
/// \sa \ref exceptions /// \sa \ref exceptions
using Exception_Handler = std::shared_ptr<detail::Exception_Handler_Base>; typedef std::shared_ptr<detail::Exception_Handler_Base> Exception_Handler;
/// \brief creates a chaiscript::Exception_Handler which handles one type of exception unboxing /// \brief creates a chaiscript::Exception_Handler which handles one type of exception unboxing
/// \sa \ref exceptions /// \sa \ref exceptions
template<typename... T> template<typename T1>
Exception_Handler exception_specification() { Exception_Handler exception_specification()
return std::make_shared<detail::Exception_Handler_Impl<T...>>(); {
return Exception_Handler(new detail::Exception_Handler_Impl1<T1>());
} }
} // namespace chaiscript
/// \brief creates a chaiscript::Exception_Handler which handles two types of exception unboxing
/// \sa \ref exceptions
template<typename T1, typename T2>
Exception_Handler exception_specification()
{
return Exception_Handler(new detail::Exception_Handler_Impl2<T1, T2>());
}
/// \brief creates a chaiscript::Exception_Handler which handles three types of exception unboxing
/// \sa \ref exceptions
template<typename T1, typename T2, typename T3>
Exception_Handler exception_specification()
{
return Exception_Handler(new detail::Exception_Handler_Impl3<T1, T2, T3>());
}
/// \brief creates a chaiscript::Exception_Handler which handles four types of exception unboxing
/// \sa \ref exceptions
template<typename T1, typename T2, typename T3, typename T4>
Exception_Handler exception_specification()
{
return Exception_Handler(new detail::Exception_Handler_Impl4<T1, T2, T3, T4>());
}
/// \brief creates a chaiscript::Exception_Handler which handles five types of exception unboxing
/// \sa \ref exceptions
template<typename T1, typename T2, typename T3, typename T4, typename T5>
Exception_Handler exception_specification()
{
return Exception_Handler(new detail::Exception_Handler_Impl5<T1, T2, T3, T4, T5>());
}
}
#endif #endif

View File

@ -1,12 +1,9 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_FUNCTION_CALL_HPP_ #ifndef CHAISCRIPT_FUNCTION_CALL_HPP_
#define CHAISCRIPT_FUNCTION_CALL_HPP_ #define CHAISCRIPT_FUNCTION_CALL_HPP_
@ -17,25 +14,20 @@
#include "boxed_cast.hpp" #include "boxed_cast.hpp"
#include "function_call_detail.hpp" #include "function_call_detail.hpp"
#include "proxy_functions.hpp" #include "proxy_functions.hpp"
#include "callable_traits.hpp"
namespace chaiscript { namespace chaiscript {
class Boxed_Value; class Boxed_Value;
class Type_Conversions_State; class Type_Conversions;
namespace detail { namespace detail {
template<typename T> template <typename T> struct Cast_Helper;
struct Cast_Helper;
} // namespace detail } // namespace detail
} // namespace chaiscript } // namespace chaiscript
namespace chaiscript { namespace chaiscript
namespace dispatch { {
namespace detail { namespace dispatch
template<typename Ret, typename... Param> {
constexpr auto arity(Ret (*)(Param...)) noexcept {
return sizeof...(Param);
}
} // namespace detail
/// Build a function caller that knows how to dispatch on a set of functions /// Build a function caller that knows how to dispatch on a set of functions
/// example: /// example:
/// std::function<void (int)> f = /// std::function<void (int)> f =
@ -43,9 +35,12 @@ namespace chaiscript {
/// \returns A std::function object for dispatching /// \returns A std::function object for dispatching
/// \param[in] funcs the set of functions to dispatch on. /// \param[in] funcs the set of functions to dispatch on.
template<typename FunctionType> template<typename FunctionType>
std::function<FunctionType> functor(const std::vector<Const_Proxy_Function> &funcs, const Type_Conversions_State *t_conversions) { std::function<FunctionType>
const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(), [](const Const_Proxy_Function &f) { functor(const std::vector<Const_Proxy_Function> &funcs, const Type_Conversions *t_conversions)
return f->get_arity() == -1 || size_t(f->get_arity()) == detail::arity(static_cast<FunctionType *>(nullptr)); {
const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(),
[](const Const_Proxy_Function &f) {
return f->get_arity() == -1 || size_t(f->get_arity()) == chaiscript::dispatch::detail::Arity<FunctionType>::arity;
}); });
if (!has_arity_match) { if (!has_arity_match) {
@ -68,24 +63,33 @@ namespace chaiscript {
/// \returns A std::function object for dispatching /// \returns A std::function object for dispatching
/// \param[in] func A function to execute. /// \param[in] func A function to execute.
template<typename FunctionType> template<typename FunctionType>
std::function<FunctionType> functor(Const_Proxy_Function func, const Type_Conversions_State *t_conversions) { std::function<FunctionType>
functor(Const_Proxy_Function func, const Type_Conversions *t_conversions)
{
return functor<FunctionType>(std::vector<Const_Proxy_Function>({std::move(func)}), t_conversions); return functor<FunctionType>(std::vector<Const_Proxy_Function>({std::move(func)}), t_conversions);
} }
/// Helper for automatically unboxing a Boxed_Value that contains a function object /// Helper for automatically unboxing a Boxed_Value that contains a function object
/// and creating a typesafe C++ function caller from it. /// and creating a typesafe C++ function caller from it.
template<typename FunctionType> template<typename FunctionType>
std::function<FunctionType> functor(const Boxed_Value &bv, const Type_Conversions_State *t_conversions) { std::function<FunctionType>
functor(const Boxed_Value &bv, const Type_Conversions *t_conversions)
{
return functor<FunctionType>(boxed_cast<Const_Proxy_Function >(bv, t_conversions), t_conversions); return functor<FunctionType>(boxed_cast<Const_Proxy_Function >(bv, t_conversions), t_conversions);
} }
} // namespace dispatch }
namespace detail{ namespace detail{
/// Cast helper to handle automatic casting to const std::function & /// Cast helper to handle automatic casting to const std::function &
template<typename Signature> template<typename Signature>
struct Cast_Helper<const std::function<Signature> &> { struct Cast_Helper<const std::function<Signature> &>
static std::function<Signature> cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { {
if (ob.get_type_info().bare_equal(user_type<Const_Proxy_Function>())) { typedef std::function<Signature> Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *t_conversions)
{
if (ob.get_type_info().bare_equal(user_type<Const_Proxy_Function>()))
{
return dispatch::functor<Signature>(ob, t_conversions); return dispatch::functor<Signature>(ob, t_conversions);
} else { } else {
return Cast_Helper_Inner<const std::function<Signature> &>::cast(ob, t_conversions); return Cast_Helper_Inner<const std::function<Signature> &>::cast(ob, t_conversions);
@ -95,9 +99,14 @@ namespace chaiscript {
/// Cast helper to handle automatic casting to std::function /// Cast helper to handle automatic casting to std::function
template<typename Signature> template<typename Signature>
struct Cast_Helper<std::function<Signature>> { struct Cast_Helper<std::function<Signature> >
static std::function<Signature> cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { {
if (ob.get_type_info().bare_equal(user_type<Const_Proxy_Function>())) { typedef std::function<Signature> Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *t_conversions)
{
if (ob.get_type_info().bare_equal(user_type<Const_Proxy_Function>()))
{
return dispatch::functor<Signature>(ob, t_conversions); return dispatch::functor<Signature>(ob, t_conversions);
} else { } else {
return Cast_Helper_Inner<std::function<Signature> >::cast(ob, t_conversions); return Cast_Helper_Inner<std::function<Signature> >::cast(ob, t_conversions);
@ -107,16 +116,22 @@ namespace chaiscript {
/// Cast helper to handle automatic casting to const std::function /// Cast helper to handle automatic casting to const std::function
template<typename Signature> template<typename Signature>
struct Cast_Helper<const std::function<Signature>> { struct Cast_Helper<const std::function<Signature> >
static std::function<Signature> cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { {
if (ob.get_type_info().bare_equal(user_type<Const_Proxy_Function>())) { typedef std::function<Signature> Result_Type;
static Result_Type cast(const Boxed_Value &ob, const Type_Conversions *t_conversions)
{
if (ob.get_type_info().bare_equal(user_type<Const_Proxy_Function>()))
{
return dispatch::functor<Signature>(ob, t_conversions); return dispatch::functor<Signature>(ob, t_conversions);
} else { } else {
return Cast_Helper_Inner<const std::function<Signature> >::cast(ob, t_conversions); return Cast_Helper_Inner<const std::function<Signature> >::cast(ob, t_conversions);
} }
} }
}; };
} // namespace detail }
} // namespace chaiscript }
#endif #endif

View File

@ -1,12 +1,9 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_ #ifndef CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_
#define CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_ #define CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_
@ -19,74 +16,106 @@
#include "boxed_cast.hpp" #include "boxed_cast.hpp"
#include "boxed_number.hpp" #include "boxed_number.hpp"
#include "boxed_value.hpp" #include "boxed_value.hpp"
#include "proxy_functions.hpp"
#include "type_conversions.hpp" #include "type_conversions.hpp"
#include "proxy_functions.hpp"
namespace chaiscript::dispatch::detail { namespace chaiscript
/// used internally for unwrapping a function call's types {
namespace dispatch
{
namespace detail
{
/// Internal helper class for handling the return
/// value of a build_function_caller
template<typename Ret, bool is_arithmetic>
struct Function_Caller_Ret
{
static Ret call(const std::vector<Const_Proxy_Function> &t_funcs,
const std::vector<Boxed_Value> &params, const Type_Conversions *t_conversions)
{
return boxed_cast<Ret>(dispatch::dispatch(t_funcs, params, t_conversions?*t_conversions:Type_Conversions()), t_conversions);
}
};
/**
* Specialization for arithmetic return types
*/
template<typename Ret>
struct Function_Caller_Ret<Ret, true>
{
static Ret call(const std::vector<Const_Proxy_Function> &t_funcs,
const std::vector<Boxed_Value> &params, const Type_Conversions *t_conversions)
{
return Boxed_Number(dispatch::dispatch(t_funcs, params, t_conversions?*t_conversions:Type_Conversions())).get_as<Ret>();
}
};
/**
* Specialization for void return types
*/
template<>
struct Function_Caller_Ret<void, false>
{
static void call(const std::vector<Const_Proxy_Function> &t_funcs,
const std::vector<Boxed_Value> &params, const Type_Conversions *t_conversions)
{
dispatch::dispatch(t_funcs, params, t_conversions?*t_conversions:Type_Conversions());
}
};
/**
* used internally for unwrapping a function call's types
*/
template<typename Ret, typename ... Param> template<typename Ret, typename ... Param>
struct Build_Function_Caller_Helper { struct Build_Function_Caller_Helper
{
Build_Function_Caller_Helper(std::vector<Const_Proxy_Function> t_funcs, const Type_Conversions *t_conversions) Build_Function_Caller_Helper(std::vector<Const_Proxy_Function> t_funcs, const Type_Conversions *t_conversions)
: m_funcs(std::move(t_funcs)) : m_funcs(std::move(t_funcs)),
, m_conversions(t_conversions) { m_conversions(t_conversions)
} {
Ret call(const chaiscript::Function_Params &params, const Type_Conversions_State &t_state) {
if constexpr (std::is_arithmetic_v<Ret> && !std::is_same_v<std::remove_cv_t<std::remove_reference_t<Ret>>, bool>) {
return Boxed_Number(dispatch::dispatch(m_funcs, params, t_state)).get_as<Ret>();
} else if constexpr (std::is_same_v<void, Ret>) {
dispatch::dispatch(m_funcs, params, t_state);
} else {
return boxed_cast<Ret>(dispatch::dispatch(m_funcs, params, t_state), &t_state);
}
} }
template<typename ... P> template<typename ... P>
Ret operator()(P &&...param) { Ret operator()(P&& ... param)
std::array<Boxed_Value, sizeof...(P)> params{box<P>(std::forward<P>(param))...}; {
return Function_Caller_Ret<Ret, std::is_arithmetic<Ret>::value && !std::is_same<Ret, bool>::value>::call(m_funcs, {
box<P>(std::forward<P>(param))...
}, m_conversions
);
if (m_conversions) {
Type_Conversions_State state(*m_conversions, m_conversions->conversion_saves());
return call(chaiscript::Function_Params{params}, state);
} else {
Type_Conversions conv;
Type_Conversions_State state(conv, conv.conversion_saves());
return call(chaiscript::Function_Params{params}, state);
} }
}
template<typename T>
struct is_shared_ptr : std::false_type {};
template<typename T>
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
template<typename P, typename Q> template<typename P, typename Q>
static Boxed_Value box(Q &&q) { static auto box(Q&& q) -> typename std::enable_if<std::is_reference<P>::value&&!std::is_same<chaiscript::Boxed_Value, typename std::remove_const<typename std::remove_reference<P>::type>::type>::value, Boxed_Value>::type
using bare_p = std::remove_cv_t<std::remove_reference_t<P>>; {
if constexpr (std::is_same_v<chaiscript::Boxed_Value, std::decay_t<Q>>) {
return std::forward<Q>(q);
} else if constexpr (is_shared_ptr<bare_p>::value) {
// Pass shared_ptr arguments through by value rather than wrapping a
// reference. Boxed_Value's shared_ptr-aware overloads then record the
// bare type as the pointee, allowing dispatch to a C++ function that
// takes the same shared_ptr<T> on the other side.
return Boxed_Value(bare_p(std::forward<Q>(q)));
} else if constexpr (std::is_reference_v<P>) {
return Boxed_Value(std::ref(std::forward<Q>(q))); return Boxed_Value(std::ref(std::forward<Q>(q)));
} else { }
template<typename P, typename Q>
static auto box(Q&& q) -> typename std::enable_if<!std::is_reference<P>::value&&!std::is_same<chaiscript::Boxed_Value, typename std::remove_const<typename std::remove_reference<P>::type>::type>::value, Boxed_Value>::type
{
return Boxed_Value(std::forward<Q>(q)); return Boxed_Value(std::forward<Q>(q));
} }
template<typename P>
static Boxed_Value box(Boxed_Value bv)
{
return bv;
} }
std::vector<Const_Proxy_Function> m_funcs; std::vector<Const_Proxy_Function> m_funcs;
const Type_Conversions *m_conversions; const Type_Conversions *m_conversions;
}; };
/// \todo what happens if t_conversions is deleted out from under us?! /// \todo what happens if t_conversions is deleted out from under us?!
template<typename Ret, typename ... Params> template<typename Ret, typename ... Params>
std::function<Ret(Params...)> std::function<Ret (Params...)> build_function_caller_helper(Ret (Params...), const std::vector<Const_Proxy_Function> &funcs, const Type_Conversions *t_conversions)
build_function_caller_helper(Ret(Params...), const std::vector<Const_Proxy_Function> &funcs, const Type_Conversions_State *t_conversions) { {
/* /*
if (funcs.size() == 1) if (funcs.size() == 1)
{ {
@ -103,8 +132,11 @@ namespace chaiscript::dispatch::detail {
} }
*/ */
return std::function<Ret(Params...)>(Build_Function_Caller_Helper<Ret, Params...>(funcs, t_conversions ? t_conversions->get() : nullptr)); return std::function<Ret (Params...)>(Build_Function_Caller_Helper<Ret, Params...>(funcs, t_conversions));
}
}
}
} }
} // namespace chaiscript::dispatch::detail
#endif #endif

View File

@ -1,23 +0,0 @@
// 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
#ifndef CHAISCRIPT_FUNCTION_PARAMS_HPP
#define CHAISCRIPT_FUNCTION_PARAMS_HPP
#include "boxed_value.hpp"
#include <span>
namespace chaiscript {
using Function_Params = std::span<const Boxed_Value>;
} // namespace chaiscript
#endif

View File

@ -1,149 +0,0 @@
#ifndef CHAISCRIPT_FUNCTION_SIGNATURE_HPP
#define CHAISCRIPT_FUNCTION_SIGNATURE_HPP
#include <type_traits>
namespace chaiscript::dispatch::detail {
template<typename... Param>
struct Function_Params {
};
template<typename Ret, typename Params, bool IsNoExcept = false, bool IsMember = false, bool IsMemberObject = false, bool IsObject = false>
struct Function_Signature {
using Param_Types = Params;
using Return_Type = Ret;
constexpr static const bool is_object = IsObject;
constexpr static const bool is_member_object = IsMemberObject;
constexpr static const bool is_noexcept = IsNoExcept;
template<typename T>
constexpr Function_Signature(T &&) noexcept {
}
constexpr Function_Signature() noexcept = default;
};
// Free functions
template<typename Ret, typename... Param>
Function_Signature(Ret (*f)(Param...)) -> Function_Signature<Ret, Function_Params<Param...>>;
template<typename Ret, typename... Param>
Function_Signature(Ret (*f)(Param...) noexcept) -> Function_Signature<Ret, Function_Params<Param...>, true>;
// no reference specifier
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile) -> Function_Signature<Ret, Function_Params<volatile Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile noexcept)
-> Function_Signature<Ret, Function_Params<volatile Class &, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile const)
-> Function_Signature<Ret, Function_Params<volatile const Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile const noexcept)
-> Function_Signature<Ret, Function_Params<volatile const Class &, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...)) -> Function_Signature<Ret, Function_Params<Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) noexcept) -> Function_Signature<Ret, Function_Params<Class &, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) const) -> Function_Signature<Ret, Function_Params<const Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) const noexcept) -> Function_Signature<Ret, Function_Params<const Class &, Param...>, true, true>;
// & reference specifier
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile &) -> Function_Signature<Ret, Function_Params<volatile Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile & noexcept)
-> Function_Signature<Ret, Function_Params<volatile Class &, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile const &)
-> Function_Signature<Ret, Function_Params<volatile const Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile const & noexcept)
-> Function_Signature<Ret, Function_Params<volatile const Class &, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) &) -> Function_Signature<Ret, Function_Params<Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) & noexcept) -> Function_Signature<Ret, Function_Params<Class &, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) const &) -> Function_Signature<Ret, Function_Params<const Class &, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) const & noexcept) -> Function_Signature<Ret, Function_Params<const Class &, Param...>, true, true>;
// && reference specifier
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile &&) -> Function_Signature<Ret, Function_Params<volatile Class &&, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile && noexcept)
-> Function_Signature<Ret, Function_Params<volatile Class &&, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile const &&)
-> Function_Signature<Ret, Function_Params<volatile const Class &&, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) volatile const && noexcept)
-> Function_Signature<Ret, Function_Params<volatile const Class &&, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) &&) -> Function_Signature<Ret, Function_Params<Class &&, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) && noexcept) -> Function_Signature<Ret, Function_Params<Class &&, Param...>, true, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) const &&) -> Function_Signature<Ret, Function_Params<const Class &&, Param...>, false, true>;
template<typename Ret, typename Class, typename... Param>
Function_Signature(Ret (Class::*f)(Param...) const && noexcept)
-> Function_Signature<Ret, Function_Params<const Class &&, Param...>, true, true>;
template<typename Ret, typename Class>
Function_Signature(Ret Class::*f) -> Function_Signature<Ret, Function_Params<Class &>, true, true, true>;
// primary template handles types that have no nested ::type member:
template<class, class = std::void_t<>>
struct has_call_operator : std::false_type {
};
// specialization recognizes types that do have a nested ::type member:
template<class T>
struct has_call_operator<T, std::void_t<decltype(&T::operator())>> : std::true_type {
};
template<typename Func>
auto function_signature(const Func &f) {
if constexpr (has_call_operator<Func>::value) {
return Function_Signature<typename decltype(Function_Signature{&std::decay_t<Func>::operator()})::Return_Type,
typename decltype(Function_Signature{&std::decay_t<Func>::operator()})::Param_Types,
decltype(Function_Signature{&std::decay_t<Func>::operator()})::is_noexcept,
false,
false,
true>{};
} else {
return Function_Signature{f};
}
}
} // namespace chaiscript::dispatch::detail
#endif

View File

@ -1,12 +1,9 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_HANDLE_RETURN_HPP_ #ifndef CHAISCRIPT_HANDLE_RETURN_HPP_
#define CHAISCRIPT_HANDLE_RETURN_HPP_ #define CHAISCRIPT_HANDLE_RETURN_HPP_
@ -21,175 +18,261 @@ namespace chaiscript {
class Boxed_Number; class Boxed_Number;
} // namespace chaiscript } // namespace chaiscript
namespace chaiscript { namespace chaiscript
namespace dispatch { {
template<class T, class U> namespace dispatch
class Proxy_Function_Callable_Impl; {
template<class T> template<class T, class U> class Proxy_Function_Callable_Impl;
class Assignable_Proxy_Function_Impl; template<class T> class Assignable_Proxy_Function_Impl;
namespace detail { namespace detail
/// Used internally for handling a return value from a Proxy_Function call {
/**
* Used internally for handling a return value from a Proxy_Function call
*/
template<typename Ret> template<typename Ret>
struct Handle_Return { struct Handle_Return
template<typename T, typename = typename std::enable_if_t<std::is_trivial_v<typename std::decay_t<T>>>> {
static Boxed_Value handle(T r) { template<typename T,
typename = typename std::enable_if<std::is_pod<typename std::decay<T>::type>::value>::type>
static Boxed_Value handle(T r)
{
return Boxed_Value(std::move(r), true); return Boxed_Value(std::move(r), true);
} }
template<typename T, typename = typename std::enable_if_t<!(std::is_trivial_v<typename std::decay_t<T>>)>> template<typename T,
static Boxed_Value handle(T &&r) { typename = typename std::enable_if<!std::is_pod<typename std::decay<T>::type>::value>::type>
static Boxed_Value handle(T &&r)
{
return Boxed_Value(std::make_shared<T>(std::forward<T>(r)), true); return Boxed_Value(std::make_shared<T>(std::forward<T>(r)), true);
} }
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return<const std::function<Ret> &> { struct Handle_Return<const std::function<Ret> &>
{
static Boxed_Value handle(const std::function<Ret> &f) { static Boxed_Value handle(const std::function<Ret> &f) {
return Boxed_Value( return Boxed_Value(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret, std::function<Ret>>>(f)); chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret, std::function<Ret>>>(f)
);
} }
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return<std::function<Ret>> : Handle_Return<const std::function<Ret> &> { struct Handle_Return<std::function<Ret>>
{
static Boxed_Value handle(const std::function<Ret> &f) {
return Boxed_Value(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret, std::function<Ret>>>(f)
);
}
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return<const std::shared_ptr<std::function<Ret>>> { struct Handle_Return<const std::shared_ptr<std::function<Ret>>>
{
static Boxed_Value handle(const std::shared_ptr<std::function<Ret>> &f) { static Boxed_Value handle(const std::shared_ptr<std::function<Ret>> &f) {
return Boxed_Value( return Boxed_Value(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function_Impl<Ret>>(std::ref(*f), f)); chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function_Impl<Ret>>(std::ref(*f),f)
);
} }
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return<const std::shared_ptr<std::function<Ret>> &> : Handle_Return<const std::shared_ptr<std::function<Ret>>> { struct Handle_Return<const std::shared_ptr<std::function<Ret>> &>
{
static Boxed_Value handle(const std::shared_ptr<std::function<Ret>> &f) {
return Boxed_Value(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function_Impl<Ret>>(std::ref(*f),f)
);
}
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return<std::shared_ptr<std::function<Ret>>> : Handle_Return<const std::shared_ptr<std::function<Ret>>> { struct Handle_Return<std::shared_ptr<std::function<Ret>>>
{
static Boxed_Value handle(const std::shared_ptr<std::function<Ret>> &f) {
return Boxed_Value(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function_Impl<Ret>>(std::ref(*f),f)
);
}
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return<std::function<Ret> &> { struct Handle_Return<std::function<Ret> &>
{
static Boxed_Value handle(std::function<Ret> &f) { static Boxed_Value handle(std::function<Ret> &f) {
return Boxed_Value(chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function_Impl<Ret>>( return Boxed_Value(
std::ref(f), std::shared_ptr<std::function<Ret>>())); chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function_Impl<Ret>>(std::ref(f),
std::shared_ptr<std::function<Ret>>())
);
} }
static Boxed_Value handle(const std::function<Ret> &f) { static Boxed_Value handle(const std::function<Ret> &f) {
return Boxed_Value( return Boxed_Value(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret, std::function<Ret>>>(f)); chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret, std::function<Ret>>>(f)
);
} }
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return<Ret *&> { struct Handle_Return<Ret *>
static Boxed_Value handle(Ret *p) { return Boxed_Value(p, true); } {
}; static Boxed_Value handle(Ret *p)
{
template<typename Ret> return Boxed_Value(p, true);
struct Handle_Return<const Ret *&> {
static Boxed_Value handle(const Ret *p) { return Boxed_Value(p, true); }
};
template<typename Ret>
struct Handle_Return<Ret *> {
static Boxed_Value handle(Ret *p) { return Boxed_Value(p, true); }
};
template<typename Ret>
struct Handle_Return<const Ret *> {
static Boxed_Value handle(const Ret *p) { return Boxed_Value(p, true); }
};
template<typename Ret>
struct Handle_Return<std::shared_ptr<Ret> &> {
static Boxed_Value handle(const std::shared_ptr<Ret> &r) { return Boxed_Value(r, true); }
};
template<typename Ret>
struct Handle_Return<std::shared_ptr<Ret>> : Handle_Return<std::shared_ptr<Ret> &> {
};
template<typename Ret>
struct Handle_Return<const std::shared_ptr<Ret> &> : Handle_Return<std::shared_ptr<Ret> &> {
};
template<typename Ret>
struct Handle_Return<std::unique_ptr<Ret>> : Handle_Return<std::unique_ptr<Ret> &> {
static Boxed_Value handle(std::unique_ptr<Ret> &&r) { return Boxed_Value(std::move(r), true); }
};
template<typename Ret, bool Ptr>
struct Handle_Return_Ref {
template<typename T>
static Boxed_Value handle(T &&r) {
return Boxed_Value(std::cref(r), false);
} }
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return_Ref<Ret, true> { struct Handle_Return<const Ret *>
template<typename T> {
static Boxed_Value handle(T &&r) { static Boxed_Value handle(const Ret *p)
return Boxed_Value(typename std::remove_reference<decltype(r)>::type{r}, true); {
return Boxed_Value(p, true);
} }
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return<const Ret &> : Handle_Return_Ref<const Ret &, std::is_pointer<typename std::remove_reference<const Ret &>::type>::value> { struct Handle_Return<std::shared_ptr<Ret> &>
{
static Boxed_Value handle(const std::shared_ptr<Ret> &r)
{
return Boxed_Value(r, true);
}
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return<const Ret> { struct Handle_Return<std::shared_ptr<Ret> >
static Boxed_Value handle(Ret r) { return Boxed_Value(std::move(r)); } {
static Boxed_Value handle(const std::shared_ptr<Ret> &r)
{
return Boxed_Value(r, true);
}
}; };
template<typename Ret> template<typename Ret>
struct Handle_Return<Ret &> { struct Handle_Return<const std::shared_ptr<Ret> &>
static Boxed_Value handle(Ret &r) { return Boxed_Value(std::ref(r)); } {
static Boxed_Value handle(const std::shared_ptr<Ret> &r)
{
return Boxed_Value(r, true);
}
}; };
template<> template<typename Ret>
struct Handle_Return<Boxed_Value> { struct Handle_Return<const Ret &>
static Boxed_Value handle(const Boxed_Value &r) noexcept { return r; } {
static Boxed_Value handle(const Ret &r)
{
return Boxed_Value(std::cref(r), true);
}
}; };
template<>
struct Handle_Return<const Boxed_Value> : Handle_Return<Boxed_Value> {
};
template<> /**
struct Handle_Return<Boxed_Value &> : Handle_Return<Boxed_Value> { * Used internally for handling a return value from a Proxy_Function call
}; */
template<typename Ret>
struct Handle_Return<Ret &>
{
static Boxed_Value handle(Ret &r)
{
return Boxed_Value(std::ref(r));
}
template<> static Boxed_Value handle(const Ret &r)
struct Handle_Return<const Boxed_Value &> : Handle_Return<Boxed_Value> { {
return Boxed_Value(std::cref(r));
}
}; };
/** /**
* Used internally for handling a return value from a Proxy_Function call * Used internally for handling a return value from a Proxy_Function call
*/ */
template<> template<>
struct Handle_Return<Boxed_Number> { struct Handle_Return<Boxed_Value>
static Boxed_Value handle(const Boxed_Number &r) noexcept { return r.bv; } {
}; static Boxed_Value handle(const Boxed_Value &r)
{
template<> return r;
struct Handle_Return<const Boxed_Number> : Handle_Return<Boxed_Number> { }
}; };
/** /**
* Used internally for handling a return value from a Proxy_Function call * Used internally for handling a return value from a Proxy_Function call
*/ */
template<> template<>
struct Handle_Return<void> { struct Handle_Return<const Boxed_Value>
static Boxed_Value handle() { return void_var(); } {
static Boxed_Value handle(const Boxed_Value &r)
{
return r;
}
}; };
} // namespace detail
} // namespace dispatch /**
} // namespace chaiscript * Used internally for handling a return value from a Proxy_Function call
*/
template<>
struct Handle_Return<Boxed_Value &>
{
static Boxed_Value handle(const Boxed_Value &r)
{
return r;
}
};
/**
* Used internally for handling a return value from a Proxy_Function call
*/
template<>
struct Handle_Return<const Boxed_Value &>
{
static Boxed_Value handle(const Boxed_Value &r)
{
return r;
}
};
/**
* Used internally for handling a return value from a Proxy_Function call
*/
template<>
struct Handle_Return<Boxed_Number>
{
static Boxed_Value handle(const Boxed_Number &r)
{
return r.bv;
}
};
/**
* Used internally for handling a return value from a Proxy_Function call
*/
template<>
struct Handle_Return<const Boxed_Number>
{
static Boxed_Value handle(const Boxed_Number &r)
{
return r.bv;
}
};
/**
* Used internally for handling a return value from a Proxy_Function call
*/
template<>
struct Handle_Return<void>
{
static Boxed_Value handle()
{
return Boxed_Value(Boxed_Value::Void_Type());
}
};
}
}
}
#endif #endif

View File

@ -1,184 +1,465 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_OPERATORS_HPP_ #ifndef CHAISCRIPT_OPERATORS_HPP_
#define CHAISCRIPT_OPERATORS_HPP_ #define CHAISCRIPT_OPERATORS_HPP_
#include "../chaiscript_defines.hpp" #include "../chaiscript_defines.hpp"
#include "../dispatchkit/dispatchkit.hpp"
#include "register_function.hpp" #include "register_function.hpp"
namespace chaiscript::bootstrap::operators { namespace chaiscript
template<typename T, typename ModuleType> {
void assign(ModuleType &m) { namespace bootstrap
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs = rhs; }), "="); {
namespace operators
{
namespace detail
{
/// \todo make this return a decltype once we drop gcc 4.6
template<typename L, typename R>
auto assign(L l, R r) -> L&
{
return (l = r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void assign_bitwise_and(ModuleType &m) { auto assign_bitwise_and(L l, R r) -> decltype((l &= r))
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs &= rhs; }), "&="); {
return (l &= r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void assign_xor(ModuleType &m) { auto assign_xor(L l, R r) -> decltype((l^=r))
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs ^= rhs; }), "^="); {
return (l ^= r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void assign_bitwise_or(ModuleType &m) { auto assign_bitwise_or(L l, R r) -> decltype((l |= r))
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs |= rhs; }), "|="); {
return (l |= r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void assign_difference(ModuleType &m) { auto assign_difference(L l, R r) -> decltype(( l -= r))
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs -= rhs; }), "-="); {
return (l -= r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void assign_left_shift(ModuleType &m) { auto assign_left_shift(L l, R r) -> decltype(( l <<= r))
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs <<= rhs; }), "<<="); {
return (l <<= r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void assign_product(ModuleType &m) { auto assign_product(L l, R r) -> decltype(( l *= r ))
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs <<= rhs; }), "*="); {
return (l *= r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void assign_quotient(ModuleType &m) { auto assign_quotient(L l, R r) -> decltype(( l /= r ))
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs /= rhs; }), "/="); {
return (l /= r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void assign_remainder(ModuleType &m) { auto assign_remainder(L l, R r) -> decltype(( l %= r ))
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs %= rhs; }), "%="); {
return (l %= r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void assign_right_shift(ModuleType &m) { auto assign_right_shift(L l, R r) -> decltype(( l >>= r))
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs >>= rhs; }), ">>="); {
return (l >>= r);
} }
template<typename T, typename ModuleType> /// \todo make this return a decltype once we drop gcc 4.6
void assign_sum(ModuleType &m) { template<typename L, typename R>
m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs += rhs; }), "+="); auto assign_sum(L l, R r) -> L&
{
return (l += r);
} }
template<typename T, typename ModuleType> template<typename L>
void prefix_decrement(ModuleType &m) { auto prefix_decrement(L l) -> decltype(( --l ))
m.add(chaiscript::fun([](T &lhs) -> T & { return --lhs; }), "--"); {
return (--l);
} }
template<typename T, typename ModuleType> template<typename L>
void prefix_increment(ModuleType &m) { auto prefix_increment(L l) -> decltype(( ++l ))
m.add(chaiscript::fun([](T &lhs) -> T & { return ++lhs; }), "++"); {
return (++l);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void equal(ModuleType &m) { auto equal(L l, R r) -> decltype(( l == r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs == rhs; }), "=="); {
return (l == r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void greater_than(ModuleType &m) { auto greater_than(L l, R r) -> decltype(( l > r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs > rhs; }), ">"); {
return (l > r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void greater_than_equal(ModuleType &m) { auto greater_than_equal(L l, R r) -> decltype(( l >= r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs >= rhs; }), ">="); {
return (l >= r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void less_than(ModuleType &m) { auto less_than(L l, R r) -> decltype(( l < r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs < rhs; }), "<"); {
return (l < r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void less_than_equal(ModuleType &m) { auto less_than_equal(L l, R r) -> decltype(( l <= r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs <= rhs; }), "<="); {
return (l <= r);
} }
template<typename T, typename ModuleType> template<typename L>
void logical_compliment(ModuleType &m) { auto logical_compliment(L l) -> decltype(( !l ))
m.add(chaiscript::fun([](const T &lhs) { return !lhs; }), "!"); {
return (!l);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void not_equal(ModuleType &m) { auto not_equal(L l, R r) -> decltype(( l != r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs != rhs; }), "!="); {
return (l != r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void addition(ModuleType &m) { auto addition(L l, R r) -> decltype(( l + r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs + rhs; }), "+"); {
return (l + r);
} }
template<typename T, typename ModuleType> template<typename L>
void unary_plus(ModuleType &m) { auto unary_plus(L l) -> decltype(( +l ))
m.add(chaiscript::fun([](const T &lhs) { return +lhs; }), "+"); {
return (+l);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void subtraction(ModuleType &m) { auto subtraction(L l, R r) -> decltype(( l - r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs - rhs; }), "-"); {
return (l - r);
} }
template<typename T, typename ModuleType> template<typename L>
void unary_minus(ModuleType &m) { auto unary_minus(L l) -> decltype(( -l ))
m.add(chaiscript::fun([](const T &lhs) { return -lhs; }), "-"); {
#ifdef CHAISCRIPT_MSVC
#pragma warning(push)
#pragma warning(disable : 4146)
return (-l);
#pragma warning(pop)
#else
return (-l);
#endif
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void bitwise_and(ModuleType &m) { auto bitwise_and(L l, R r) -> decltype(( l & r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs & rhs; }), "&"); {
return (l & r);
} }
template<typename T, typename ModuleType> template<typename L>
void bitwise_compliment(ModuleType &m) { auto bitwise_compliment(L l) -> decltype(( ~l ))
m.add(chaiscript::fun([](const T &lhs) { return ~lhs; }), "~"); {
return (~l);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void bitwise_xor(ModuleType &m) { auto bitwise_xor(L l, R r) -> decltype(( l ^ r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs ^ rhs; }), "^"); {
return (l ^ r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void bitwise_or(ModuleType &m) { auto bitwise_or(L l, R r) -> decltype(( l | r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs | rhs; }), "|"); {
return (l | r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void division(ModuleType &m) { auto division(L l, R r) -> decltype(( l / r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs / rhs; }), "/"); {
return (l / r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void left_shift(ModuleType &m) { auto left_shift(L l, R r) -> decltype(( l << r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs << rhs; }), "<<"); {
return l << r;
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void multiplication(ModuleType &m) { auto multiplication(L l, R r) -> decltype(( l * r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs * rhs; }), "*"); {
return l * r;
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void remainder(ModuleType &m) { auto remainder(L l, R r) -> decltype(( l % r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs % rhs; }), "%"); {
return (l % r);
} }
template<typename T, typename ModuleType> template<typename L, typename R>
void right_shift(ModuleType &m) { auto right_shift(L l, R r) -> decltype(( l >> r ))
m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs >> rhs; }), ">>"); {
return (l >> r);
}
}
template<typename T>
ModulePtr assign(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign<T &, const T&>), "=");
return m;
}
template<typename T>
ModulePtr assign_bitwise_and(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign_bitwise_and<T &, const T&>), "&=");
return m;
}
template<typename T>
ModulePtr assign_xor(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign_xor<T &, const T&>), "^=");
return m;
}
template<typename T>
ModulePtr assign_bitwise_or(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign_bitwise_or<T &, const T&>), "|=");
return m;
}
template<typename T>
ModulePtr assign_difference(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign_difference<T &, const T&>), "-=");
return m;
}
template<typename T>
ModulePtr assign_left_shift(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign_left_shift<T &, const T&>), "<<=");
return m;
}
template<typename T>
ModulePtr assign_product(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign_product<T &, const T&>), "*=");
return m;
}
template<typename T>
ModulePtr assign_quotient(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign_quotient<T &, const T&>), "/=");
return m;
}
template<typename T>
ModulePtr assign_remainder(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign_remainder<T &, const T&>), "%=");
return m;
}
template<typename T>
ModulePtr assign_right_shift(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign_right_shift<T &, const T&>), ">>=");
return m;
}
template<typename T>
ModulePtr assign_sum(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::assign_sum<T &, const T&>), "+=");
return m;
}
template<typename T>
ModulePtr prefix_decrement(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::prefix_decrement<T &>), "--");
return m;
}
template<typename T>
ModulePtr prefix_increment(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::prefix_increment<T &>), "++");
return m;
}
template<typename T>
ModulePtr equal(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::equal<const T&, const T&>), "==");
return m;
}
template<typename T>
ModulePtr greater_than(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::greater_than<const T&, const T&>), ">");
return m;
}
template<typename T>
ModulePtr greater_than_equal(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::greater_than_equal<const T&, const T&>), ">=");
return m;
}
template<typename T>
ModulePtr less_than(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::less_than<const T&, const T&>), "<");
return m;
}
template<typename T>
ModulePtr less_than_equal(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::less_than_equal<const T&, const T&>), "<=");
return m;
}
template<typename T>
ModulePtr logical_compliment(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::logical_compliment<const T &>), "!");
return m;
}
template<typename T>
ModulePtr not_equal(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::not_equal<const T &, const T &>), "!=");
return m;
}
template<typename T>
ModulePtr addition(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::addition<const T &, const T &>), "+");
return m;
}
template<typename T>
ModulePtr unary_plus(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::unary_plus<const T &>), "+");
return m;
}
template<typename T>
ModulePtr subtraction(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::subtraction<const T &, const T &>), "-");
return m;
}
template<typename T>
ModulePtr unary_minus(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::unary_minus<const T &>), "-");
return m;
}
template<typename T>
ModulePtr bitwise_and(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::bitwise_and<const T &, const T &>), "&");
return m;
}
template<typename T>
ModulePtr bitwise_compliment(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::bitwise_compliment<const T &>), "~");
return m;
}
template<typename T>
ModulePtr bitwise_xor(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::bitwise_xor<const T &, const T &>), "^");
return m;
}
template<typename T>
ModulePtr bitwise_or(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::bitwise_or<const T &, const T &>), "|");
return m;
}
template<typename T>
ModulePtr division(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::division<const T &, const T &>), "/");
return m;
}
template<typename T>
ModulePtr left_shift(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::left_shift<const T &, const T &>), "<<");
return m;
}
template<typename T>
ModulePtr multiplication(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::multiplication<const T &, const T &>), "*");
return m;
}
template<typename T>
ModulePtr remainder(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::remainder<const T &, const T &>), "%");
return m;
}
template<typename T>
ModulePtr right_shift(ModulePtr m = std::make_shared<Module>())
{
m->add(chaiscript::fun(&detail::right_shift<const T &, const T &>), ">>");
return m;
}
}
}
} }
} // namespace chaiscript::bootstrap::operators
#endif #endif

View File

@ -1,37 +1,34 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_ #ifndef CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_
#define CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_ #define CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_
#include "proxy_functions.hpp" #include "proxy_functions.hpp"
namespace chaiscript::dispatch::detail { namespace chaiscript
{
namespace dispatch
{
namespace detail
{
template<typename Class, typename ... Params > template<typename Class, typename ... Params >
Proxy_Function build_constructor_(Class (*)(Params...)) { Proxy_Function build_constructor_(Class (*)(Params...))
if constexpr (!std::is_copy_constructible_v<Class>) { {
auto call = [](auto &&...param) { return std::make_shared<Class>(std::forward<decltype(param)>(param)...); }; auto call = dispatch::detail::Constructor<Class, Params...>();
return Proxy_Function( return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<std::shared_ptr<Class> (Params...), decltype(call)>>(call));
dispatch::Proxy_Function_Callable_Impl<std::shared_ptr<Class>(Params...), decltype(call)>>(call)); }
} else if constexpr (true) {
auto call = [](auto &&...param) { return Class(std::forward<decltype(param)>(param)...); };
return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Class(Params...), decltype(call)>>(
call));
} }
} }
} // namespace chaiscript::dispatch::detail
namespace chaiscript {
/// \brief Generates a constructor function for use with ChaiScript /// \brief Generates a constructor function for use with ChaiScript
/// ///
/// \tparam T The signature of the constructor to generate. In the form of: ClassType (ParamType1, ParamType2, ...) /// \tparam T The signature of the constructor to generate. In the form of: ClassType (ParamType1, ParamType2, ...)
@ -44,10 +41,13 @@ namespace chaiscript {
/// chai.add(constructor<MyClass (int, float)>(), "MyClass"); /// chai.add(constructor<MyClass (int, float)>(), "MyClass");
/// \endcode /// \endcode
template<typename T> template<typename T>
Proxy_Function constructor() { Proxy_Function constructor()
{
T *f = nullptr; T *f = nullptr;
return (dispatch::detail::build_constructor_(f)); return (dispatch::detail::build_constructor_(f));
} }
} // namespace chaiscript
}
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,12 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_ #ifndef CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_
#define CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_ #define CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_
#include <array>
#include <functional> #include <functional>
#include <stdexcept> #include <stdexcept>
#include <vector> #include <vector>
@ -18,104 +14,262 @@
#include "../chaiscript_defines.hpp" #include "../chaiscript_defines.hpp"
#include "boxed_cast.hpp" #include "boxed_cast.hpp"
#include "boxed_value.hpp" #include "boxed_value.hpp"
#include "function_params.hpp"
#include "handle_return.hpp" #include "handle_return.hpp"
#include "type_info.hpp" #include "type_info.hpp"
#include "callable_traits.hpp"
namespace chaiscript { namespace chaiscript {
class Type_Conversions_State; class Type_Conversions;
namespace exception { namespace exception {
class bad_boxed_cast; class bad_boxed_cast;
} // namespace exception } // namespace exception
} // namespace chaiscript } // namespace chaiscript
namespace chaiscript { namespace chaiscript
namespace exception { {
namespace exception
{
/** /**
* Exception thrown when there is a mismatch in number of * Exception thrown when there is a mismatch in number of
* parameters during Proxy_Function execution * parameters during Proxy_Function execution
*/ */
struct arity_error : std::range_error { struct arity_error : std::range_error
{
arity_error(int t_got, int t_expected) arity_error(int t_got, int t_expected)
: std::range_error("Function dispatch arity mismatch") : std::range_error("Function dispatch arity mismatch"),
, got(t_got) got(t_got), expected(t_expected)
, expected(t_expected) { {
} }
arity_error(const arity_error &) = default; arity_error(const arity_error &) = default;
~arity_error() noexcept override = default; virtual ~arity_error() CHAISCRIPT_NOEXCEPT {}
int got; int got;
int expected; int expected;
}; };
} // namespace exception }
namespace dispatch { namespace dispatch
namespace detail { {
namespace detail
{
/** /**
* Used by Proxy_Function_Impl to return a list of all param types * Used by Proxy_Function_Impl to return a list of all param types
* it contains. * it contains.
*/ */
template<typename Ret, typename ... Params> template<typename Ret, typename ... Params>
std::vector<Type_Info> build_param_type_list(Ret (*)(Params...)) { std::vector<Type_Info> build_param_type_list(Ret (*)(Params...))
{
/// \note somehow this is responsible for a large part of the code generation /// \note somehow this is responsible for a large part of the code generation
return { user_type<Ret>(), user_type<Params>()... }; return { user_type<Ret>(), user_type<Params>()... };
} }
#ifdef CHAISCRIPT_GCC_4_6
/// \todo REMOVE THIS WHEN WE DROP G++4.6
// Forward declaration
template<typename ... Rest>
struct Try_Cast;
template<typename Param, typename ... Rest>
struct Try_Cast<Param, Rest...>
{
static void do_try(const std::vector<Boxed_Value> &params, size_t generation, const Type_Conversions &t_conversions)
{
boxed_cast<Param>(params[generation], &t_conversions);
Try_Cast<Rest...>::do_try(params, generation+1, t_conversions);
}
};
// 0th case
template<>
struct Try_Cast<>
{
static void do_try(const std::vector<Boxed_Value> &, size_t, const Type_Conversions &)
{
}
};
/** /**
* Used by Proxy_Function_Impl to determine if it is equivalent to another * Used by Proxy_Function_Impl to determine if it is equivalent to another
* Proxy_Function_Impl object. This function is primarily used to prevent * Proxy_Function_Impl object. This function is primarily used to prevent
* registration of two functions with the exact same signatures * registration of two functions with the exact same signatures
*/ */
template<typename Ret, typename ... Params> template<typename Ret, typename ... Params>
bool compare_types_cast(Ret (*)(Params...), const chaiscript::Function_Params &params, const Type_Conversions_State &t_conversions) noexcept { bool compare_types_cast(Ret (*)(Params...),
const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions)
{
try { try {
std::vector<Boxed_Value>::size_type i = 0; Try_Cast<Params...>::do_try(params, 0, t_conversions);
(boxed_cast<Params>(params[i++], &t_conversions), ...); } catch (const exception::bad_boxed_cast &) {
return false;
}
return true;
}
template<typename Ret, int count, typename ... Params>
struct Call_Func
{
template<typename Callable, typename ... InnerParams>
static Ret do_call(const Callable &f,
const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions, InnerParams &&... innerparams)
{
return Call_Func<Ret, count - 1, Params...>::do_call(f, params, t_conversions, std::forward<InnerParams>(innerparams)..., params[sizeof...(Params) - count]);
}
};
template<typename Ret, typename ... Params>
struct Call_Func<Ret, 0, Params...>
{
#ifdef CHAISCRIPT_MSVC
#pragma warning(push)
#pragma warning(disable : 4100) /// Disable unreferenced formal parameter warning, which only shows up in MSVC I don't think there's any way around it \todo evaluate this
#endif
template<typename Callable, typename ... InnerParams>
static Ret do_call(const Callable &f,
const std::vector<Boxed_Value> &, const Type_Conversions &t_conversions, InnerParams &&... innerparams)
{
return f(boxed_cast<Params>(std::forward<InnerParams>(innerparams), &t_conversions)...);
}
#ifdef CHAISCRIPT_MSVC
#pragma warning(pop)
#endif
};
/**
* Used by Proxy_Function_Impl to perform typesafe execution of a function.
* The function attempts to unbox each parameter to the expected type.
* if any unboxing fails the execution of the function fails and
* the bad_boxed_cast is passed up to the caller.
*/
template<typename Callable, typename Ret, typename ... Params>
Ret call_func(const chaiscript::dispatch::detail::Function_Signature<Ret (Params...)> &, const Callable &f,
const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions)
{
if (params.size() == sizeof...(Params))
{
return Call_Func<Ret, sizeof...(Params), Params...>::do_call(f, params, t_conversions);
}
throw exception::arity_error(static_cast<int>(params.size()), sizeof...(Params));
}
#else
template<size_t ... I>
struct Indexes
{
};
template<size_t S, size_t ... I>
struct Make_Indexes
{
typedef typename Make_Indexes<S-1, I..., sizeof...(I)>::indexes indexes;
};
template<size_t ... I>
struct Make_Indexes<0, I...>
{
typedef Indexes<I...> indexes;
};
/**
* Used by Proxy_Function_Impl to determine if it is equivalent to another
* Proxy_Function_Impl object. This function is primarily used to prevent
* registration of two functions with the exact same signatures
*/
template<typename Ret, typename ... Params, size_t ... I>
bool compare_types_cast(Indexes<I...>, Ret (*)(Params...),
const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions)
{
try {
(void)params; (void)t_conversions;
(void)std::initializer_list<int>{(boxed_cast<Params>(params[I], &t_conversions), 0)...};
return true; return true;
} catch (const exception::bad_boxed_cast &) { } catch (const exception::bad_boxed_cast &) {
return false; return false;
} }
} }
template<typename Ret, typename ... Params>
bool compare_types_cast(Ret (*f)(Params...),
const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions)
{
typedef typename Make_Indexes<sizeof...(Params)>::indexes indexes;
return compare_types_cast(indexes(), f, params, t_conversions);
}
template<typename Callable, typename Ret, typename ... Params, size_t ... I> template<typename Callable, typename Ret, typename ... Params, size_t ... I>
Ret call_func_impl(Ret (*)(Params...), Ret call_func(const chaiscript::dispatch::detail::Function_Signature<Ret (Params...)> &, Indexes<I...>, const Callable &f,
std::index_sequence<I...>, const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions)
const Callable &f, {
[[maybe_unused]] const chaiscript::Function_Params &params, (void)params; (void)t_conversions;
[[maybe_unused]] const Type_Conversions_State &t_conversions) {
return f(boxed_cast<Params>(params[I], &t_conversions)...); return f(boxed_cast<Params>(params[I], &t_conversions)...);
} }
/// Used by Proxy_Function_Impl to perform typesafe execution of a function.
/// The function attempts to unbox each parameter to the expected type. /**
/// if any unboxing fails the execution of the function fails and * Used by Proxy_Function_Impl to perform typesafe execution of a function.
/// the bad_boxed_cast is passed up to the caller. * The function attempts to unbox each parameter to the expected type.
* if any unboxing fails the execution of the function fails and
* the bad_boxed_cast is passed up to the caller.
*/
template<typename Callable, typename Ret, typename ... Params> template<typename Callable, typename Ret, typename ... Params>
Boxed_Value Ret call_func(const chaiscript::dispatch::detail::Function_Signature<Ret (Params...)> &sig, const Callable &f,
call_func(Ret (*sig)(Params...), const Callable &f, const chaiscript::Function_Params &params, const Type_Conversions_State &t_conversions) { const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions)
return Handle_Return<Ret>::handle(call_func_impl(sig, std::index_sequence_for<Params...>{}, f, params, t_conversions)); {
typedef typename Make_Indexes<sizeof...(Params)>::indexes indexes;
return call_func(sig, indexes(), f, params, t_conversions);
} }
// MSVC has a broken warning for unreachable code in this block
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4702)
#endif #endif
template<typename Callable, typename... Params>
Boxed_Value }
call_func(void (*sig)(Params...), const Callable &f, const chaiscript::Function_Params &params, const Type_Conversions_State &t_conversions) { }
call_func_impl(sig, std::index_sequence_for<Params...>{}, f, params, t_conversions);
}
namespace chaiscript
{
namespace dispatch
{
namespace detail
{
template<typename Ret>
struct Do_Call
{
template<typename Signature, typename Callable>
static Boxed_Value go(const Callable &fun, const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions)
{
return Handle_Return<Ret>::handle(call_func(Function_Signature<Signature>(), fun, params, t_conversions));
}
};
template<>
struct Do_Call<void>
{
template<typename Signature, typename Callable>
static Boxed_Value go(const Callable &fun, const std::vector<Boxed_Value> &params, const Type_Conversions &t_conversions)
{
call_func(Function_Signature<Signature>(), fun, params, t_conversions);
return Handle_Return<void>::handle(); return Handle_Return<void>::handle();
} }
#ifdef _MSC_VER };
#pragma warning(pop) }
#endif }
}
} // namespace detail
} // namespace dispatch
} // namespace chaiscript
#endif #endif

View File

@ -1,61 +1,19 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_REGISTER_FUNCTION_HPP_ #ifndef CHAISCRIPT_REGISTER_FUNCTION_HPP_
#define CHAISCRIPT_REGISTER_FUNCTION_HPP_ #define CHAISCRIPT_REGISTER_FUNCTION_HPP_
#include <type_traits> #include <type_traits>
#include "bind_first.hpp" #include "bind_first.hpp"
#include "function_signature.hpp"
#include "proxy_functions.hpp" #include "proxy_functions.hpp"
namespace chaiscript { namespace chaiscript
namespace dispatch::detail { {
template<typename Obj, typename Param1, typename... Rest>
Param1 get_first_param(Function_Params<Param1, Rest...>, Obj &&obj) {
return static_cast<Param1>(std::forward<Obj>(obj));
}
template<typename Func, bool Is_Noexcept, bool Is_Member, bool Is_MemberObject, bool Is_Object, typename Ret, typename... Param>
auto make_callable_impl(Func &&func, Function_Signature<Ret, Function_Params<Param...>, Is_Noexcept, Is_Member, Is_MemberObject, Is_Object>) {
if constexpr (Is_MemberObject) {
// we now that the Param pack will have only one element, so we are safe expanding it here
return Proxy_Function(chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Attribute_Access<Ret, std::decay_t<Param>...>>(
std::forward<Func>(func)));
} else if constexpr (Is_Member) {
// TODO some kind of bug is preventing forwarding of this noexcept for the lambda
auto call = [func = std::forward<Func>(func)](auto &&obj, auto &&...param) /* noexcept(Is_Noexcept) */ -> decltype(auto) {
return ((get_first_param(Function_Params<Param...>{}, obj).*func)(std::forward<decltype(param)>(param)...));
};
return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret(Param...), decltype(call)>>(
std::move(call)));
} else {
return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret(Param...), std::decay_t<Func>>>(
std::forward<Func>(func)));
}
}
// this version peels off the function object itself from the function signature, when used
// on a callable object
template<typename Func, typename Ret, typename Object, typename... Param, bool Is_Noexcept>
auto make_callable(Func &&func, Function_Signature<Ret, Function_Params<Object, Param...>, Is_Noexcept, false, false, true>) {
return make_callable_impl(std::forward<Func>(func), Function_Signature<Ret, Function_Params<Param...>, Is_Noexcept, false, false, true>{});
}
template<typename Func, typename Ret, typename... Param, bool Is_Noexcept, bool Is_Member, bool Is_MemberObject>
auto make_callable(Func &&func, Function_Signature<Ret, Function_Params<Param...>, Is_Noexcept, Is_Member, Is_MemberObject, false> fs) {
return make_callable_impl(std::forward<Func>(func), fs);
}
} // namespace dispatch::detail
/// \brief Creates a new Proxy_Function object from a free function, member function or data member /// \brief Creates a new Proxy_Function object from a free function, member function or data member
/// \param[in] t Function / member to expose /// \param[in] t Function / member to expose
@ -78,47 +36,53 @@ namespace chaiscript {
/// ///
/// \sa \ref adding_functions /// \sa \ref adding_functions
template<typename T> template<typename T>
Proxy_Function fun(T &&t) { Proxy_Function fun(const T &t)
return dispatch::detail::make_callable(std::forward<T>(t), dispatch::detail::function_signature(t)); {
typedef typename dispatch::detail::Callable_Traits<T>::Signature Signature;
return Proxy_Function(
chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Signature, T>>(t));
} }
/// \brief Creates a new Proxy_Function object from an overloaded free function, with the template<typename Ret, typename ... Param>
/// signature specified explicitly to disambiguate the overload. Proxy_Function fun(Ret (*func)(Param...))
/// {
/// \b Example: auto fun_call = dispatch::detail::Fun_Caller<Ret, Param...>(func);
/// \code
/// int overloaded(int); return Proxy_Function(
/// double overloaded(double); chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (Param...), decltype(fun_call)>>(fun_call));
///
/// chai.add(chaiscript::fun<int(int)>(&overloaded), "overloaded");
/// chai.add(chaiscript::fun<double(double)>(&overloaded), "overloaded");
/// \endcode
template<typename Sig, std::enable_if_t<std::is_function_v<Sig>, int> = 0>
Proxy_Function fun(std::type_identity_t<Sig> *f) {
return dispatch::detail::make_callable(f, dispatch::detail::function_signature(f));
} }
/// \brief Creates a new Proxy_Function object from an overloaded member function, with the template<typename Ret, typename Class, typename ... Param>
/// signature specified explicitly to disambiguate the overload. Proxy_Function fun(Ret (Class::*t_func)(Param...) const)
/// {
/// \b Example: auto call = dispatch::detail::Const_Caller<Ret, Class, Param...>(t_func);
/// \code
/// class MyClass { return Proxy_Function(
/// public: chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (const Class &, Param...), decltype(call)>>(call));
/// int overloaded(int);
/// double overloaded(double);
/// int const_overloaded() const;
/// };
///
/// chai.add(chaiscript::fun<int(int)>(&MyClass::overloaded), "overloaded");
/// chai.add(chaiscript::fun<double(double)>(&MyClass::overloaded), "overloaded");
/// chai.add(chaiscript::fun<int() const>(&MyClass::const_overloaded), "const_overloaded");
/// \endcode
template<typename Sig, typename Class, std::enable_if_t<std::is_function_v<Sig>, int> = 0>
Proxy_Function fun(std::type_identity_t<Sig> Class::*f) {
return dispatch::detail::make_callable(f, dispatch::detail::function_signature(f));
} }
template<typename Ret, typename Class, typename ... Param>
Proxy_Function fun(Ret (Class::*t_func)(Param...))
{
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));
}
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));
}
/// \brief Creates a new Proxy_Function object from a free function, member function or data member and binds the first parameter of it /// \brief Creates a new Proxy_Function object from a free function, member function or data member and binds the first parameter of it
/// \param[in] t Function / member to expose /// \param[in] t Function / member to expose
/// \param[in] q Value to bind to first parameter /// \param[in] q Value to bind to first parameter
@ -138,10 +102,39 @@ namespace chaiscript {
/// ///
/// \sa \ref adding_functions /// \sa \ref adding_functions
template<typename T, typename Q> template<typename T, typename Q>
Proxy_Function fun(T &&t, const Q &q) { Proxy_Function fun(T &&t, const Q &q)
{
return fun(detail::bind_first(std::forward<T>(t), q)); return fun(detail::bind_first(std::forward<T>(t), q));
} }
} // namespace chaiscript /// \brief Creates a new Proxy_Function object from a free function or member function and binds the first and second parameters of it
/// \param[in] t Function / member to expose
/// \param[in] q Value to bind to first parameter
/// \param[in] r Value to bind to second parameter
///
/// \b Example:
/// \code
/// struct MyClass
/// {
/// void memberfunction(int);
/// };
///
/// MyClass obj;
/// chaiscript::ChaiScript chai;
/// // Add function taking only no arguments, and permanently bound to "obj" and "1"
/// // memberfunction() will be equivalent to obj.memberfunction(1)
/// chai.add(fun(&MyClass::memberfunction, std::ref(obj), 1), "memberfunction");
/// \endcode
///
/// \sa \ref adding_functions
template<typename T, typename Q, typename R>
Proxy_Function fun(T &&t, Q &&q, R &&r)
{
return fun(detail::bind_first(detail::bind_first(std::forward<T>(t), std::forward<Q>(q)), std::forward<R>(r)));
}
}
#endif #endif

View File

@ -1,16 +1,12 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_ #ifndef CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_
#define CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_ #define CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_
#include <algorithm>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <set> #include <set>
@ -20,116 +16,141 @@
#include <typeinfo> #include <typeinfo>
#include "../chaiscript_threading.hpp" #include "../chaiscript_threading.hpp"
#include "../utility/static_string.hpp"
#include "bad_boxed_cast.hpp" #include "bad_boxed_cast.hpp"
#include "boxed_cast_helper.hpp" #include "boxed_cast_helper.hpp"
#include "boxed_value.hpp" #include "boxed_value.hpp"
#include "type_info.hpp" #include "type_info.hpp"
namespace chaiscript { namespace chaiscript
namespace exception { {
/// \brief Error thrown when there's a problem with type conversion namespace exception
class conversion_error : public bad_boxed_cast { {
class bad_boxed_dynamic_cast : public bad_boxed_cast
{
public: public:
conversion_error(const Type_Info t_to, const Type_Info t_from, const utility::Static_String what) noexcept bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to,
: bad_boxed_cast(t_from, (*t_to.bare_type_info()), what) const std::string &t_what) CHAISCRIPT_NOEXCEPT
, type_to(t_to) { : bad_boxed_cast(t_from, t_to, t_what)
{
} }
Type_Info type_to; bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to) CHAISCRIPT_NOEXCEPT
}; : bad_boxed_cast(t_from, t_to)
{
class bad_boxed_dynamic_cast : public bad_boxed_cast {
public:
bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to, const utility::Static_String &t_what) noexcept
: bad_boxed_cast(t_from, t_to, t_what) {
} }
bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to) noexcept bad_boxed_dynamic_cast(const std::string &w) CHAISCRIPT_NOEXCEPT
: bad_boxed_cast(t_from, t_to) { : bad_boxed_cast(w)
} {
explicit bad_boxed_dynamic_cast(const utility::Static_String &w) noexcept
: bad_boxed_cast(w) {
} }
bad_boxed_dynamic_cast(const bad_boxed_dynamic_cast &) = default; bad_boxed_dynamic_cast(const bad_boxed_dynamic_cast &) = default;
~bad_boxed_dynamic_cast() noexcept override = default; virtual ~bad_boxed_dynamic_cast() CHAISCRIPT_NOEXCEPT {}
}; };
class bad_boxed_type_cast : public bad_boxed_cast { class bad_boxed_type_cast : public bad_boxed_cast
{
public: public:
bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to, const utility::Static_String &t_what) noexcept bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to,
: bad_boxed_cast(t_from, t_to, t_what) { const std::string &t_what) CHAISCRIPT_NOEXCEPT
: bad_boxed_cast(t_from, t_to, t_what)
{
} }
bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to) noexcept bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to) CHAISCRIPT_NOEXCEPT
: bad_boxed_cast(t_from, t_to) { : bad_boxed_cast(t_from, t_to)
{
} }
explicit bad_boxed_type_cast(const utility::Static_String &w) noexcept bad_boxed_type_cast(const std::string &w) CHAISCRIPT_NOEXCEPT
: bad_boxed_cast(w) { : bad_boxed_cast(w)
{
} }
bad_boxed_type_cast(const bad_boxed_type_cast &) = default; bad_boxed_type_cast(const bad_boxed_type_cast &) = default;
~bad_boxed_type_cast() noexcept override = default; virtual ~bad_boxed_type_cast() CHAISCRIPT_NOEXCEPT {}
}; };
} // namespace exception }
namespace detail {
class Type_Conversion_Base { namespace detail
{
class Type_Conversion_Base
{
public: public:
virtual Boxed_Value convert(const Boxed_Value &from) const = 0; virtual Boxed_Value convert(const Boxed_Value &from) const = 0;
virtual Boxed_Value convert_down(const Boxed_Value &to) const = 0; virtual Boxed_Value convert_down(const Boxed_Value &to) const = 0;
const Type_Info &to() const noexcept { return m_to; } const Type_Info &to() const
const Type_Info &from() const noexcept { return m_from; } {
return m_to;
virtual bool bidir() const noexcept { return true; } }
const Type_Info &from() const
virtual ~Type_Conversion_Base() = default; {
return m_from;
protected:
Type_Conversion_Base(Type_Info t_to, Type_Info t_from)
: m_to(std::move(t_to))
, m_from(std::move(t_from)) {
} }
virtual bool bidir() const
{
return true;
}
virtual ~Type_Conversion_Base() {}
protected:
Type_Conversion_Base(const Type_Info &t_to, const Type_Info &t_from)
: m_to(t_to), m_from(t_from)
{
}
private: private:
const Type_Info m_to; Type_Info m_to;
const Type_Info m_from; Type_Info m_from;
}; };
template<typename From, typename To> template<typename From, typename To>
class Static_Caster { class Static_Caster
{
public: public:
static Boxed_Value cast(const Boxed_Value &t_from) { static Boxed_Value cast(const Boxed_Value &t_from)
if (t_from.get_type_info().bare_equal(chaiscript::user_type<From>())) { {
if (t_from.is_pointer()) { if (t_from.get_type_info().bare_equal(chaiscript::user_type<From>()))
{
if (t_from.is_pointer())
{
// Dynamic cast out the contained boxed value, which we know is the type we want // Dynamic cast out the contained boxed value, which we know is the type we want
if (t_from.is_const()) { if (t_from.is_const())
return Boxed_Value([&]() { {
if (auto data return Boxed_Value(
= std::static_pointer_cast<const To>(detail::Cast_Helper<std::shared_ptr<const From>>::cast(t_from, nullptr))) { [&]()->std::shared_ptr<const To>{
if (auto data = std::static_pointer_cast<const To>(detail::Cast_Helper<std::shared_ptr<const From> >::cast(t_from, nullptr)))
{
return data; return data;
} else { } else {
throw std::bad_cast(); throw std::bad_cast();
} }
}()); }()
);
} else { } else {
return Boxed_Value([&]() { return Boxed_Value(
if (auto data = std::static_pointer_cast<To>(detail::Cast_Helper<std::shared_ptr<From>>::cast(t_from, nullptr))) { [&]()->std::shared_ptr<To>{
if (auto data = std::static_pointer_cast<To>(detail::Cast_Helper<std::shared_ptr<From> >::cast(t_from, nullptr)))
{
return data; return data;
} else { } else {
throw std::bad_cast(); throw std::bad_cast();
} }
}()); }()
);
} }
} else { } else {
// Pull the reference out of the contained boxed value, which we know is the type we want // Pull the reference out of the contained boxed value, which we know is the type we want
if (t_from.is_const()) { if (t_from.is_const())
{
const From &d = detail::Cast_Helper<const From &>::cast(t_from, nullptr); const From &d = detail::Cast_Helper<const From &>::cast(t_from, nullptr);
const To &data = static_cast<const To &>(d); const To &data = static_cast<const To &>(d);
return Boxed_Value(std::cref(data)); return Boxed_Value(std::cref(data));
@ -143,27 +164,38 @@ namespace chaiscript {
throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion"); throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion");
} }
} }
}; };
template<typename From, typename To> template<typename From, typename To>
class Dynamic_Caster { class Dynamic_Caster
{
public: public:
static Boxed_Value cast(const Boxed_Value &t_from) { static Boxed_Value cast(const Boxed_Value &t_from)
if (t_from.get_type_info().bare_equal(chaiscript::user_type<From>())) { {
if (t_from.is_pointer()) { if (t_from.get_type_info().bare_equal(chaiscript::user_type<From>()))
{
if (t_from.is_pointer())
{
// Dynamic cast out the contained boxed value, which we know is the type we want // Dynamic cast out the contained boxed value, which we know is the type we want
if (t_from.is_const()) { if (t_from.is_const())
return Boxed_Value([&]() { {
if (auto data return Boxed_Value(
= std::dynamic_pointer_cast<const To>(detail::Cast_Helper<std::shared_ptr<const From>>::cast(t_from, nullptr))) { [&]()->std::shared_ptr<const To>{
if (auto data = std::dynamic_pointer_cast<const To>(detail::Cast_Helper<std::shared_ptr<const From> >::cast(t_from, nullptr)))
{
return data; return data;
} else { } else {
throw std::bad_cast(); throw std::bad_cast();
} }
}()); }()
);
} else { } else {
return Boxed_Value([&]() { return Boxed_Value(
if (auto data = std::dynamic_pointer_cast<To>(detail::Cast_Helper<std::shared_ptr<From>>::cast(t_from, nullptr))) { [&]()->std::shared_ptr<To>{
if (auto data = std::dynamic_pointer_cast<To>(detail::Cast_Helper<std::shared_ptr<From> >::cast(t_from, nullptr)))
{
return data; return data;
} else { } else {
#ifdef CHAISCRIPT_LIBCPP #ifdef CHAISCRIPT_LIBCPP
@ -177,11 +209,13 @@ namespace chaiscript {
#endif #endif
throw std::bad_cast(); throw std::bad_cast();
} }
}()); }()
);
} }
} else { } else {
// Pull the reference out of the contained boxed value, which we know is the type we want // Pull the reference out of the contained boxed value, which we know is the type we want
if (t_from.is_const()) { if (t_from.is_const())
{
const From &d = detail::Cast_Helper<const From &>::cast(t_from, nullptr); const From &d = detail::Cast_Helper<const From &>::cast(t_from, nullptr);
const To &data = dynamic_cast<const To &>(d); const To &data = dynamic_cast<const To &>(d);
return Boxed_Value(std::cref(data)); return Boxed_Value(std::cref(data));
@ -195,91 +229,127 @@ namespace chaiscript {
throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion"); throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion");
} }
} }
}; };
template<typename Base, typename Derived> template<typename Base, typename Derived>
class Dynamic_Conversion_Impl : public Type_Conversion_Base { class Dynamic_Conversion_Impl : public Type_Conversion_Base
{
public: public:
Dynamic_Conversion_Impl() Dynamic_Conversion_Impl()
: Type_Conversion_Base(chaiscript::user_type<Base>(), chaiscript::user_type<Derived>()) { : Type_Conversion_Base(chaiscript::user_type<Base>(), chaiscript::user_type<Derived>())
{
} }
Boxed_Value convert_down(const Boxed_Value &t_base) const override { return Dynamic_Caster<Base, Derived>::cast(t_base); } virtual Boxed_Value convert_down(const Boxed_Value &t_base) const CHAISCRIPT_OVERRIDE
{
return Dynamic_Caster<Base, Derived>::cast(t_base);
}
Boxed_Value convert(const Boxed_Value &t_derived) const override { return Static_Caster<Derived, Base>::cast(t_derived); } virtual Boxed_Value convert(const Boxed_Value &t_derived) const CHAISCRIPT_OVERRIDE
{
return Static_Caster<Derived, Base>::cast(t_derived);
}
}; };
template<typename Base, typename Derived> template<typename Base, typename Derived>
class Static_Conversion_Impl : public Type_Conversion_Base { class Static_Conversion_Impl : public Type_Conversion_Base
{
public: public:
Static_Conversion_Impl() Static_Conversion_Impl()
: Type_Conversion_Base(chaiscript::user_type<Base>(), chaiscript::user_type<Derived>()) { : Type_Conversion_Base(chaiscript::user_type<Base>(), chaiscript::user_type<Derived>())
{
} }
Boxed_Value convert_down(const Boxed_Value &t_base) const override { virtual Boxed_Value convert_down(const Boxed_Value &t_base) const CHAISCRIPT_OVERRIDE
throw chaiscript::exception::bad_boxed_dynamic_cast(t_base.get_type_info(), {
typeid(Derived), throw chaiscript::exception::bad_boxed_dynamic_cast(t_base.get_type_info(), typeid(Derived), "Unable to cast down inheritance hierarchy with non-polymorphic types");
"Unable to cast down inheritance hierarchy with non-polymorphic types");
} }
bool bidir() const noexcept override { return false; } virtual bool bidir() const CHAISCRIPT_OVERRIDE
{
return false;
}
Boxed_Value convert(const Boxed_Value &t_derived) const override { return Static_Caster<Derived, Base>::cast(t_derived); } virtual Boxed_Value convert(const Boxed_Value &t_derived) const CHAISCRIPT_OVERRIDE
{
return Static_Caster<Derived, Base>::cast(t_derived);
}
}; };
template<typename Callable> template<typename Callable>
class Type_Conversion_Impl : public Type_Conversion_Base { class Type_Conversion_Impl : public Type_Conversion_Base
{
public: public:
Type_Conversion_Impl(Type_Info t_from, Type_Info t_to, Callable t_func) Type_Conversion_Impl(Type_Info t_from, Type_Info t_to, Callable t_func)
: Type_Conversion_Base(t_to, t_from) : Type_Conversion_Base(std::move(t_to), std::move(t_from)),
, m_func(std::move(t_func)) { m_func(std::move(t_func))
{
} }
Boxed_Value convert_down(const Boxed_Value &) const override { virtual Boxed_Value convert_down(const Boxed_Value &) const CHAISCRIPT_OVERRIDE
{
throw chaiscript::exception::bad_boxed_type_cast("No conversion exists"); throw chaiscript::exception::bad_boxed_type_cast("No conversion exists");
} }
Boxed_Value convert(const Boxed_Value &t_from) const override { virtual Boxed_Value convert(const Boxed_Value &t_from) const CHAISCRIPT_OVERRIDE
{
/// \todo better handling of errors from the conversion function /// \todo better handling of errors from the conversion function
return m_func(t_from); return m_func(t_from);
} }
bool bidir() const noexcept override { return false; } virtual bool bidir() const CHAISCRIPT_OVERRIDE
{
return false;
}
private: private:
Callable m_func; Callable m_func;
}; };
} // namespace detail
class Type_Conversions { }
class Type_Conversions
{
public: public:
struct Conversion_Saves { struct Less_Than
bool enabled = false; {
std::vector<Boxed_Value> saves; bool operator()(const std::type_info *t_lhs, const std::type_info *t_rhs) const
}; {
struct Less_Than {
bool operator()(const std::type_info *t_lhs, const std::type_info *t_rhs) const noexcept {
return *t_lhs != *t_rhs && t_lhs->before(*t_rhs); return *t_lhs != *t_rhs && t_lhs->before(*t_rhs);
} }
}; };
Type_Conversions() Type_Conversions()
: m_mutex() : m_mutex(),
, m_conversions() m_conversions(),
, m_convertableTypes() m_convertableTypes(),
, m_num_types(0) { m_num_types(0),
m_thread_cache(this),
m_conversion_saves(this)
{
} }
Type_Conversions(const Type_Conversions &t_other) = delete; Type_Conversions(const Type_Conversions &t_other)
Type_Conversions(Type_Conversions &&) = delete; : m_mutex(),
m_conversions(t_other.get_conversions()),
m_convertableTypes(t_other.m_convertableTypes),
m_num_types(m_conversions.size()),
m_thread_cache(this),
m_conversion_saves(this)
Type_Conversions &operator=(const Type_Conversions &) = delete; {
Type_Conversions &operator=(Type_Conversions &&) = delete; }
const std::set<const std::type_info *, Less_Than> &thread_cache() const { const std::set<const std::type_info *, Less_Than> &thread_cache() const
{
auto &cache = *m_thread_cache; auto &cache = *m_thread_cache;
if (cache.size() != m_num_types) { if (cache.size() != m_num_types)
{
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
cache = m_convertableTypes; cache = m_convertableTypes;
} }
@ -287,125 +357,142 @@ namespace chaiscript {
return cache; return cache;
} }
void add_conversion(const std::shared_ptr<detail::Type_Conversion_Base> &conversion) { void add_conversion(const std::shared_ptr<detail::Type_Conversion_Base> &conversion)
{
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
if (find_bidir(conversion->to(), conversion->from()) != m_conversions.end()) { /// \todo error if a conversion already exists
throw exception::conversion_error(conversion->to(), conversion->from(), "Trying to re-insert an existing conversion!");
}
m_conversions.insert(conversion); m_conversions.insert(conversion);
m_convertableTypes.insert({conversion->to().bare_type_info(), conversion->from().bare_type_info()}); m_convertableTypes.insert({conversion->to().bare_type_info(), conversion->from().bare_type_info()});
m_num_types = m_convertableTypes.size(); m_num_types = m_convertableTypes.size();
} }
template<typename T> template<typename T>
bool convertable_type() const noexcept { bool convertable_type() const
const auto type = user_type<T>().bare_type_info(); {
return thread_cache().count(type) != 0; return thread_cache().count(user_type<T>().bare_type_info()) != 0;
} }
template<typename To, typename From> template<typename To, typename From>
bool converts() const noexcept { bool converts() const
{
return converts(user_type<To>(), user_type<From>()); return converts(user_type<To>(), user_type<From>());
} }
bool converts(const Type_Info &to, const Type_Info &from) const noexcept { bool converts(const Type_Info &to, const Type_Info &from) const
{
const auto &types = thread_cache(); const auto &types = thread_cache();
if (types.count(to.bare_type_info()) != 0 && types.count(from.bare_type_info()) != 0) { if (types.count(to.bare_type_info()) != 0 && types.count(from.bare_type_info()) != 0)
{
return has_conversion(to, from); return has_conversion(to, from);
} else { } else {
return false; return false;
} }
} }
template<typename To> template<typename To>
Boxed_Value boxed_type_conversion(Conversion_Saves &t_saves, const Boxed_Value &from) const { Boxed_Value boxed_type_conversion(const Boxed_Value &from) const
return boxed_type_conversion(user_type<To>(), t_saves, from); {
try {
Boxed_Value ret = get_conversion(user_type<To>(), from.get_type_info())->convert(from);
if (m_conversion_saves->enabled) m_conversion_saves->saves.push_back(ret);
return ret;
} catch (const std::out_of_range &) {
throw exception::bad_boxed_dynamic_cast(from.get_type_info(), typeid(To), "No known conversion");
} catch (const std::bad_cast &) {
throw exception::bad_boxed_dynamic_cast(from.get_type_info(), typeid(To), "Unable to perform dynamic_cast operation");
}
} }
template<typename From> template<typename From>
Boxed_Value boxed_type_down_conversion(Conversion_Saves &t_saves, const Boxed_Value &to) const { Boxed_Value boxed_type_down_conversion(const Boxed_Value &to) const
return boxed_type_down_conversion(user_type<From>(), t_saves, to); {
}
Boxed_Value boxed_type_conversion(const Type_Info &to, Conversion_Saves &t_saves, const Boxed_Value &from) const {
try { try {
Boxed_Value ret = get_conversion(to, from.get_type_info())->convert(from); Boxed_Value ret = get_conversion(to.get_type_info(), user_type<From>())->convert_down(to);
if (t_saves.enabled) { if (m_conversion_saves->enabled) m_conversion_saves->saves.push_back(ret);
t_saves.saves.push_back(ret);
}
return ret; return ret;
} catch (const std::out_of_range &) { } catch (const std::out_of_range &) {
throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "No known conversion"); throw exception::bad_boxed_dynamic_cast(to.get_type_info(), typeid(From), "No known conversion");
} catch (const std::bad_cast &) { } catch (const std::bad_cast &) {
throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "Unable to perform dynamic_cast operation"); throw exception::bad_boxed_dynamic_cast(to.get_type_info(), typeid(From), "Unable to perform dynamic_cast operation");
} }
} }
Boxed_Value boxed_type_down_conversion(const Type_Info &from, Conversion_Saves &t_saves, const Boxed_Value &to) const { void enable_conversion_saves(bool t_val)
try { {
Boxed_Value ret = get_conversion(to.get_type_info(), from)->convert_down(to); m_conversion_saves->enabled = t_val;
if (t_saves.enabled) {
t_saves.saves.push_back(ret);
}
return ret;
} catch (const std::out_of_range &) {
throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "No known conversion");
} catch (const std::bad_cast &) {
throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "Unable to perform dynamic_cast operation");
}
} }
static void enable_conversion_saves(Conversion_Saves &t_saves, bool t_val) { t_saves.enabled = t_val; } std::vector<Boxed_Value> take_saves()
{
std::vector<Boxed_Value> take_saves(Conversion_Saves &t_saves) {
std::vector<Boxed_Value> ret; std::vector<Boxed_Value> ret;
std::swap(ret, t_saves.saves); std::swap(ret, m_conversion_saves->saves);
return ret; return ret;
} }
bool has_conversion(const Type_Info &to, const Type_Info &from) const { bool has_conversion(const Type_Info &to, const Type_Info &from) const
{
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
return find_bidir(to, from) != m_conversions.end(); return find_bidir(to, from) != m_conversions.end();
} }
std::shared_ptr<detail::Type_Conversion_Base> get_conversion(const Type_Info &to, const Type_Info &from) const { std::shared_ptr<detail::Type_Conversion_Base> get_conversion(const Type_Info &to, const Type_Info &from) const
{
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
const auto itr = find(to, from); auto itr = find(to, from);
if (itr != m_conversions.end()) { if (itr != m_conversions.end())
{
return *itr; return *itr;
} else { } else {
throw std::out_of_range(std::string("No such conversion exists from ") + from.bare_name() + " to " + to.bare_name()); throw std::out_of_range("No such conversion exists from " + from.bare_name() + " to " + to.bare_name());
} }
} }
Conversion_Saves &conversion_saves() const noexcept { return *m_conversion_saves; }
private: private:
std::set<std::shared_ptr<detail::Type_Conversion_Base>>::const_iterator find_bidir(const Type_Info &to, const Type_Info &from) const { std::set<std::shared_ptr<detail::Type_Conversion_Base> >::const_iterator find_bidir(
return std::find_if(m_conversions.begin(), const Type_Info &to, const Type_Info &from) const
m_conversions.end(), {
[&to, &from](const std::shared_ptr<detail::Type_Conversion_Base> &conversion) -> bool { return std::find_if(m_conversions.begin(), m_conversions.end(),
[&to, &from](const std::shared_ptr<detail::Type_Conversion_Base> &conversion) -> bool
{
return (conversion->to().bare_equal(to) && conversion->from().bare_equal(from)) return (conversion->to().bare_equal(to) && conversion->from().bare_equal(from))
|| (conversion->bidir() && conversion->from().bare_equal(to) && conversion->to().bare_equal(from)); || (conversion->bidir() && conversion->from().bare_equal(to) && conversion->to().bare_equal(from));
}); ;
}
);
} }
std::set<std::shared_ptr<detail::Type_Conversion_Base>>::const_iterator find(const Type_Info &to, const Type_Info &from) const { std::set<std::shared_ptr<detail::Type_Conversion_Base> >::const_iterator find(
return std::find_if(m_conversions.begin(), const Type_Info &to, const Type_Info &from) const
m_conversions.end(), {
[&to, &from](const std::shared_ptr<detail::Type_Conversion_Base> &conversion) { return std::find_if(m_conversions.begin(), m_conversions.end(),
[&to, &from](const std::shared_ptr<detail::Type_Conversion_Base> &conversion)
{
return conversion->to().bare_equal(to) && conversion->from().bare_equal(from); return conversion->to().bare_equal(to) && conversion->from().bare_equal(from);
}); }
);
} }
std::set<std::shared_ptr<detail::Type_Conversion_Base>> get_conversions() const { std::set<std::shared_ptr<detail::Type_Conversion_Base>> get_conversions() const
{
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
return m_conversions; return m_conversions;
} }
struct Conversion_Saves
{
Conversion_Saves()
: enabled(false)
{}
bool enabled;
std::vector<Boxed_Value> saves;
};
mutable chaiscript::detail::threading::shared_mutex m_mutex; mutable chaiscript::detail::threading::shared_mutex m_mutex;
std::set<std::shared_ptr<detail::Type_Conversion_Base>> m_conversions; std::set<std::shared_ptr<detail::Type_Conversion_Base>> m_conversions;
std::set<const std::type_info *, Less_Than> m_convertableTypes; std::set<const std::type_info *, Less_Than> m_convertableTypes;
@ -414,25 +501,7 @@ namespace chaiscript {
mutable chaiscript::detail::threading::Thread_Storage<Conversion_Saves> m_conversion_saves; mutable chaiscript::detail::threading::Thread_Storage<Conversion_Saves> m_conversion_saves;
}; };
class Type_Conversions_State { typedef std::shared_ptr<chaiscript::detail::Type_Conversion_Base> Type_Conversion;
public:
Type_Conversions_State(const Type_Conversions &t_conversions, Type_Conversions::Conversion_Saves &t_saves)
: m_conversions(t_conversions)
, m_saves(t_saves) {
}
const Type_Conversions *operator->() const noexcept { return &m_conversions.get(); }
const Type_Conversions *get() const noexcept { return &m_conversions.get(); }
Type_Conversions::Conversion_Saves &saves() const noexcept { return m_saves; }
private:
std::reference_wrapper<const Type_Conversions> m_conversions;
std::reference_wrapper<Type_Conversions::Conversion_Saves> m_saves;
};
using Type_Conversion = std::shared_ptr<chaiscript::detail::Type_Conversion_Base>;
/// \brief Used to register a to / parent class relationship with ChaiScript. Necessary if you /// \brief Used to register a to / parent class relationship with ChaiScript. Necessary if you
/// want automatic conversions up your inheritance hierarchy. /// want automatic conversions up your inheritance hierarchy.
@ -456,124 +525,75 @@ namespace chaiscript {
/// \endcode /// \endcode
/// ///
template<typename Base, typename Derived> template<typename Base, typename Derived>
Type_Conversion base_class() { Type_Conversion base_class(typename std::enable_if<std::is_polymorphic<Base>::value && std::is_polymorphic<Derived>::value>::type* = nullptr)
{
//Can only be used with related polymorphic types //Can only be used with related polymorphic types
//may be expanded some day to support conversions other than child -> parent //may be expanded some day to support conversions other than child -> parent
static_assert(std::is_base_of<Base,Derived>::value, "Classes are not related by inheritance"); static_assert(std::is_base_of<Base,Derived>::value, "Classes are not related by inheritance");
if constexpr (std::is_polymorphic<Base>::value && std::is_polymorphic<Derived>::value) {
return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Dynamic_Conversion_Impl<Base, Derived>>(); return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Dynamic_Conversion_Impl<Base, Derived>>();
} else {
return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Static_Conversion_Impl<Base, Derived>>();
}
} }
template<typename Base, typename Derived>
Type_Conversion base_class(typename std::enable_if<!std::is_polymorphic<Base>::value || !std::is_polymorphic<Derived>::value>::type* = nullptr)
{
//Can only be used with related polymorphic types
//may be expanded some day to support conversions other than child -> parent
static_assert(std::is_base_of<Base,Derived>::value, "Classes are not related by inheritance");
return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Static_Conversion_Impl<Base, Derived>>();
}
template<typename Callable> template<typename Callable>
Type_Conversion type_conversion(const Type_Info &t_from, const Type_Info &t_to, const Callable &t_func) { Type_Conversion type_conversion(const Type_Info &t_from, const Type_Info &t_to,
const Callable &t_func)
{
return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<Callable>>(t_from, t_to, t_func); return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<Callable>>(t_from, t_to, t_func);
} }
template<typename From, typename To, typename Callable> template<typename From, typename To, typename Callable>
Type_Conversion type_conversion(const Callable &t_function) { Type_Conversion type_conversion(const Callable &t_function)
{
auto func = [t_function](const Boxed_Value &t_bv) -> Boxed_Value { auto func = [t_function](const Boxed_Value &t_bv) -> Boxed_Value {
// not even attempting to call boxed_cast so that we don't get caught in some call recursion // not even attempting to call boxed_cast so that we don't get caught in some call recursion
return chaiscript::Boxed_Value(t_function(detail::Cast_Helper<const From &>::cast(t_bv, nullptr))); return chaiscript::Boxed_Value(t_function(detail::Cast_Helper<const From &>::cast(t_bv, nullptr)));
}; };
return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(user_type<From>(), return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(user_type<From>(), user_type<To>(), func);
user_type<To>(),
func);
} }
template<typename From, typename To> template<typename From, typename To>
Type_Conversion type_conversion() { Type_Conversion type_conversion()
{
static_assert(std::is_convertible<From, To>::value, "Types are not automatically convertible"); static_assert(std::is_convertible<From, To>::value, "Types are not automatically convertible");
auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { auto func = [](const Boxed_Value &t_bv) -> Boxed_Value {
// not even attempting to call boxed_cast so that we don't get caught in some call recursion // not even attempting to call boxed_cast so that we don't get caught in some call recursion
return chaiscript::Boxed_Value(To(detail::Cast_Helper<From>::cast(t_bv, nullptr))); return chaiscript::Boxed_Value(To(detail::Cast_Helper<From>::cast(t_bv, nullptr)));
}; };
return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(user_type<From>(), return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(user_type<From>(), user_type<To>(), func);
user_type<To>(),
func);
} }
namespace detail {
template<typename T>
struct is_std_vector : std::false_type {};
template<typename T, typename A>
struct is_std_vector<std::vector<T, A>> : std::true_type {};
template<typename T>
T convert_vector_element(const Boxed_Value &bv) {
if constexpr (is_std_vector<T>::value) {
const auto &inner = Cast_Helper<const std::vector<Boxed_Value> &>::cast(bv, nullptr);
T result;
result.reserve(inner.size());
for (const Boxed_Value &elem : inner) {
result.push_back(convert_vector_element<typename T::value_type>(elem));
}
return result;
} else {
return Cast_Helper<T>::cast(bv, nullptr);
}
}
} // namespace detail
template<typename To> template<typename To>
Type_Conversion vector_conversion() { Type_Conversion vector_conversion()
{
auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { auto func = [](const Boxed_Value &t_bv) -> Boxed_Value {
const std::vector<Boxed_Value> &from_vec = detail::Cast_Helper<const std::vector<Boxed_Value> &>::cast(t_bv, nullptr); const std::vector<Boxed_Value> &from_vec = detail::Cast_Helper<const std::vector<Boxed_Value> &>::cast(t_bv, nullptr);
To vec; To vec;
vec.reserve(from_vec.size());
for (const Boxed_Value &bv : from_vec) { for (const Boxed_Value &bv : from_vec) {
vec.push_back(detail::convert_vector_element<typename To::value_type>(bv)); vec.push_back(detail::Cast_Helper<typename To::value_type>::cast(bv, nullptr));
} }
return Boxed_Value(std::move(vec)); return Boxed_Value(std::move(vec));
}; };
return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(user_type<std::vector<Boxed_Value>>(), return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(user_type<std::vector<Boxed_Value>>(), user_type<To>(), func);
user_type<To>(),
func);
} }
template<typename To>
Type_Conversion map_conversion() {
auto func = [](const Boxed_Value &t_bv) -> Boxed_Value {
const std::map<std::string, Boxed_Value> &from_map
= detail::Cast_Helper<const std::map<std::string, Boxed_Value> &>::cast(t_bv, nullptr);
To map;
for (const std::pair<const std::string, Boxed_Value> &p : from_map) {
map.insert(std::make_pair(p.first, detail::Cast_Helper<typename To::mapped_type>::cast(p.second, nullptr)));
} }
return Boxed_Value(std::move(map));
};
return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(
user_type<std::map<std::string, Boxed_Value>>(), user_type<To>(), func);
}
template<typename Left, typename Right>
Type_Conversion pair_conversion() {
auto func = [](const Boxed_Value &t_bv) -> Boxed_Value {
const std::pair<Boxed_Value, Boxed_Value> &from_pair
= detail::Cast_Helper<const std::pair<Boxed_Value, Boxed_Value> &>::cast(t_bv, nullptr);
auto pair = std::make_pair(
detail::Cast_Helper<Left>::cast(from_pair.first, nullptr),
detail::Cast_Helper<Right>::cast(from_pair.second, nullptr));
return Boxed_Value(std::move(pair));
};
return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(
user_type<std::pair<Boxed_Value, Boxed_Value>>(), user_type<std::pair<Left, Right>>(), func);
}
} // namespace chaiscript
#endif #endif

View File

@ -1,115 +1,143 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_TYPE_INFO_HPP_ #ifndef CHAISCRIPT_TYPE_INFO_HPP_
#define CHAISCRIPT_TYPE_INFO_HPP_ #define CHAISCRIPT_TYPE_INFO_HPP_
#include <memory> #include <memory>
#include <string>
#include <type_traits> #include <type_traits>
#include <typeinfo> #include <typeinfo>
#include <string>
namespace chaiscript { namespace chaiscript
namespace detail { {
namespace detail
{
template<typename T> template<typename T>
struct Bare_Type { struct Bare_Type
using type = typename std::remove_cv<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::type; {
typedef typename std::remove_cv<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::type type;
}; };
} // namespace detail }
/// \brief Compile time deduced information about a type /// \brief Compile time deduced information about a type
class Type_Info { class Type_Info
{
public: public:
constexpr Type_Info(const bool t_is_const, CHAISCRIPT_CONSTEXPR Type_Info(bool t_is_const, bool t_is_reference, bool t_is_pointer, bool t_is_void,
const bool t_is_reference, bool t_is_arithmetic, const std::type_info *t_ti, const std::type_info *t_bare_ti)
const bool t_is_pointer, : m_type_info(t_ti), m_bare_type_info(t_bare_ti),
const bool t_is_void, m_is_const(t_is_const), m_is_reference(t_is_reference), m_is_pointer(t_is_pointer),
const bool t_is_arithmetic, m_is_void(t_is_void), m_is_arithmetic(t_is_arithmetic),
const std::type_info *t_ti, m_is_undef(false)
const std::type_info *t_bare_ti) noexcept {
: m_type_info(t_ti)
, m_bare_type_info(t_bare_ti)
, m_flags((static_cast<unsigned int>(t_is_const) << is_const_flag) + (static_cast<unsigned int>(t_is_reference) << is_reference_flag)
+ (static_cast<unsigned int>(t_is_pointer) << is_pointer_flag) + (static_cast<unsigned int>(t_is_void) << is_void_flag)
+ (static_cast<unsigned int>(t_is_arithmetic) << is_arithmetic_flag)) {
} }
constexpr Type_Info() noexcept = default; CHAISCRIPT_CONSTEXPR Type_Info()
: m_type_info(nullptr), m_bare_type_info(nullptr),
bool operator<(const Type_Info &ti) const noexcept { return m_type_info->before(*ti.m_type_info); } m_is_const(false), m_is_reference(false), m_is_pointer(false),
m_is_void(false), m_is_arithmetic(false),
constexpr bool operator!=(const Type_Info &ti) const noexcept { return !(operator==(ti)); } m_is_undef(true)
{
constexpr bool operator!=(const std::type_info &ti) const noexcept { return !(operator==(ti)); }
constexpr bool operator==(const Type_Info &ti) const noexcept {
return ti.m_type_info == m_type_info || *ti.m_type_info == *m_type_info;
} }
constexpr bool operator==(const std::type_info &ti) const noexcept { return !is_undef() && (*m_type_info) == ti; } #if !defined(_MSC_VER) || _MSC_VER != 1800
Type_Info(Type_Info&&) = default;
Type_Info& operator=(Type_Info&&) = default;
#endif
constexpr bool bare_equal(const Type_Info &ti) const noexcept { Type_Info(const Type_Info&) = default;
return ti.m_bare_type_info == m_bare_type_info || *ti.m_bare_type_info == *m_bare_type_info; Type_Info& operator=(const Type_Info&) = default;
CHAISCRIPT_CONSTEXPR bool operator<(const Type_Info &ti) const CHAISCRIPT_NOEXCEPT
{
return m_type_info < ti.m_type_info;
} }
constexpr bool bare_equal_type_info(const std::type_info &ti) const noexcept { return !is_undef() && (*m_bare_type_info) == ti; } CHAISCRIPT_CONSTEXPR bool operator==(const Type_Info &ti) const CHAISCRIPT_NOEXCEPT
{
return ti.m_type_info == m_type_info
|| (ti.m_type_info && m_type_info && *ti.m_type_info == *m_type_info);
}
constexpr bool is_const() const noexcept { return (m_flags & (1 << is_const_flag)) != 0; } CHAISCRIPT_CONSTEXPR bool operator==(const std::type_info &ti) const CHAISCRIPT_NOEXCEPT
constexpr bool is_reference() const noexcept { return (m_flags & (1 << is_reference_flag)) != 0; } {
constexpr bool is_void() const noexcept { return (m_flags & (1 << is_void_flag)) != 0; } return m_type_info != nullptr && (*m_type_info) == ti;
constexpr bool is_arithmetic() const noexcept { return (m_flags & (1 << is_arithmetic_flag)) != 0; } }
constexpr bool is_undef() const noexcept { return (m_flags & (1 << is_undef_flag)) != 0; }
constexpr bool is_pointer() const noexcept { return (m_flags & (1 << is_pointer_flag)) != 0; }
const char *name() const noexcept { CHAISCRIPT_CONSTEXPR bool bare_equal(const Type_Info &ti) const CHAISCRIPT_NOEXCEPT
if (!is_undef()) { {
return ti.m_bare_type_info == m_bare_type_info
|| (ti.m_bare_type_info && m_bare_type_info && *ti.m_bare_type_info == *m_bare_type_info);
}
CHAISCRIPT_CONSTEXPR bool bare_equal_type_info(const std::type_info &ti) const CHAISCRIPT_NOEXCEPT
{
return m_bare_type_info != nullptr
&& (*m_bare_type_info) == ti;
}
CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return m_is_const; }
CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return m_is_reference; }
CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return m_is_void; }
CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return m_is_arithmetic; }
CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return m_is_undef; }
CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return m_is_pointer; }
std::string name() const
{
if (m_type_info)
{
return m_type_info->name(); return m_type_info->name();
} else { } else {
return ""; return "";
} }
} }
const char *bare_name() const noexcept { std::string bare_name() const
if (!is_undef()) { {
if (m_bare_type_info)
{
return m_bare_type_info->name(); return m_bare_type_info->name();
} else { } else {
return ""; return "";
} }
} }
constexpr const std::type_info *bare_type_info() const noexcept { return m_bare_type_info; } CHAISCRIPT_CONSTEXPR const std::type_info *bare_type_info() const
{
void make_const() noexcept { m_flags |= (1 << is_const_flag); } return m_bare_type_info;
}
private: private:
struct Unknown_Type { const std::type_info *m_type_info;
const std::type_info *m_bare_type_info;
bool m_is_const;
bool m_is_reference;
bool m_is_pointer;
bool m_is_void;
bool m_is_arithmetic;
bool m_is_undef;
}; };
const std::type_info *m_type_info = &typeid(Unknown_Type); namespace detail
const std::type_info *m_bare_type_info = &typeid(Unknown_Type); {
static const int is_const_flag = 0;
static const int is_reference_flag = 1;
static const int is_pointer_flag = 2;
static const int is_void_flag = 3;
static const int is_arithmetic_flag = 4;
static const int is_undef_flag = 5;
unsigned int m_flags = (1 << is_undef_flag);
};
namespace detail {
/// Helper used to create a Type_Info object /// Helper used to create a Type_Info object
template<typename T> template<typename T>
struct Get_Type_Info { struct Get_Type_Info
constexpr static Type_Info get() noexcept { {
typedef T type;
static Type_Info get()
{
return Type_Info(std::is_const<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::value, return Type_Info(std::is_const<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::value,
std::is_reference<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value,
std::is_pointer<T>::value,
std::is_void<T>::value, std::is_void<T>::value,
(std::is_arithmetic<T>::value || std::is_arithmetic<typename std::remove_reference<T>::type>::value) (std::is_arithmetic<T>::value || std::is_arithmetic<typename std::remove_reference<T>::type>::value)
&& !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value, && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
@ -119,66 +147,66 @@ namespace chaiscript {
}; };
template<typename T> template<typename T>
struct Get_Type_Info<std::shared_ptr<T>> { struct Get_Type_Info<std::shared_ptr<T> >
constexpr static Type_Info get() noexcept { {
return Type_Info(std::is_const<T>::value, typedef T type;
std::is_reference<T>::value,
std::is_pointer<T>::value, static Type_Info get()
{
return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value,
std::is_void<T>::value, std::is_void<T>::value,
std::is_arithmetic<T>::value std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&& !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&typeid(std::shared_ptr<T> ), &typeid(std::shared_ptr<T> ),
&typeid(typename Bare_Type<T>::type)); &typeid(typename Bare_Type<T>::type));
} }
}; };
template<typename T> template<typename T>
struct Get_Type_Info<std::shared_ptr<T> &> : Get_Type_Info<std::shared_ptr<T>> { struct Get_Type_Info<const std::shared_ptr<T> &>
}; {
typedef T type;
template<typename T> static Type_Info get()
struct Get_Type_Info<const std::shared_ptr<T> &> { {
constexpr static Type_Info get() noexcept { return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value,
return Type_Info(std::is_const<T>::value,
std::is_reference<T>::value,
std::is_pointer<T>::value,
std::is_void<T>::value, std::is_void<T>::value,
std::is_arithmetic<T>::value std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&& !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&typeid(const std::shared_ptr<T> &), &typeid(const std::shared_ptr<T> &),
&typeid(typename Bare_Type<T>::type)); &typeid(typename Bare_Type<T>::type));
} }
}; };
template<typename T> template<typename T>
struct Get_Type_Info<std::reference_wrapper<T>> { struct Get_Type_Info<std::reference_wrapper<T> >
constexpr static Type_Info get() noexcept { {
return Type_Info(std::is_const<T>::value, typedef T type;
std::is_reference<T>::value,
std::is_pointer<T>::value, static Type_Info get()
{
return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value,
std::is_void<T>::value, std::is_void<T>::value,
std::is_arithmetic<T>::value std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&& !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&typeid(std::reference_wrapper<T> ), &typeid(std::reference_wrapper<T> ),
&typeid(typename Bare_Type<T>::type)); &typeid(typename Bare_Type<T>::type));
} }
}; };
template<typename T> template<typename T>
struct Get_Type_Info<const std::reference_wrapper<T> &> { struct Get_Type_Info<const std::reference_wrapper<T> &>
constexpr static Type_Info get() noexcept { {
return Type_Info(std::is_const<T>::value, typedef T type;
std::is_reference<T>::value,
std::is_pointer<T>::value, static Type_Info get()
{
return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value,
std::is_void<T>::value, std::is_void<T>::value,
std::is_arithmetic<T>::value std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&& !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
&typeid(const std::reference_wrapper<T> &), &typeid(const std::reference_wrapper<T> &),
&typeid(typename Bare_Type<T>::type)); &typeid(typename Bare_Type<T>::type));
} }
}; };
} // namespace detail }
/// \brief Creates a Type_Info object representing the type passed in /// \brief Creates a Type_Info object representing the type passed in
/// \tparam T Type of object to get a Type_Info for, derived from the passed in parameter /// \tparam T Type of object to get a Type_Info for, derived from the passed in parameter
@ -190,10 +218,12 @@ namespace chaiscript {
/// chaiscript::Type_Info ti = chaiscript::user_type(i); /// chaiscript::Type_Info ti = chaiscript::user_type(i);
/// \endcode /// \endcode
template<typename T> template<typename T>
constexpr Type_Info user_type(const T & /*t*/) noexcept { Type_Info user_type(const T &/*t*/)
{
return detail::Get_Type_Info<T>::get(); return detail::Get_Type_Info<T>::get();
} }
/// \brief Creates a Type_Info object representing the templated type /// \brief Creates a Type_Info object representing the templated type
/// \tparam T Type of object to get a Type_Info for /// \tparam T Type of object to get a Type_Info for
/// \return Type_Info for T /// \return Type_Info for T
@ -203,10 +233,12 @@ namespace chaiscript {
/// chaiscript::Type_Info ti = chaiscript::user_type<int>(); /// chaiscript::Type_Info ti = chaiscript::user_type<int>();
/// \endcode /// \endcode
template<typename T> template<typename T>
constexpr Type_Info user_type() noexcept { Type_Info user_type()
{
return detail::Get_Type_Info<T>::get(); return detail::Get_Type_Info<T>::get();
} }
} // namespace chaiscript }
#endif #endif

View File

@ -1,168 +1,130 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_ALGEBRAIC_HPP_ #ifndef CHAISCRIPT_ALGEBRAIC_HPP_
#define CHAISCRIPT_ALGEBRAIC_HPP_ #define CHAISCRIPT_ALGEBRAIC_HPP_
#include "../utility/hash.hpp"
#include <array>
#include <string> #include <string>
namespace chaiscript { namespace chaiscript
{
struct Operators { struct Operators {
enum class Opers { enum Opers
equals, {
less_than, boolean_flag,
greater_than, equals, less_than, greater_than, less_than_equal, greater_than_equal, not_equal,
less_than_equal, non_const_flag,
greater_than_equal, assign, pre_increment, pre_decrement, assign_product, assign_sum,
not_equal, assign_quotient, assign_difference,
assign, non_const_int_flag,
pre_increment, assign_bitwise_and, assign_bitwise_or, assign_shift_left, assign_shift_right,
pre_decrement, assign_remainder, assign_bitwise_xor,
assign_product, const_int_flag,
assign_sum, shift_left, shift_right, remainder, bitwise_and, bitwise_or, bitwise_xor, bitwise_complement,
assign_quotient, const_flag,
assign_difference, sum, quotient, product, difference, unary_plus, unary_minus,
assign_bitwise_and,
assign_bitwise_or,
assign_shift_left,
assign_shift_right,
assign_remainder,
assign_bitwise_xor,
shift_left,
shift_right,
remainder,
bitwise_and,
bitwise_or,
bitwise_xor,
bitwise_complement,
sum,
quotient,
product,
difference,
unary_plus,
unary_minus,
invalid invalid
}; };
constexpr static std::string_view to_string(Opers t_oper) noexcept { static const char *to_string(Opers t_oper) {
constexpr const std::array opers const char *opers[] = {
= {"", "==", "<", ">", "<=", ">=", "!=", "", "=", "++", "--", "*=", "+=", "/=", "-=", "", "&=", "|=", "<<=", ">>=", "%=", "^=", "", "<<", ">>", "%", "&", "|", "^", "~", "", "+", "/", "*", "-", "+", "-", ""}; "",
return opers[static_cast<std::size_t>(t_oper)]; "==", "<", ">", "<=", ">=", "!=",
} "",
"=", "++", "--", "*=", "+=",
constexpr static Opers to_operator(std::string_view t_str, bool t_is_unary = false) noexcept { "/=", "-=",
#ifdef CHAISCRIPT_MSVC "",
#pragma warning(push) "&=", "|=", "<<=", ">>=",
#pragma warning(disable : 4307) "%=", "^=",
#endif "",
"<<", ">>", "%", "&", "|", "^", "~",
const auto op_hash = utility::hash(t_str); "",
switch (op_hash) { "+", "/", "*", "-", "+", "-",
case utility::hash("=="): { ""
return Opers::equals;
}
case utility::hash("<"): {
return Opers::less_than;
}
case utility::hash(">"): {
return Opers::greater_than;
}
case utility::hash("<="): {
return Opers::less_than_equal;
}
case utility::hash(">="): {
return Opers::greater_than_equal;
}
case utility::hash("!="): {
return Opers::not_equal;
}
case utility::hash("="): {
return Opers::assign;
}
case utility::hash("++"): {
return Opers::pre_increment;
}
case utility::hash("--"): {
return Opers::pre_decrement;
}
case utility::hash("*="): {
return Opers::assign_product;
}
case utility::hash("+="): {
return Opers::assign_sum;
}
case utility::hash("-="): {
return Opers::assign_difference;
}
case utility::hash("&="): {
return Opers::assign_bitwise_and;
}
case utility::hash("|="): {
return Opers::assign_bitwise_or;
}
case utility::hash("<<="): {
return Opers::assign_shift_left;
}
case utility::hash(">>="): {
return Opers::assign_shift_right;
}
case utility::hash("%="): {
return Opers::assign_remainder;
}
case utility::hash("^="): {
return Opers::assign_bitwise_xor;
}
case utility::hash("<<"): {
return Opers::shift_left;
}
case utility::hash(">>"): {
return Opers::shift_right;
}
case utility::hash("%"): {
return Opers::remainder;
}
case utility::hash("&"): {
return Opers::bitwise_and;
}
case utility::hash("|"): {
return Opers::bitwise_or;
}
case utility::hash("^"): {
return Opers::bitwise_xor;
}
case utility::hash("~"): {
return Opers::bitwise_complement;
}
case utility::hash("+"): {
return t_is_unary ? Opers::unary_plus : Opers::sum;
}
case utility::hash("-"): {
return t_is_unary ? Opers::unary_minus : Opers::difference;
}
case utility::hash("/"): {
return Opers::quotient;
}
case utility::hash("*"): {
return Opers::product;
}
default: {
return Opers::invalid;
}
}
#ifdef CHAISCRIPT_MSVC
#pragma warning(pop)
#endif
}
}; };
} // namespace chaiscript return opers[t_oper];
}
static Opers to_operator(const std::string &t_str, bool t_is_unary = false)
{
if (t_str == "==")
{
return equals;
} else if (t_str == "<") {
return less_than;
} else if (t_str == ">") {
return greater_than;
} else if (t_str == "<=") {
return less_than_equal;
} else if (t_str == ">=") {
return greater_than_equal;
} else if (t_str == "!=") {
return not_equal;
} else if (t_str == "=") {
return assign;
} else if (t_str == "++") {
return pre_increment;
} else if (t_str == "--") {
return pre_decrement;
} else if (t_str == "*=") {
return assign_product;
} else if (t_str == "+=") {
return assign_sum;
} else if (t_str == "-=") {
return assign_difference;
} else if (t_str == "&=") {
return assign_bitwise_and;
} else if (t_str == "|=") {
return assign_bitwise_or;
} else if (t_str == "<<=") {
return assign_shift_left;
} else if (t_str == ">>=") {
return assign_shift_right;
} else if (t_str == "%=") {
return assign_remainder;
} else if (t_str == "^=") {
return assign_bitwise_xor;
} else if (t_str == "<<") {
return shift_left;
} else if (t_str == ">>") {
return shift_right;
} else if (t_str == "%") {
return remainder;
} else if (t_str == "&") {
return bitwise_and;
} else if (t_str == "|") {
return bitwise_or;
} else if (t_str == "^") {
return bitwise_xor;
} else if (t_str == "~") {
return bitwise_complement;
} else if (t_str == "+") {
if (t_is_unary) {
return unary_plus;
} else {
return sum;
}
} else if (t_str == "-") {
if (t_is_unary) {
return unary_minus;
} else {
return difference;
}
} else if (t_str == "/") {
return quotient;
} else if (t_str == "*") {
return product;
} else {
return invalid;
}
}
};
}
#endif /* _CHAISCRIPT_ALGEBRAIC_HPP */ #endif /* _CHAISCRIPT_ALGEBRAIC_HPP */

View File

@ -1,12 +1,9 @@
// This file is distributed under the BSD License. // This file is distributed under the BSD License.
// See "license.txt" for details. // See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // Copyright 2009-2015, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.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
#ifndef CHAISCRIPT_COMMON_HPP_ #ifndef CHAISCRIPT_COMMON_HPP_
#define CHAISCRIPT_COMMON_HPP_ #define CHAISCRIPT_COMMON_HPP_
@ -22,281 +19,88 @@
#include "../dispatchkit/dispatchkit.hpp" #include "../dispatchkit/dispatchkit.hpp"
#include "../dispatchkit/proxy_functions.hpp" #include "../dispatchkit/proxy_functions.hpp"
#include "../dispatchkit/type_info.hpp" #include "../dispatchkit/type_info.hpp"
#include <unordered_set>
namespace chaiscript { namespace chaiscript {
struct AST_Node; struct AST_Node;
} // namespace chaiscript } // namespace chaiscript
namespace chaiscript { namespace chaiscript
struct Name_Validator { {
template<typename T>
static bool is_reserved_word(const T &s) noexcept {
const static std::unordered_set<std::uint32_t>
words{utility::hash("def"), utility::hash("fun"), utility::hash("while"), utility::hash("for"), utility::hash("if"), utility::hash("else"), utility::hash("&&"), utility::hash("||"), utility::hash(","), utility::hash("auto"), utility::hash("return"), utility::hash("break"), utility::hash("true"), utility::hash("false"), utility::hash("class"), utility::hash("attr"), utility::hash("var"), utility::hash("global"), utility::hash("GLOBAL"), utility::hash("_"), utility::hash("__LINE__"), utility::hash("__FILE__"), utility::hash("__FUNC__"), utility::hash("__CLASS__"), utility::hash("const"), utility::hash("using"), utility::hash("enum")};
return words.count(utility::hash(s)) == 1;
}
template<typename T>
static bool valid_object_name(const T &name) noexcept {
return name.find("::") == std::string::npos && !is_reserved_word(name);
}
template<typename T>
static void validate_object_name(const T &name) {
if (is_reserved_word(name)) {
throw exception::reserved_word_error(std::string(name));
}
if (name.find("::") != std::string::npos) {
throw exception::illegal_name_error(std::string(name));
}
}
};
/// Signature of module entry point that all binary loadable modules must implement. /// Signature of module entry point that all binary loadable modules must implement.
using Create_Module_Func = ModulePtr (*)(); typedef ModulePtr (*Create_Module_Func)();
/// Types of AST nodes available to the parser and eval /// Types of AST nodes available to the parser and eval
enum class AST_Node_Type { class AST_Node_Type {
Id, public:
Fun_Call, enum Type { Error, Int, Float, Id, Char, Str, Eol, Fun_Call, Arg_List, Variable, Equation, Var_Decl,
Unused_Return_Fun_Call, Comparison, Addition, Subtraction, Multiplication, Division, Modulus, Array_Call, Dot_Access, Quoted_String, Single_Quoted_String,
Arg_List, Lambda, Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range,
Equation, Inline_Range, Annotation, Try, Catch, Finally, Method, Attr_Decl, Shift, Equality, Bitwise_And, Bitwise_Xor, Bitwise_Or,
Var_Decl, Logical_And, Logical_Or, Reference, Switch, Case, Default, Ternary_Cond, Noop, Class, Binary, Arg, Global_Decl
Assign_Decl, };
Array_Call,
Dot_Access,
Lambda,
Block,
Scopeless_Block,
Def,
While,
If,
For,
Ranged_For,
Inline_Array,
Inline_Map,
Return,
File,
Prefix,
Break,
Continue,
Map_Pair,
Value_Range,
Inline_Range,
Try,
Catch,
Finally,
Method,
Attr_Decl,
Logical_And,
Logical_Or,
Reference,
Switch,
Case,
Default,
Noop,
Class,
Binary,
Arg,
Global_Decl,
Constant,
Compiled,
Const_Var_Decl,
Const_Assign_Decl,
Using,
Enum,
Namespace_Block
}; };
enum class Operator_Precedence { namespace
Ternary_Cond, {
Logical_Or,
Logical_And,
Bitwise_Or,
Bitwise_Xor,
Bitwise_And,
Equality,
Comparison,
Shift,
Addition,
Multiplication,
Prefix
};
namespace {
/// Helper lookup to get the name of each node type /// Helper lookup to get the name of each node type
constexpr const char *ast_node_type_to_string(AST_Node_Type ast_node_type) noexcept { const char *ast_node_type_to_string(int ast_node_type) {
constexpr std::array ast_node_types = {"Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", "Assign_Decl", "Array_Call", "Dot_Access", "Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", "Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Noop", "Class", "Binary", "Arg", "Global_Decl", "Constant", "Compiled", "Const_Var_Decl", "Const_Assign_Decl", "Using", "Enum", "Namespace_Block"}; const char *ast_node_types[] = { "Internal Parser Error", "Int", "Float", "Id", "Char", "Str", "Eol", "Fun_Call", "Arg_List", "Variable", "Equation", "Var_Decl",
"Comparison", "Addition", "Subtraction", "Multiplication", "Division", "Modulus", "Array_Call", "Dot_Access", "Quoted_String", "Single_Quoted_String",
"Lambda", "Block", "Def", "While", "If", "For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range",
"Inline_Range", "Annotation", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Shift", "Equality", "Bitwise_And", "Bitwise_Xor", "Bitwise_Or",
"Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Ternary Condition", "Noop", "Class", "Binary", "Arg"};
return ast_node_types[static_cast<std::size_t>(ast_node_type)]; return ast_node_types[ast_node_type];
}
} }
} // namespace
/// \brief Convenience type for file positions /// \brief Convenience type for file positions
struct File_Position { struct File_Position {
int line = 0; int line;
int column = 0; int column;
constexpr File_Position(int t_file_line, int t_file_column) noexcept File_Position(int t_file_line, int t_file_column)
: line(t_file_line) : line(t_file_line), column(t_file_column) { }
, column(t_file_column) {
}
constexpr File_Position() noexcept = default; File_Position() : line(0), column(0) { }
}; };
struct Parse_Location { struct Parse_Location {
Parse_Location(std::string t_fname = "", const int t_start_line = 0, const int t_start_col = 0, const int t_end_line = 0, const int t_end_col = 0) Parse_Location(std::string t_fname="", const int t_start_line=0, const int t_start_col=0,
: start(t_start_line, t_start_col) const int t_end_line=0, const int t_end_col=0)
, end(t_end_line, t_end_col) : start(t_start_line, t_start_col),
, filename(std::make_shared<std::string>(std::move(t_fname))) { end(t_end_line, t_end_col),
filename(std::make_shared<std::string>(std::move(t_fname)))
{
} }
Parse_Location(std::shared_ptr<std::string> t_fname, Parse_Location(std::shared_ptr<std::string> t_fname, const int t_start_line=0, const int t_start_col=0,
const int t_start_line = 0, const int t_end_line=0, const int t_end_col=0)
const int t_start_col = 0, : start(t_start_line, t_start_col),
const int t_end_line = 0, end(t_end_line, t_end_col),
const int t_end_col = 0) filename(std::move(t_fname))
: start(t_start_line, t_start_col) {
, end(t_end_line, t_end_col)
, filename(std::move(t_fname)) {
} }
File_Position start; File_Position start;
File_Position end; File_Position end;
std::shared_ptr<std::string> filename; std::shared_ptr<std::string> filename;
}; };
/// \brief Struct that doubles as both a parser ast_node and an AST node.
struct AST_Node {
public:
const AST_Node_Type identifier;
const std::string text;
Parse_Location location;
const std::string &filename() const noexcept { return *location.filename; }
const File_Position &start() const noexcept { return location.start; }
const File_Position &end() const noexcept { return location.end; }
std::string pretty_print() const {
std::ostringstream oss;
oss << text;
for (auto &elem : get_children()) {
oss << elem.get().pretty_print() << ' ';
}
return oss.str();
}
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;
/// Prints the contents of an AST node, including its children, recursively
std::string to_string(const std::string &t_prepend = "") const {
std::ostringstream oss;
oss << t_prepend << "(" << ast_node_type_to_string(this->identifier) << ") " << this->text << " : " << this->location.start.line
<< ", " << this->location.start.column << '\n';
for (auto &elem : get_children()) {
oss << elem.get().to_string(t_prepend + " ");
}
return oss.str();
}
static inline bool get_bool_condition(const Boxed_Value &t_bv, const chaiscript::detail::Dispatch_State &t_ss);
virtual ~AST_Node() noexcept = default;
AST_Node(AST_Node &&) = default;
AST_Node &operator=(AST_Node &&) = delete;
AST_Node(const AST_Node &) = delete;
AST_Node &operator=(const AST_Node &) = delete;
protected:
AST_Node(std::string t_ast_node_text, AST_Node_Type t_id, Parse_Location t_loc)
: identifier(t_id)
, text(std::move(t_ast_node_text))
, location(std::move(t_loc)) {
}
};
/// \brief Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree /// \brief Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree
using AST_NodePtr = std::unique_ptr<AST_Node>; typedef std::shared_ptr<AST_Node> AST_NodePtr;
using AST_NodePtr_Const = std::unique_ptr<const AST_Node>; typedef std::shared_ptr<const AST_Node> AST_NodePtr_Const;
struct AST_Node_Trace {
const AST_Node_Type identifier;
const std::string text;
Parse_Location location;
const std::string &filename() const noexcept { return *location.filename; }
const File_Position &start() const noexcept { return location.start; }
const File_Position &end() const noexcept { 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;
};
/// \brief Classes which may be thrown during error cases when ChaiScript is executing. /// \brief Classes which may be thrown during error cases when ChaiScript is executing.
namespace exception { namespace exception
/// \brief Thrown if an error occurs while attempting to load a binary module {
struct load_module_error : std::runtime_error {
explicit load_module_error(const std::string &t_reason)
: std::runtime_error(t_reason) {
}
load_module_error(const std::string &t_name, const std::vector<load_module_error> &t_errors)
: std::runtime_error(format_error(t_name, t_errors)) {
}
load_module_error(const load_module_error &) = default;
~load_module_error() noexcept override = default;
static std::string format_error(const std::string &t_name, const std::vector<load_module_error> &t_errors) {
std::stringstream ss;
ss << "Error loading module '" << t_name << "'\n"
<< " The following locations were searched:\n";
for (const auto &err : t_errors) {
ss << " " << err.what() << "\n";
}
return ss.str();
}
};
/// Errors generated during parsing or evaluation /// Errors generated during parsing or evaluation
struct eval_error : std::runtime_error { struct eval_error : std::runtime_error {
@ -304,57 +108,50 @@ namespace chaiscript {
File_Position start_position; File_Position start_position;
std::string filename; std::string filename;
std::string detail; std::string detail;
std::vector<AST_Node_Trace> call_stack; std::vector<AST_NodePtr_Const> 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,
bool t_dot_notation,
const chaiscript::detail::Dispatch_Engine &t_ss) CHAISCRIPT_NOEXCEPT :
std::runtime_error(format(t_why, t_where, t_fname, t_parameters, t_dot_notation, t_ss)),
reason(t_why), start_position(t_where), filename(t_fname), detail(format_detail(t_functions, t_dot_notation, t_ss))
{}
eval_error(const std::string &t_why, eval_error(const std::string &t_why,
const File_Position &t_where, const std::vector<Boxed_Value> &t_parameters, const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
const std::string &t_fname,
const std::vector<Boxed_Value> &t_parameters,
const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
bool t_dot_notation, bool t_dot_notation,
const chaiscript::detail::Dispatch_Engine &t_ss) noexcept const chaiscript::detail::Dispatch_Engine &t_ss) CHAISCRIPT_NOEXCEPT :
: std::runtime_error(format(t_why, t_where, t_fname, t_parameters, t_dot_notation, t_ss)) std::runtime_error(format(t_why, t_parameters, t_dot_notation, t_ss)),
, reason(t_why) reason(t_why), detail(format_detail(t_functions, t_dot_notation, t_ss))
, start_position(t_where) {}
, filename(t_fname)
, detail(format_detail(t_functions, t_dot_notation, t_ss)) {
}
eval_error(const std::string &t_why,
const std::vector<Boxed_Value> &t_parameters,
const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
bool t_dot_notation,
const chaiscript::detail::Dispatch_Engine &t_ss) noexcept
: std::runtime_error(format(t_why, t_parameters, t_dot_notation, t_ss))
, reason(t_why)
, detail(format_detail(t_functions, t_dot_notation, t_ss)) {
}
eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname) noexcept eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname) CHAISCRIPT_NOEXCEPT :
: std::runtime_error(format(t_why, t_where, t_fname)) std::runtime_error(format(t_why, t_where, t_fname)),
, reason(t_why) reason(t_why), start_position(t_where), filename(t_fname)
, start_position(t_where) {}
, filename(t_fname) {
}
explicit eval_error(const std::string &t_why) noexcept eval_error(const std::string &t_why) CHAISCRIPT_NOEXCEPT
: std::runtime_error("Error: \"" + t_why + "\" ") : std::runtime_error("Error: \"" + t_why + "\" "),
, reason(t_why) { reason(t_why)
} {}
eval_error(const eval_error &) = default; eval_error(const eval_error &) = default;
std::string pretty_print() const { std::string pretty_print() const
{
std::ostringstream ss; std::ostringstream ss;
ss << what(); ss << what();
if (!call_stack.empty()) { if (call_stack.size() > 0) {
ss << "during evaluation at (" << fname(call_stack[0]) << " " << startpos(call_stack[0]) << ")\n"; ss << "during evaluation at (" << fname(call_stack[0]) << " " << startpos(call_stack[0]) << ")\n";
ss << '\n' ss << '\n' << detail << '\n';
<< detail << '\n';
ss << " " << fname(call_stack[0]) << " (" << startpos(call_stack[0]) << ") '" << pretty(call_stack[0]) << "'"; ss << " " << fname(call_stack[0]) << " (" << startpos(call_stack[0]) << ") '" << pretty(call_stack[0]) << "'";
for (size_t j = 1; j < call_stack.size(); ++j) { for (size_t j = 1; j < call_stack.size(); ++j) {
if (id(call_stack[j]) != chaiscript::AST_Node_Type::Block && id(call_stack[j]) != chaiscript::AST_Node_Type::File) { if (id(call_stack[j]) != chaiscript::AST_Node_Type::Block
&& id(call_stack[j]) != chaiscript::AST_Node_Type::File)
{
ss << '\n'; ss << '\n';
ss << " from " << fname(call_stack[j]) << " (" << startpos(call_stack[j]) << ") '" << pretty(call_stack[j]) << "'"; ss << " from " << fname(call_stack[j]) << " (" << startpos(call_stack[j]) << ") '" << pretty(call_stack[j]) << "'";
} }
@ -364,42 +161,54 @@ namespace chaiscript {
return ss.str(); return ss.str();
} }
~eval_error() noexcept override = default; virtual ~eval_error() CHAISCRIPT_NOEXCEPT {}
private: private:
template<typename T> template<typename T>
static AST_Node_Type id(const T &t) noexcept { static int id(const T& t)
return t.identifier; {
return t->identifier;
} }
template<typename T> template<typename T>
static std::string pretty(const T &t) { static std::string pretty(const T& t)
return t.pretty_print(); {
return t->pretty_print();
} }
template<typename T> template<typename T>
static const std::string &fname(const T &t) noexcept { static const std::string &fname(const T& t)
return t.filename(); {
return t->filename();
} }
template<typename T> template<typename T>
static std::string startpos(const T &t) { static std::string startpos(const T& t)
{
std::ostringstream oss; std::ostringstream oss;
oss << t.start().line << ", " << t.start().column; oss << t->start().line << ", " << t->start().column;
return oss.str(); return oss.str();
} }
static std::string format_why(const std::string &t_why) { return "Error: \"" + t_why + "\""; } static std::string format_why(const std::string &t_why)
{
return "Error: \"" + t_why + "\"";
}
static std::string format_types(const Const_Proxy_Function &t_func, bool t_dot_notation, const chaiscript::detail::Dispatch_Engine &t_ss) { static std::string format_types(const Const_Proxy_Function &t_func,
assert(t_func); bool t_dot_notation,
const chaiscript::detail::Dispatch_Engine &t_ss)
{
int arity = t_func->get_arity(); int arity = t_func->get_arity();
std::vector<Type_Info> types = t_func->get_param_types(); std::vector<Type_Info> types = t_func->get_param_types();
std::string retval; std::string retval;
if (arity == -1) { if (arity == -1)
{
retval = "(...)"; retval = "(...)";
if (t_dot_notation) { if (t_dot_notation)
{
retval = "(Object)." + retval; retval = "(Object)." + retval;
} }
} else if (types.size() <= 1) { } else if (types.size() <= 1) {
@ -410,13 +219,18 @@ namespace chaiscript {
std::string paramstr; std::string paramstr;
for (size_t index = 1; index != types.size(); ++index) { for (size_t index = 1;
index != types.size();
++index)
{
paramstr += (types[index].is_const()?"const ":""); paramstr += (types[index].is_const()?"const ":"");
paramstr += t_ss.get_type_name(types[index]); paramstr += t_ss.get_type_name(types[index]);
if (index == 1 && t_dot_notation) { if (index == 1 && t_dot_notation)
{
paramstr += ").("; paramstr += ").(";
if (types.size() == 2) { if (types.size() == 2)
{
paramstr += ", "; paramstr += ", ";
} }
} else { } else {
@ -430,15 +244,19 @@ namespace chaiscript {
retval = ss.str(); retval = ss.str();
} }
std::shared_ptr<const dispatch::Dynamic_Proxy_Function> dynfun std::shared_ptr<const dispatch::Dynamic_Proxy_Function> dynfun
= std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_func); = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_func);
if (dynfun && dynfun->has_parse_tree()) { if (dynfun)
{
Proxy_Function f = dynfun->get_guard(); Proxy_Function f = dynfun->get_guard();
if (f) { if (f)
{
auto dynfunguard = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(f); auto dynfunguard = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(f);
if (dynfunguard && dynfunguard->has_parse_tree()) { if (dynfunguard)
{
retval += " : " + format_guard(dynfunguard->get_parse_tree()); retval += " : " + format_guard(dynfunguard->get_parse_tree());
} }
} }
@ -450,50 +268,69 @@ namespace chaiscript {
} }
template<typename T> template<typename T>
static std::string format_guard(const T &t) { static std::string format_guard(const T &t)
return t.pretty_print(); {
return t->pretty_print();
} }
template<typename T> template<typename T>
static std::string format_location(const T &t) { static std::string format_location(const T &t)
{
if (t) {
std::ostringstream oss; std::ostringstream oss;
oss << "(" << t.filename() << " " << t.start().line << ", " << t.start().column << ")"; oss << "(" << t->filename() << " " << t->start().line << ", " << t->start().column << ")";
return oss.str(); return oss.str();
} else {
return "(internal)";
}
} }
static std::string format_detail(const std::vector<chaiscript::Const_Proxy_Function> &t_functions, static std::string format_detail(const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
bool t_dot_notation, bool t_dot_notation,
const chaiscript::detail::Dispatch_Engine &t_ss) { const chaiscript::detail::Dispatch_Engine &t_ss)
{
std::stringstream ss; std::stringstream ss;
if (t_functions.size() == 1) { if (t_functions.size() == 1)
assert(t_functions[0]); {
ss << " Expected: " << format_types(t_functions[0], t_dot_notation, t_ss) << '\n'; ss << " Expected: " << format_types(t_functions[0], t_dot_notation, t_ss) << '\n';
} else { } else {
ss << " " << t_functions.size() << " overloads available:\n"; ss << " " << t_functions.size() << " overloads available:\n";
for (const auto &t_function : t_functions) { for (const auto & t_function : t_functions)
{
ss << " " << format_types((t_function), t_dot_notation, t_ss) << '\n'; ss << " " << format_types((t_function), t_dot_notation, t_ss) << '\n';
} }
} }
return ss.str(); return ss.str();
} }
static std::string static std::string format_parameters(const std::vector<Boxed_Value> &t_parameters,
format_parameters(const std::vector<Boxed_Value> &t_parameters, bool t_dot_notation, const chaiscript::detail::Dispatch_Engine &t_ss) { bool t_dot_notation,
const chaiscript::detail::Dispatch_Engine &t_ss)
{
std::stringstream ss; std::stringstream ss;
ss << "("; ss << "(";
if (!t_parameters.empty()) { if (!t_parameters.empty())
{
std::string paramstr; std::string paramstr;
for (auto itr = t_parameters.begin(); itr != t_parameters.end(); ++itr) { for (auto itr = t_parameters.begin();
itr != t_parameters.end();
++itr)
{
paramstr += (itr->is_const()?"const ":""); paramstr += (itr->is_const()?"const ":"");
paramstr += t_ss.type_name(*itr); paramstr += t_ss.type_name(*itr);
if (itr == t_parameters.begin() && t_dot_notation) { if (itr == t_parameters.begin() && t_dot_notation)
{
paramstr += ").("; paramstr += ").(";
if (t_parameters.size() == 1) { if (t_parameters.size() == 1)
{
paramstr += ", "; paramstr += ", ";
} }
} else { } else {
@ -508,10 +345,12 @@ namespace chaiscript {
return ss.str(); return ss.str();
} }
static std::string format_filename(const std::string &t_fname) { static std::string format_filename(const std::string &t_fname)
{
std::stringstream ss; std::stringstream ss;
if (t_fname != "__EVAL__") { if (t_fname != "__EVAL__")
{
ss << "in '" << t_fname << "' "; ss << "in '" << t_fname << "' ";
} else { } else {
ss << "during evaluation "; ss << "during evaluation ";
@ -520,18 +359,16 @@ namespace chaiscript {
return ss.str(); return ss.str();
} }
static std::string format_location(const File_Position &t_where) { static std::string format_location(const File_Position &t_where)
{
std::stringstream ss; std::stringstream ss;
ss << "at (" << t_where.line << ", " << t_where.column << ")"; ss << "at (" << t_where.line << ", " << t_where.column << ")";
return ss.str(); return ss.str();
} }
static std::string format(const std::string &t_why, static std::string format(const std::string &t_why, const File_Position &t_where, const std::string &t_fname,
const File_Position &t_where, const std::vector<Boxed_Value> &t_parameters, bool t_dot_notation, const chaiscript::detail::Dispatch_Engine &t_ss)
const std::string &t_fname, {
const std::vector<Boxed_Value> &t_parameters,
bool t_dot_notation,
const chaiscript::detail::Dispatch_Engine &t_ss) {
std::stringstream ss; std::stringstream ss;
ss << format_why(t_why); ss << format_why(t_why);
@ -551,7 +388,8 @@ namespace chaiscript {
static std::string format(const std::string &t_why, static std::string format(const std::string &t_why,
const std::vector<Boxed_Value> &t_parameters, const std::vector<Boxed_Value> &t_parameters,
bool t_dot_notation, bool t_dot_notation,
const chaiscript::detail::Dispatch_Engine &t_ss) { const chaiscript::detail::Dispatch_Engine &t_ss)
{
std::stringstream ss; std::stringstream ss;
ss << format_why(t_why); ss << format_why(t_why);
@ -563,7 +401,8 @@ namespace chaiscript {
return ss.str(); return ss.str();
} }
static std::string format(const std::string &t_why, const File_Position &t_where, const std::string &t_fname) { static std::string format(const std::string &t_why, const File_Position &t_where, const std::string &t_fname)
{
std::stringstream ss; std::stringstream ss;
ss << format_why(t_why); ss << format_why(t_why);
@ -578,125 +417,218 @@ namespace chaiscript {
} }
}; };
/// Errors generated when loading a file /// Errors generated when loading a file
struct file_not_found_error : std::runtime_error { struct file_not_found_error : std::runtime_error {
explicit file_not_found_error(const std::string &t_filename) file_not_found_error(const std::string &t_filename) CHAISCRIPT_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(const file_not_found_error &) = default;
~file_not_found_error() noexcept override = default; virtual ~file_not_found_error() CHAISCRIPT_NOEXCEPT {}
std::string filename;
}; };
} // namespace exception }
// static
bool AST_Node::get_bool_condition(const Boxed_Value &t_bv, const chaiscript::detail::Dispatch_State &t_ss) { /// \brief Struct that doubles as both a parser ast_node and an AST node.
struct AST_Node : std::enable_shared_from_this<AST_Node> {
public:
const int identifier; //< \todo shouldn't this be a strongly typed enum value?
const std::string text;
Parse_Location location;
std::vector<AST_NodePtr> children;
AST_NodePtr annotation;
const std::string &filename() const {
return *location.filename;
}
const File_Position &start() const {
return location.start;
}
const File_Position &end() const {
return location.end;
}
virtual std::string pretty_print() const
{
std::ostringstream oss;
oss << text;
for (auto & elem : this->children) {
oss << elem->pretty_print();
}
return oss.str();
}
/// Prints the contents of an AST node, including its children, recursively
std::string to_string(const std::string &t_prepend = "") const {
std::ostringstream oss;
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->children) {
oss << elem->to_string(t_prepend + " ");
}
return oss.str();
}
Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const
{
try { try {
return t_ss->boxed_cast<bool>(t_bv); return eval_internal(t_e);
} catch (const exception::bad_boxed_cast &) { } catch (exception::eval_error &ee) {
ee.call_stack.push_back(shared_from_this());
throw;
}
}
static bool get_bool_condition(const Boxed_Value &t_bv) {
try {
return boxed_cast<bool>(t_bv);
}
catch (const exception::bad_boxed_cast &) {
throw exception::eval_error("Condition not boolean"); throw exception::eval_error("Condition not boolean");
} }
} }
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(const AST_Node &t, std::string prepend = "") const = 0;
virtual void *get_tracer_ptr() = 0;
virtual ~ChaiScript_Parser_Base() = default;
ChaiScript_Parser_Base() = default;
ChaiScript_Parser_Base(ChaiScript_Parser_Base &&) = default;
ChaiScript_Parser_Base &operator=(ChaiScript_Parser_Base &&) = delete;
ChaiScript_Parser_Base &operator=(const ChaiScript_Parser_Base &&) = delete;
template<typename T> void replace_child(const AST_NodePtr &t_child, const AST_NodePtr &t_new_child)
T &get_tracer() noexcept { {
// to do type check this somehow? std::replace(children.begin(), children.end(), t_child, t_new_child);
return *static_cast<T *>(get_tracer_ptr());
} }
protected: virtual ~AST_Node() {}
ChaiScript_Parser_Base(const ChaiScript_Parser_Base &) = default;
};
} // namespace parser
namespace eval { protected:
namespace detail { AST_Node(std::string t_ast_node_text, int t_id, Parse_Location t_loc,
std::vector<AST_NodePtr> t_children = std::vector<AST_NodePtr>()) :
identifier(t_id), text(std::move(t_ast_node_text)),
location(std::move(t_loc)),
children(std::move(t_children))
{
}
virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const
{
throw std::runtime_error("Undispatched ast_node (internal error)");
}
private:
// Copy and assignment explicitly unimplemented
AST_Node(const AST_Node &) = delete;
AST_Node& operator=(const AST_Node &) = delete;
};
namespace eval
{
namespace detail
{
/// Special type for returned values /// Special type for returned values
struct Return_Value { struct Return_Value {
Boxed_Value retval; Boxed_Value retval;
Return_Value(Boxed_Value t_return_value) : retval(std::move(t_return_value)) { }
}; };
/// Special type indicating a call to 'break' /// Special type indicating a call to 'break'
struct Break_Loop { struct Break_Loop {
Break_Loop() { }
}; };
/// Special type indicating a call to 'continue' /// Special type indicating a call to 'continue'
struct Continue_Loop { struct Continue_Loop {
Continue_Loop() { }
}; };
/// Creates a new scope then pops it on destruction /// Creates a new scope then pops it on destruction
struct Scope_Push_Pop { struct Scope_Push_Pop
Scope_Push_Pop(Scope_Push_Pop &&) = default; {
Scope_Push_Pop &operator=(Scope_Push_Pop &&) = delete;
Scope_Push_Pop(const Scope_Push_Pop &) = delete; Scope_Push_Pop(const Scope_Push_Pop &) = delete;
Scope_Push_Pop& operator=(const Scope_Push_Pop &) = delete; Scope_Push_Pop& operator=(const Scope_Push_Pop &) = delete;
explicit Scope_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) Scope_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds)
: m_ds(t_ds) { : m_ds(t_ds)
m_ds->new_scope(m_ds.stack_holder()); {
m_ds.get()->new_scope(m_ds.get().stack_holder());
}
~Scope_Push_Pop()
{
m_ds.get()->pop_scope(m_ds.get().stack_holder());
} }
~Scope_Push_Pop() { m_ds->pop_scope(m_ds.stack_holder()); }
private: private:
const chaiscript::detail::Dispatch_State &m_ds; std::reference_wrapper<const chaiscript::detail::Dispatch_State> m_ds;
}; };
/// Creates a new function call and pops it on destruction /// Creates a new function call and pops it on destruction
struct Function_Push_Pop { struct Function_Push_Pop
Function_Push_Pop(Function_Push_Pop &&) = default; {
Function_Push_Pop &operator=(Function_Push_Pop &&) = delete;
Function_Push_Pop(const Function_Push_Pop &) = delete; Function_Push_Pop(const Function_Push_Pop &) = delete;
Function_Push_Pop& operator=(const Function_Push_Pop &) = delete; Function_Push_Pop& operator=(const Function_Push_Pop &) = delete;
explicit Function_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) Function_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds)
: m_ds(t_ds) { : m_ds(t_ds)
m_ds->new_function_call(m_ds.stack_holder(), m_ds.conversion_saves()); {
m_ds.get()->new_function_call(m_ds.get().stack_holder());
} }
~Function_Push_Pop() { m_ds->pop_function_call(m_ds.stack_holder(), m_ds.conversion_saves()); } ~Function_Push_Pop()
{
m_ds.get()->pop_function_call(m_ds.get().stack_holder());
}
void save_params(const std::vector<Boxed_Value> &t_params)
{
m_ds.get()->save_function_params(t_params);
}
void save_params(std::initializer_list<Boxed_Value> t_params)
{
m_ds.get()->save_function_params(std::move(t_params));
}
void save_params(const Function_Params &t_params) { m_ds->save_function_params(t_params); }
private: private:
const chaiscript::detail::Dispatch_State &m_ds; std::reference_wrapper<const chaiscript::detail::Dispatch_State> m_ds;
}; };
/// Creates a new scope then pops it on destruction /// Creates a new scope then pops it on destruction
struct Stack_Push_Pop { struct Stack_Push_Pop
Stack_Push_Pop(Stack_Push_Pop &&) = default; {
Stack_Push_Pop &operator=(Stack_Push_Pop &&) = delete;
Stack_Push_Pop(const Stack_Push_Pop &) = delete; Stack_Push_Pop(const Stack_Push_Pop &) = delete;
Stack_Push_Pop& operator=(const Stack_Push_Pop &) = delete; Stack_Push_Pop& operator=(const Stack_Push_Pop &) = delete;
explicit Stack_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) Stack_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds)
: m_ds(t_ds) { : m_ds(t_ds)
m_ds->new_stack(m_ds.stack_holder()); {
m_ds.get()->new_stack(m_ds.get().stack_holder());
}
~Stack_Push_Pop()
{
m_ds.get()->pop_stack(m_ds.get().stack_holder());
} }
~Stack_Push_Pop() { m_ds->pop_stack(m_ds.stack_holder()); }
private: private:
const chaiscript::detail::Dispatch_State &m_ds; std::reference_wrapper<const chaiscript::detail::Dispatch_State> m_ds;
}; };
} // namespace detail }
} // namespace eval }
} // namespace chaiscript }
#endif /* _CHAISCRIPT_COMMON_HPP */ #endif /* _CHAISCRIPT_COMMON_HPP */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,443 +0,0 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_OPTIMIZER_HPP_
#define CHAISCRIPT_OPTIMIZER_HPP_
#include "chaiscript_eval.hpp"
namespace chaiscript {
namespace optimizer {
template<typename... T>
struct Optimizer : T... {
Optimizer() = default;
explicit Optimizer(T... t)
: T(std::move(t))... {
}
template<typename Tracer>
auto optimize(eval::AST_Node_Impl_Ptr<Tracer> p) {
((p = static_cast<T &>(*this).optimize(std::move(p))), ...);
return p;
}
};
template<typename T>
eval::AST_Node_Impl<T> &child_at(eval::AST_Node_Impl<T> &node, const size_t offset) noexcept {
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];
}
}
template<typename T>
const eval::AST_Node_Impl<T> &child_at(const eval::AST_Node_Impl<T> &node, const size_t offset) noexcept {
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];
}
/*
if (node->identifier == AST_Node_Type::Compiled) {
return dynamic_cast<const eval::Compiled_AST_Node<T>&>(*node).m_original_node->children[offset];
} else {
return node->children[offset];
}
*/
}
template<typename T>
auto child_count(const eval::AST_Node_Impl<T> &node) noexcept {
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();
}
}
template<typename T, typename 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_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(eval::AST_Node_Impl_Ptr<T> p) {
if ((p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda) && !p->children.empty()) {
auto &last_child = p->children.back();
if (last_child->identifier == AST_Node_Type::Block) {
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() = std::move(block_last_child->children[0]);
}
}
}
}
return p;
}
};
template<typename T>
bool contains_var_decl_in_scope(const eval::AST_Node_Impl<T> &node) noexcept {
if (node.identifier == AST_Node_Type::Var_Decl || node.identifier == AST_Node_Type::Assign_Decl
|| node.identifier == AST_Node_Type::Const_Var_Decl || node.identifier == AST_Node_Type::Const_Assign_Decl
|| node.identifier == AST_Node_Type::Reference) {
return true;
}
const auto num = child_count(node);
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
&& child.identifier != AST_Node_Type::Ranged_For && contains_var_decl_in_scope(child)) {
return true;
}
}
return false;
}
struct Block {
template<typename T>
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if (node->identifier == AST_Node_Type::Block) {
if (!contains_var_decl_in_scope(*node)) {
if (node->children.size() == 1) {
return std::move(node->children[0]);
} else {
return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Scopeless_Block_AST_Node<T>>(node->text,
node->location,
std::move(node->children));
}
}
}
return node;
}
};
struct Dead_Code {
template<typename T>
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if (node->identifier == AST_Node_Type::Block) {
std::vector<size_t> keepers;
const auto num_children = node->children.size();
keepers.reserve(num_children);
for (size_t i = 0; i < num_children; ++i) {
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);
}
}
if (keepers.size() == num_children) {
return node;
} else {
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;
}
}
};
struct Unused_Return {
template<typename T>
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].get();
if (child->identifier == AST_Node_Type::Fun_Call) {
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) {
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_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));
}
}
}
}
return node;
}
};
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));
} else if ((node->identifier == AST_Node_Type::Equation) && node->text == "=" && node->children.size() == 2
&& node->children[0]->identifier == AST_Node_Type::Const_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::Const_Assign_Decl_AST_Node<T>>(node->text,
node->location,
std::move(new_children));
}
return node;
}
};
struct If {
template<typename T>
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 = 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 std::move(node->children[1]);
} else if (node->children.size() == 3) {
return std::move(node->children[2]);
}
}
}
return node;
}
};
struct Partial_Fold {
template<typename T>
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
// Fold right side
if (node->identifier == AST_Node_Type::Binary && node->children.size() == 2
&& node->children[0]->identifier != AST_Node_Type::Constant && node->children[1]->identifier == AST_Node_Type::Constant) {
try {
const auto &oper = node->text;
const auto parsed = Operators::to_operator(oper);
if (parsed != Operators::Opers::invalid) {
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_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 &) {
// failure to fold, that's OK
}
}
return node;
}
};
struct Constant_Fold {
template<typename T>
auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
if (node->identifier == AST_Node_Type::Prefix && node->children.size() == 1 && node->children[0]->identifier == AST_Node_Type::Constant) {
try {
const auto &oper = node->text;
const auto parsed = Operators::to_operator(oper, true);
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_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_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
}
} else if ((node->identifier == AST_Node_Type::Logical_And || node->identifier == AST_Node_Type::Logical_Or)
&& node->children.size() == 2 && node->children[0]->identifier == AST_Node_Type::Constant
&& node->children[1]->identifier == AST_Node_Type::Constant) {
try {
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] {
if (id == AST_Node_Type::Logical_And) {
return Boxed_Value(lhs_val && rhs_val);
} else {
return Boxed_Value(lhs_val || rhs_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
}
} else if (node->identifier == AST_Node_Type::Binary && node->children.size() == 2
&& node->children[0]->identifier == AST_Node_Type::Constant && node->children[1]->identifier == AST_Node_Type::Constant) {
try {
const auto &oper = node->text;
const auto parsed = Operators::to_operator(oper);
if (parsed != Operators::Opers::invalid) {
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_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
}
} else if (node->identifier == AST_Node_Type::Fun_Call && node->children.size() == 2
&& node->children[0]->identifier == AST_Node_Type::Id && node->children[1]->identifier == AST_Node_Type::Arg_List
&& node->children[1]->children.size() == 1 && node->children[1]->children[0]->identifier == AST_Node_Type::Constant) {
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_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match),
node->location,
const_var(val));
};
if (fun_name == "double") {
return make_constant(Boxed_Number(arg).get_as<double>());
} else if (fun_name == "int") {
return make_constant(Boxed_Number(arg).get_as<int>());
} else if (fun_name == "float") {
return make_constant(Boxed_Number(arg).get_as<float>());
} else if (fun_name == "long") {
return make_constant(Boxed_Number(arg).get_as<long>());
} else if (fun_name == "size_t") {
return make_constant(Boxed_Number(arg).get_as<size_t>());
}
}
}
return node;
}
};
struct For_Loop {
template<typename T>
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);
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::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(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(eq_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>())) {
const auto start_int = boxed_cast<int>(begin);
const auto end_int = boxed_cast<int>(end);
// 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);
Boxed_Value bv_i(start_int);
auto &i = *static_cast<int *>(bv_i.get_ptr());
t_ss.add_object(id, bv_i);
try {
for (; i < end_int; ++i) {
try {
// Body of Loop
children[0]->eval(t_ss);
} catch (eval::detail::Continue_Loop &) {
// we got a continue exception, which means all of the remaining
// loop implementation is skipped and we just need to continue to
// the next iteration step
}
}
} catch (eval::detail::Break_Loop &) {
// loop broken
}
return void_var();
});
} else {
return for_node;
}
} else {
return for_node;
}
}
};
using Optimizer_Default = Optimizer<optimizer::Partial_Fold,
optimizer::Unused_Return,
optimizer::Constant_Fold,
optimizer::If,
optimizer::Return,
optimizer::Dead_Code,
optimizer::Block,
optimizer::For_Loop,
optimizer::Assign_Decl>;
} // namespace optimizer
} // namespace chaiscript
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +0,0 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_POSIX_HPP_
#define CHAISCRIPT_POSIX_HPP_
namespace chaiscript::detail {
struct Loadable_Module {
struct DLModule {
explicit DLModule(const std::string &t_filename)
: m_data(dlopen(t_filename.c_str(), RTLD_NOW)) {
if (m_data == nullptr) {
throw chaiscript::exception::load_module_error(dlerror());
}
}
DLModule(DLModule &&) = default;
DLModule &operator=(DLModule &&) = default;
DLModule(const DLModule &) = delete;
DLModule &operator=(const DLModule &) = delete;
~DLModule() { dlclose(m_data); }
void *m_data;
};
template<typename T>
struct DLSym {
DLSym(DLModule &t_mod, const std::string &t_symbol)
: m_symbol(reinterpret_cast<T>(dlsym(t_mod.m_data, t_symbol.c_str()))) {
if (!m_symbol) {
throw chaiscript::exception::load_module_error(dlerror());
}
}
T m_symbol;
};
Loadable_Module(const std::string &t_module_name, const std::string &t_filename)
: m_dlmodule(t_filename)
, m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name)
, m_moduleptr(m_func.m_symbol()) {
}
DLModule m_dlmodule;
DLSym<Create_Module_Func> m_func;
ModulePtr m_moduleptr;
};
} // namespace chaiscript::detail
#endif

View File

@ -0,0 +1,539 @@
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// and Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
#ifndef CHAISCRIPT_PRELUDE_HPP_
#define CHAISCRIPT_PRELUDE_HPP_
namespace chaiscript {
struct ChaiScript_Prelude {
static std::string chaiscript_prelude() { return R""(
def lt(l, r) {
if (call_exists(`<`, l, r)) {
l < r
} else {
type_name(l) < type_name(r)
}
}
def gt(l, r) {
if (call_exists(`>`, l, r)) {
l > r
} else {
type_name(l) > type_name(r)
}
}
def eq(l, r) {
if (call_exists(`==`, l, r)) {
l == r
} else {
false
}
}
def new(x) {
eval(type_name(x))();
}
def clone(double x) {
double(x).clone_var_attrs(x)
}
def clone(string x) {
string(x).clone_var_attrs(x)
}
def clone(vector x) {
vector(x).clone_var_attrs(x)
}
def clone(int x) {
int(x).clone_var_attrs(x)
}
def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x)
{
eval(type_name(x))(x).clone_var_attrs(x);
}
# to_string for Pair()
def to_string(x) : call_exists(first, x) && call_exists(second, x) {
"<" + x.first.to_string() + ", " + x.second.to_string() + ">";
}
# to_string for containers
def to_string(x) : call_exists(range, x) && !x.is_type("string"){
"[" + x.join(", ") + "]";
}
# Prints to console with no carriage return
def puts(x) {
print_string(x.to_string());
}
# Prints to console with carriage return
def print(x) {
println_string(x.to_string());
}
# Returns the maximum value of two numbers
def max(a, b) {
if (a>b) {
a
} else {
b
}
}
# Returns the minimum value of two numbers
def min(a, b)
{
if (a<b)
{
a
} else {
b
}
}
# Returns true if the value is odd
def odd(x) {
if (x % 2 == 1)
{
true
} else {
false
}
}
# Returns true if the value is even
def even(x)
{
if (x % 2 == 0)
{
true
} else {
false
}
}
# Inserts the third value at the position of the second value into the container of the first
# while making a clone.
def insert_at(container, pos, x)
{
container.insert_ref_at(pos, clone(x));
}
# Returns the reverse of the given container
def reverse(container) {
auto retval := new(container);
auto r := range(container);
while (!r.empty()) {
retval.push_back(r.back());
r.pop_back();
}
retval;
}
def range(r) : call_exists(range_internal, r)
{
var ri := range_internal(r);
ri.get_var_attr("internal_obj") := r;
ri;
}
# Return a range from a range
def range(r) : call_exists(empty, r) && call_exists(pop_front, r) && call_exists(pop_back, r) && call_exists(back, r) && call_exists(front, r)
{
clone(r);
}
# The retro attribute that contains the underlying range
attr retro::m_range;
# Creates a retro from a retro by returning the original range
def retro(r) : call_exists(get_type_name, r) && get_type_name(r) == "retro"
{
clone(r.m_range)
}
# Creates a retro range from a range
def retro::retro(r) : call_exists(empty, r) && call_exists(pop_front, r) && call_exists(pop_back, r) && call_exists(back, r) && call_exists(front, r)
{
this.m_range = r;
}
# Returns the first value of a retro
def retro::front()
{
back(this.m_range)
}
# Returns the last value of a retro
def retro::back()
{
front(this.m_range)
}
# Moves the back iterator of a retro towards the front by one
def retro::pop_back()
{
pop_front(this.m_range)
}
# Moves the front iterator of a retro towards the back by one
def retro::pop_front()
{
pop_back(this.m_range)
}
# returns true if the retro is out of elements
def retro::empty()
{
empty(this.m_range);
}
# Performs the second value function over the container first value
def for_each(container, func) : call_exists(range, container) {
var t_range := range(container);
while (!t_range.empty()) {
func(t_range.front());
t_range.pop_front();
}
}
def back_inserter(container) {
bind(push_back, container, _);
}
def contains(container, item, compare_func) : call_exists(range, container) {
auto t_range := range(container);
while (!t_range.empty()) {
if ( compare_func(t_range.front(), item) ) {
return true;
}
t_range.pop_front();
}
false;
}
def contains(container, item) {
contains(container, item, eq)
}
def map(container, func, inserter) : call_exists(range, container) {
auto range := range(container);
while (!range.empty()) {
inserter(func(range.front()));
range.pop_front();
}
}
# Performs the second value function over the container first value. Creates a new container with the results
def map(container, func) {
auto retval := new(container);
map(container, func, back_inserter(retval));
retval;
}
# Performs the second value function over the container first value. Starts with initial and continues with each element.
def foldl(container, func, initial) : call_exists(range, container){
auto retval = initial;
auto range := range(container);
while (!range.empty()) {
retval = (func(range.front(), retval));
range.pop_front();
}
retval;
}
# Returns the sum of the elements of the given value
def sum(container) {
foldl(container, `+`, 0.0)
}
# Returns the product of the elements of the given value
def product(container) {
foldl(container, `*`, 1.0)
}
# Returns a new container with the elements of the first value concatenated with the elements of the second value
def concat(x, y) : call_exists(clone, x) {
auto retval = x;
auto inserter := back_inserter(retval);
auto range := range(y);
while (!range.empty()) {
inserter(range.front());
range.pop_front();
}
retval;
}
def take(container, num, inserter) : call_exists(range, container) {
auto r := range(container);
auto i = num;
while ((i > 0) && (!r.empty())) {
inserter(r.front());
r.pop_front();
--i;
}
}
# Returns a new container with the given number of elements taken from the container
def take(container, num) {
auto retval := new(container);
take(container, num, back_inserter(retval));
retval;
}
def take_while(container, f, inserter) : call_exists(range, container) {
auto r := range(container);
while ((!r.empty()) && f(r.front())) {
inserter(r.front());
r.pop_front();
}
}
# Returns a new container with the given elements match the second value function
def take_while(container, f) {
auto retval := new(container);
take_while(container, f, back_inserter(retval));
retval;
}
def drop(container, num, inserter) : call_exists(range, container) {
auto r := range(container);
auto i = num;
while ((i > 0) && (!r.empty())) {
r.pop_front();
--i;
}
while (!r.empty()) {
inserter(r.front());
r.pop_front();
}
}
# Returns a new container with the given number of elements dropped from the given container
def drop(container, num) {
auto retval := new(container);
drop(container, num, back_inserter(retval));
retval;
}
def drop_while(container, f, inserter) : call_exists(range, container) {
auto r := range(container);
while ((!r.empty())&& f(r.front())) {
r.pop_front();
}
while (!r.empty()) {
inserter(r.front());
r.pop_front();
}
}
# Returns a new container with the given elements dropped that match the second value function
def drop_while(container, f) {
auto retval := new(container);
drop_while(container, f, back_inserter(retval));
retval;
}
# Applies the second value function to the container. Starts with the first two elements. Expects at least 2 elements.
def reduce(container, func) : container.size() >= 2 && call_exists(range, container) {
auto r := range(container);
auto retval = r.front();
r.pop_front();
retval = func(retval, r.front());
r.pop_front();
while (!r.empty()) {
retval = func(retval, r.front());
r.pop_front();
}
retval;
}
# Returns a string of the elements in container delimited by the second value string
def join(container, delim) {
auto retval = "";
auto range := range(container);
if (!range.empty()) {
retval += to_string(range.front());
range.pop_front();
while (!range.empty()) {
retval += delim;
retval += to_string(range.front());
range.pop_front();
}
}
retval;
}
def filter(container, f, inserter) : call_exists(range, container) {
auto r := range(container);
while (!r.empty()) {
if (f(r.front())) {
inserter(r.front());
}
r.pop_front();
}
}
# Returns a new Vector which match the second value function
def filter(container, f) {
auto retval := new(container);
filter(container, f, back_inserter(retval));
retval;
}
def generate_range(x, y, inserter) {
auto i = x;
while (i <= y) {
inserter(i);
++i;
}
}
# Returns a new Vector which represents the range from the first value to the second value
def generate_range(x, y) {
auto retval := Vector();
generate_range(x,y,back_inserter(retval));
retval;
}
# Returns a new Vector with the first value to the second value as its elements
def collate(x, y) {
return [x, y];
}
def zip_with(f, x, y, inserter) : call_exists(range, x) && call_exists(range, y) {
auto r_x := range(x);
auto r_y := range(y);
while (!r_x.empty() && !r_y.empty()) {
inserter(f(r_x.front(), r_y.front()));
r_x.pop_front();
r_y.pop_front();
}
}
# Returns a new Vector which joins matching elements of the second and third value with the first value function
def zip_with(f, x, y) {
auto retval := Vector();
zip_with(f,x,y,back_inserter(retval));
retval;
}
# Returns a new Vector which joins matching elements of the first and second
def zip(x, y) {
zip_with(collate, x, y);
}
# Returns the position of the second value string in the first value string
def string::find(string substr) {
find(this, substr, size_t(0));
}
# Returns the position of last match of the second value string in the first value string
def string::rfind(string substr) {
rfind(this, substr, size_t(-1));
}
# Returns the position of the first match of elements in the second value string in the first value string
def string::find_first_of(string list) {
find_first_of(this, list, size_t(0));
}
# Returns the position of the last match of elements in the second value string in the first value string
def string::find_last_of(string list) {
find_last_of(this, list, size_t(-1));
}
# Returns the position of the first non-matching element in the second value string in the first value string
def string::find_first_not_of(string list) {
find_first_not_of(this, list, size_t(0));
}
# Returns the position of the last non-matching element in the second value string in the first value string
def string::find_last_not_of(string list) {
find_last_not_of(this, list, size_t(-1));
}
def string::ltrim() {
drop_while(this, fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'});
}
def string::rtrim() {
reverse(drop_while(reverse(this), fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'}));
}
def string::trim() {
ltrim(rtrim(this));
}
def find(container, value, Function compare_func) : call_exists(range, container) {
auto range := range(container);
while (!range.empty()) {
if (compare_func(range.front(), value)) {
return range;
} else {
range.pop_front();
}
}
range;
}
def find(container, value) {
find(container, value, eq)
}
)"";
}
};
}
#endif /* CHAISCRIPT_PRELUDE_HPP_ */

Some files were not shown because too many files have changed in this diff Show More