diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..f60382d5 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,4 @@ +--- +SystemHeaders: false +HeaderFilterRegex: '^(?!.*(boost|_deps)).*$' +FormatStyle: none diff --git a/.github/workflows/adobe_source_libraries.yml b/.github/workflows/adobe_source_libraries.yml index ebb99466..9a120920 100644 --- a/.github/workflows/adobe_source_libraries.yml +++ b/.github/workflows/adobe_source_libraries.yml @@ -47,7 +47,6 @@ jobs: rm '/usr/local/bin/python3.12' rm '/usr/local/bin/python3-config' rm '/usr/local/bin/python3.12-config' - brew install boost brew install ninja shell: bash @@ -55,27 +54,14 @@ jobs: if: ${{ startsWith(matrix.config.os, 'ubuntu') }} run: | sudo apt-get install -y ninja-build - sudo apt-get install -y libboost-all-dev shell: bash - name: Install dependencies (Windows) if: ${{ startsWith(matrix.config.os, 'windows') }} run: | choco install --yes ninja - vcpkg install --triplet=x64-windows shell: cmd - # Keep this around for debugging. If something in the `vcpkg` setup chokes, - # this step can be used to extract `issue_body.md` from the build system - # and its contents pasted into an issue filed against `vcpkg`. - # (for example, https://github.com/microsoft/vcpkg/issues/39518) - # - name: Archive dependency log files (Windows) - # if: always() - # uses: actions/upload-artifact@v4 - # with: - # name: win-dependency-log-body - # path: D:/a/adobe_source_libraries/adobe_source_libraries/vcpkg_installed/vcpkg/issue_body.md - - name: Set enviroment variables (Linux+GCC) if: ${{ matrix.config.compiler == 'gcc' }} shell: bash @@ -117,8 +103,7 @@ jobs: run: | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 mkdir ..\build - cmake -S. -B../build -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=${{matrix.config.cxxstd}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DBOOST_TEST_DYN_LINK=TRUE - + cmake -S. -B../build -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=${{matrix.config.cxxstd}} - name: Build (Unix) if: ${{ startsWith(matrix.config.os, 'ubuntu') || startsWith(matrix.config.os, 'macos') }} shell: bash diff --git a/.gitignore b/.gitignore index ae52965e..8828af7f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ gh-pages/ documentation/doxyfile.bak build_asl/ .vs/ -.vscode/ +.vscode/settings.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..0278ab88 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,162 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "(Windows) Launch", + "type": "cppvsdbg", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "PATH", + "value": "${env:PATH};${command:cmake.launchTargetDirectory}" + } + ], + "console": "integratedTerminal", + "windows": { + "type": "cppvsdbg" + } + }, + { + "name": "(lldb) Launch", + "type": "lldb", + "request": "launch", + // Resolved by CMake Tools: + "program": "${command:cmake.launchTargetPath}", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + // add the directory where our target was built to the PATHs + // it gets resolved by CMake Tools: + "name": "PATH", + "value": "$PATH:${command:cmake.launchTargetDirectory}" + }, + { + "name": "OTHER_VALUE", + "value": "Something something" + } + ], + "externalConsole": true, + "MIMode": "lldb", + "setupCommands": [ + { + "description": "Enable pretty-printing for lldb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "console": "integratedTerminal" + }, + { + "name": "(Windows) property_model_eval - minmax", + "type": "cppvsdbg", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "args": [ + "${workspaceFolder}/test/property_model_eval/minmax.pme" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "PATH", + "value": "${env:PATH};${command:cmake.launchTargetDirectory}" + } + ], + "console": "integratedTerminal", + "windows": { + "type": "cppvsdbg" + } + }, + { + "name": "(Windows) property_model_eval - default", + "type": "cppvsdbg", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "args": [ + "${workspaceFolder}/test/property_model_eval/default.pme" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "PATH", + "value": "${env:PATH};${command:cmake.launchTargetDirectory}" + } + ], + "console": "integratedTerminal", + "windows": { + "type": "cppvsdbg" + } + }, + { + "name": "(Windows) property_model_eval - pm_tut", + "type": "cppvsdbg", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "args": [ + "${workspaceFolder}/test/property_model_eval/pm_tut.pme" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "PATH", + "value": "${env:PATH};${command:cmake.launchTargetDirectory}" + } + ], + "console": "integratedTerminal", + "windows": { + "type": "cppvsdbg" + } + }, + { + "name": "property_model_eval - default", + "type": "lldb", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "args": [ + "${workspaceFolder}/test/property_model_eval/default.pme" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + "name": "PATH", + "value": "$PATH:${command:cmake.launchTargetDirectory}" + } + ], + "externalConsole": true, + "MIMode": "lldb", + "setupCommands": [ + { + "description": "Enable pretty-printing for lldb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "console": "integratedTerminal" + } + ], + "compounds": [ + { + "name": "Launch (Platform Specific)", + "configurations": [ + "(Windows) Launch", + "(lldb) Launch" + ] + }, + { + "name": "property_model_eval (Platform Specific)", + "configurations": [ + "(Windows) property_model_eval - minmax", + "property_model_eval - minmax" + ] + } + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f1cf0fa..96d2b5d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,13 @@ cmake_minimum_required(VERSION 3.29) include(FindGit) include(CMakeParseArguments) +include(./cmake/CPM.cmake) enable_testing() +if(WIN32) + set(CMAKE_OBJECT_PATH_MAX 500) +endif() + project(adobe_source_libraries CXX) set(CMAKE_CXX_EXTENSIONS OFF) @@ -12,67 +17,32 @@ endif(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") -set(VCPKG_FEATURE_FLAGS "versions") project(adobe-source-libraries CXX) -# add_definitions ("-Wall") -# add_definitions ("-Werror") - -# There is a big choice this file makes, namely how to include Boost. Build environments -# vary and we're trying to support all of them. - -set(root_path ${CMAKE_SOURCE_DIR}/..) - -function(setup_dep) - set(options IS_CMAKE) - set(oneValueArgs URL BRANCH TAG NAME) - set(multiValueArgs SUBMODULES) - cmake_parse_arguments(setup_dep "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - if ("${setup_dep_NAME}" STREQUAL "") - get_filename_component(name ${setup_dep_URL} NAME_WE) - else() - set(name ${setup_dep_NAME}) - endif() - - set(dep_path ${root_path}/${name}) - if(NOT IS_DIRECTORY ${dep_path}) - message("ASL_INFO: Setting up dep " ${name} " into " ${dep_path}) - execute_process(COMMAND ${GIT_EXECUTABLE} clone ${setup_dep_URL} ${name} WORKING_DIRECTORY ${root_path}) +CPMAddPackage( + NAME Boost + VERSION 1.87.0 + URL https://github.com/boostorg/boost/releases/download/boost-1.87.0/boost-1.87.0-cmake.7z + URL_HASH SHA256=61d61a3f33283bab8f3736745a4e47c37141f07e413986cd05d1f942c69d529a + OPTIONS + "BOOST_ENABLE_CMAKE ON" + "CMAKE_CXX_CLANG_TIDY ;" +) - if("${setup_dep_BRANCH}" STREQUAL "") - message(FATAL_ERROR "ASL_INFO: No branch given for dep " ${name}) - endif() - - execute_process(COMMAND ${GIT_EXECUTABLE} checkout ${setup_dep_BRANCH} WORKING_DIRECTORY ${dep_path}) - - foreach(submodule ${setup_dep_SUBMODULES}) - message("ASL_INFO: Setting up submodule " ${submodule} " for " ${name}) - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init ${submodule} WORKING_DIRECTORY ${dep_path}) - endforeach(submodule) - else() - message("ASL_INFO: Found dep " ${name} " into " ${dep_path}) - endif() - - if (setup_dep_IS_CMAKE) - add_subdirectory(${dep_path} ${CMAKE_BINARY_DIR}/imported/${name}) - endif() - -endfunction() function(target_link_boost target) target_link_libraries(${target} PUBLIC Boost::system) + target_link_libraries(${target} PUBLIC Boost::signals2) + target_link_libraries(${target} PUBLIC Boost::range) + target_link_libraries(${target} PUBLIC Boost::multiprecision) + target_link_libraries(${target} PUBLIC Boost::date_time) endfunction(target_link_boost) function(target_link_boost_test target) - target_compile_definitions(${target} PRIVATE BOOST_TEST_DYN_LINK) target_link_libraries(${target} PRIVATE Boost::unit_test_framework) + target_link_libraries(${target} PRIVATE Boost::program_options) endfunction(target_link_boost_test) -message("ASL_INFO: Using system boost.") -set(ASL_BOOST_COMPONENTS system unit_test_framework program_options) -find_package(Boost COMPONENTS ${ASL_BOOST_COMPONENTS} REQUIRED) - add_subdirectory(source) add_subdirectory(test) diff --git a/CMakePresets.json b/CMakePresets.json index e3f2e4f4..518b2354 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -6,18 +6,16 @@ "displayName": "debug-windows", "description": "Sets Ninja generator, build and install directory", "generator": "Ninja", - "binaryDir": "${sourceDir}/build/${presetName}", + "binaryDir": "${sourceDir}/../build/asl-${presetName}", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_BUILD_TYPE": "DEBUG", "CMAKE_CXX_STANDARD": "20", - "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", - "VCPKG_TARGET_TRIPLET": "x64-windows" + "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" }, "architecture": { "value": "x64", "strategy": "external" }, - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ @@ -26,23 +24,21 @@ } } }, - { - "name": "debug-windows-cpp17", - "displayName": "debug-windows-cpp17", + { + "name": "wd17", + "displayName": "windows-debug-C++17", "description": "Sets Ninja generator, build and install directory", "generator": "Ninja", - "binaryDir": "${sourceDir}/build/${presetName}", + "binaryDir": "${sourceDir}/../build/asl-${presetName}", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_BUILD_TYPE": "DEBUG", "CMAKE_CXX_STANDARD": "17", - "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", - "VCPKG_TARGET_TRIPLET": "x64-windows" + "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" }, "architecture": { "value": "x64", "strategy": "external" }, - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ @@ -56,9 +52,9 @@ "displayName": "macos-debug-C++20", "description": "Sets Ninja generator, build and install directory", "generator": "Ninja", - "binaryDir": "${sourceDir}/build/${presetName}", + "binaryDir": "${sourceDir}/../build/asl-${presetName}", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_BUILD_TYPE": "DEBUG", "CMAKE_CXX_STANDARD": "20", "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" }, @@ -75,17 +71,66 @@ "displayName": "macos-debug-C++17", "description": "Sets Ninja generator, build and install directory", "generator": "Ninja", - "binaryDir": "${sourceDir}/build/${presetName}", + "binaryDir": "${sourceDir}/../build/asl-${presetName}", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "DEBUG", + "CMAKE_CXX_STANDARD": "17" + } + }, + { + "name": "clang-tidy-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/../build/asl-${presetName}", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", "CMAKE_CXX_STANDARD": "17", - "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" + "CMAKE_BUILD_TYPE": "DEBUG", + "CMAKE_CXX_CLANG_TIDY": "clang-tidy" + } + }, + { + "name": "windows-clang-tidy-nofix", + "inherits": "clang-tidy-base", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "cacheVariables": { + "CMAKE_CXX_CLANG_TIDY": "clang-tidy;--extra-arg=/EHsc;--extra-arg=/DNOMINMAX" }, "vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { - "hostOS": [ - "macOS" - ] + "enableMicrosoftCodeAnalysis": true + } + } + }, + { + "name": "clang-tidy-fix", + "hidden": false, + "generator": "Ninja", + "binaryDir": "${sourceDir}/../build/asl-${presetName}", + "cacheVariables": { + "CMAKE_CXX_STANDARD": "17", + "CMAKE_BUILD_TYPE": "DEBUG", + "CMAKE_CXX_CLANG_TIDY": + "clang-tidy;--fix;--allow-no-checks;--extra-arg=/EHsc;--extra-arg=/DNOMINMAX" + } + }, + { + "name": "clang-tidy-nofix", + "hidden": false, + "generator": "Ninja", + "binaryDir": "${sourceDir}/../build/asl-${presetName}", + "cacheVariables": { + "CMAKE_CXX_STANDARD": "17", + "CMAKE_BUILD_TYPE": "DEBUG", + "CMAKE_CXX_CLANG_TIDY": "clang-tidy;--extra-arg=/EHsc;--extra-arg=/DNOMINMAX" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "enableMicrosoftCodeAnalysis": true, + "enableClangTidyCodeAnalysis": true } } } diff --git a/adobe/adam.hpp b/adobe/adam.hpp index d183371e..10e27413 100644 --- a/adobe/adam.hpp +++ b/adobe/adam.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -122,15 +123,15 @@ class sheet_t : boost::noncopyable { void touch(const name_t* first, const name_t* last); // range of input cells. /*! - The get function is intended to be connected to the VM variable lookup by the client. During - expression evaluation, triggered by initialization, reinitialize, or update the VM can call - get() to return the value of a variable. + Returns the value of the named cell, evaluating the cell if necessary. + If no cell with the given name is not found, an empty optional is returned. - \param cell name of cell to calculate/get the value. + The cell_value function is intended to be connected to the VM variable lookup by the client. + During expression evaluation, triggered by initialization, reinitialize, or update the VM can + call */ - // REVISIT (sparent) : get() is likely a bad name giving the pairing with set. - any_regular_t get(name_t cell); + auto cell_value(name_t) -> std::optional; /*! Returns the most recent ouput value of the cell cashed from the last call to update(). diff --git a/adobe/algorithm/binary_search.hpp b/adobe/algorithm/binary_search.hpp index 2267d28b..52f86589 100644 --- a/adobe/algorithm/binary_search.hpp +++ b/adobe/algorithm/binary_search.hpp @@ -118,7 +118,7 @@ template // T models Regular inline I binary_search(I f, I l, const T& x) { - return binary_search(f, l, x, less()); + return adobe::binary_search(f, l, x, less()); } template diff --git a/adobe/algorithm/sorted.hpp b/adobe/algorithm/sorted.hpp index 2974d3d0..37074e72 100644 --- a/adobe/algorithm/sorted.hpp +++ b/adobe/algorithm/sorted.hpp @@ -127,7 +127,7 @@ inline bool is_sorted(const I& r, C c) { */ template // I models ForwardRange inline bool is_sorted(const I& r) { - return is_sorted(boost::begin(r), boost::end(r), less()); + return std::is_sorted(boost::begin(r), boost::end(r), less()); } /**************************************************************************************************/ diff --git a/adobe/closed_hash.hpp b/adobe/closed_hash.hpp index 7674da38..9de3de42 100644 --- a/adobe/closed_hash.hpp +++ b/adobe/closed_hash.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -301,6 +302,11 @@ class closed_hash_set insert(f, l); } + closed_hash_set(std::initializer_list init) { + header() = 0; + insert(init.begin(), init.end()); + } + template // I models InputIterator closed_hash_set(I f, I l, size_type n, const hasher& hf = hasher(), const key_equal& eq = key_equal(), const key_transform& kf = key_transform(), @@ -621,8 +627,9 @@ pair. template class closed_hash_map : public closed_hash_set, get_element<0, std::pair>, Hash, Pred, A> { - typedef closed_hash_set, get_element<0, std::pair>, Hash, Pred, A> - set_type; + + using set_type = + closed_hash_set, get_element<0, std::pair>, Hash, Pred, A>; public: typedef T mapped_type; @@ -632,6 +639,8 @@ class closed_hash_map template // I models InputIterator closed_hash_map(I f, I l) : set_type(f, l) {} + closed_hash_map(std::initializer_list init) : set_type(init) {} + closed_hash_map(const closed_hash_map& x) : set_type(x) {} closed_hash_map(closed_hash_map&& x) noexcept : set_type(std::move(x)) {} closed_hash_map& operator=(closed_hash_map x) { diff --git a/adobe/conversion.hpp b/adobe/conversion.hpp index 8e805760..c8abe86e 100644 --- a/adobe/conversion.hpp +++ b/adobe/conversion.hpp @@ -25,9 +25,12 @@ struct promote { template <> struct promote { - typedef std::string type; + using type = std::string; }; +template +using promote_t = typename promote::type; + /**************************************************************************************************/ template diff --git a/adobe/enum_ops.hpp b/adobe/enum_ops.hpp index 773c337f..efcd7243 100644 --- a/adobe/enum_ops.hpp +++ b/adobe/enum_ops.hpp @@ -95,28 +95,28 @@ template constexpr auto operator&(const T lhs, const T rhs) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(static_cast(lhs) & static_cast(rhs)); + return static_cast(static_cast(lhs) & static_cast(rhs)); // NOLINT } template constexpr auto operator~(const T a) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(~static_cast(a)); + return static_cast(~static_cast(a)); // NOLINT } template constexpr auto operator|(const T lhs, const T rhs) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); + return static_cast(static_cast(lhs) | static_cast(rhs)); // NOLINT } template constexpr auto operator^(const T lhs, const T rhs) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(static_cast(lhs) ^ static_cast(rhs)); + return static_cast(static_cast(lhs) ^ static_cast(rhs)); // NOLINT } template @@ -144,49 +144,49 @@ template constexpr auto operator+(const T a) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(+static_cast(a)); + return static_cast(+static_cast(a)); // NOLINT } template constexpr auto operator-(const T a) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(-static_cast(a)); + return static_cast(-static_cast(a)); // NOLINT } template constexpr auto operator+(const T lhs, const T rhs) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(static_cast(lhs) + static_cast(rhs)); + return static_cast(static_cast(lhs) + static_cast(rhs)); // NOLINT } template constexpr auto operator-(const T lhs, const T rhs) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(static_cast(lhs) - static_cast(rhs)); + return static_cast(static_cast(lhs) - static_cast(rhs)); // NOLINT } template constexpr auto operator*(const T lhs, const T rhs) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(static_cast(lhs) * static_cast(rhs)); + return static_cast(static_cast(lhs) * static_cast(rhs)); // NOLINT } template constexpr auto operator/(const T lhs, const T rhs) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(static_cast(lhs) / static_cast(rhs)); + return static_cast(static_cast(lhs) / static_cast(rhs)); // NOLINT } template constexpr auto operator%(const T lhs, const T rhs) -> std::enable_if_t, T> { using underlying = std::underlying_type_t; - return static_cast(static_cast(lhs) % static_cast(rhs)); + return static_cast(static_cast(lhs) % static_cast(rhs)); // NOLINT } template @@ -222,28 +222,28 @@ constexpr auto operator%=(T& lhs, const T rhs) template constexpr auto operator++(T& lhs) -> std::enable_if_t, T> { - return lhs += static_cast(1); + return lhs += static_cast(1); // NOLINT } template constexpr auto operator++(T& lhs, int) -> std::enable_if_t, T> { T result = lhs; - lhs += static_cast(1); + lhs += static_cast(1); // NOLINT return result; } template constexpr auto operator--(T& lhs) -> std::enable_if_t, T> { - return lhs -= static_cast(1); + return lhs -= static_cast(1); // NOLINT } template constexpr auto operator--(T& lhs, int) -> std::enable_if_t, T> { T result = lhs; - lhs -= static_cast(1); + lhs -= static_cast(1); // NOLINT return result; } diff --git a/adobe/fnv.hpp b/adobe/fnv.hpp index 3924274a..90d7c674 100644 --- a/adobe/fnv.hpp +++ b/adobe/fnv.hpp @@ -290,7 +290,7 @@ inline fnvtype fnv1a(Iterator first, Iterator last) { while (first != last) result = (result ^ static_cast(*first++)) * fnv_traits::prime(); - return detail::bitmask::size(), Bits>::template mask(result); + return detail::bitmask::size(), Bits>::template mask(result); } /**************************************************************************************************/ diff --git a/adobe/implementation/expression_parser.hpp b/adobe/implementation/expression_parser.hpp index 01686b55..b0d3ee53 100644 --- a/adobe/implementation/expression_parser.hpp +++ b/adobe/implementation/expression_parser.hpp @@ -46,15 +46,14 @@ multiplicative_expression = unary_expression { ("*" | "/" | "%") unary_express unary_expression = postfix_expression | (unary_operator unary_expression). unary_operator = "+" | "-" | "!" | "~". -postfix_expression = primary_expression { ("[" expression "]") | ("." identifier) }. +postfix_expression = primary_expression { ("[" expression "]") | ("." identifier) + | "(" [argument_expression_list] ")"}. primary_expression = name | number | boolean | string | "empty" | array | dictionary - | variable_or_fuction | ( "(" expression ")" ). - -variable_or_function = identifier ["(" [argument_expression_list] ")"]. + | identifier | ( "(" expression ")" ). array = "[" [argument_list] "]". -dictionary = "{" named_argument_list "}". +dictionary = "{" [named_argument_list] "}". argument_expression_list = named_argument_list | argument_list. @@ -105,7 +104,8 @@ class expression_parser : public boost::noncopyable { // equality_expression = relational_expression { ("==" | "!=") relational_expression }. bool is_equality_expression(array_t&); - // relational_expression = bitshift_expression { relational_operator bitshift_expression }. + // relational_expression = bitshift_expression { ("<" | ">" | "<=" | ">=") bitshift_expression + // }. bool is_relational_expression(array_t&); // bitshift_expression = additive_expression { ("<<" | ">>") additive_expression }. @@ -114,36 +114,31 @@ class expression_parser : public boost::noncopyable { // additive_expression = multiplicative_expression { ("+" | "-") multiplicative_expression }. bool is_additive_expression(array_t&); - bool is_additive_operator(name_t&); - // multiplicative_expression = unary_expression { ("*" | "/" | "%" | "div") unary_expression }. bool is_multiplicative_expression(array_t&); - bool is_multiplicative_operator(name_t&); - // unary_expression = postfix_expression | (unary_operator unary_expression). bool is_unary_expression(array_t&); // unary_operator = "+" | "-" | "!" | "~". bool is_unary_operator(name_t&); - // postfix_expression = primary_expression { ("[" expression "]") | ("." identifier) }. + // postfix_expression = primary_expression { ("[" expression "]") | ("." identifier) + // | "(" [argument_expression_list] ")"}. bool is_postfix_expression(array_t&); - // primary_expression = name | number | boolean | string | "empty" | array | dictionary - // | variable_or_fuction | ( "(" expression ")" ). + // primary_expression = name | number | boolean | string | "empty" | array | + // dictionary + // | identifier | ( "(" expression ")" ). bool is_primary_expression(array_t&); - // variable_or_fuctiontion = identifier ["(" [argument_expression_list] ")"]. - bool is_variable_or_function(array_t&); - // argument_expression_list = named_argument_list | argument_list. bool is_argument_expression_list(array_t&); // array = "[" [argument_list] "]". bool is_array(array_t&); - // dictionary = "{" named_argument_list "}". + // dictionary = "{" [named_argument_list] "}". bool is_dictionary(array_t&); // argument_list = expression { "," expression }. @@ -161,12 +156,6 @@ class expression_parser : public boost::noncopyable { // boolean = "true" | "false". bool is_boolean(any_regular_t&); - // relational_operator = "<" | ">" | "<=" | ">=". - bool is_relational_operator(name_t&); - - // bitshift_operator = "<<" | ">>". - bool is_bitshift_operator(name_t&); - // lexical tokens: bool is_identifier(name_t&); @@ -178,19 +167,20 @@ class expression_parser : public boost::noncopyable { subclasses to access it directly - but for now we'll stick with the law of Demiter. */ + void throw_exception(const char* errorString); + void throw_exception(const name_t& found, const name_t& expected); + bool is_token(name_t tokenName); + protected: const stream_lex_token_t& get_token(); void putback(); bool is_token(name_t tokenName, any_regular_t& tokenValue); - bool is_token(name_t tokenName); void require_token(name_t tokenName, any_regular_t& tokenValue); void require_token(name_t tokenName); bool is_keyword(name_t keywordName); void require_keyword(name_t keywordName); - void throw_exception(const char* errorString); - void throw_exception(const name_t& found, const name_t& expected); private: class implementation; diff --git a/adobe/implementation/lex_shared.hpp b/adobe/implementation/lex_shared.hpp index ccd299a0..657f21ae 100644 --- a/adobe/implementation/lex_shared.hpp +++ b/adobe/implementation/lex_shared.hpp @@ -95,7 +95,8 @@ struct lex_base_t { private: struct lex_fragment_t { - lex_fragment_t(const token_type& token = token_type(), + lex_fragment_t() = default; + lex_fragment_t(const token_type& token, const line_position_t& line_position = line_position_t()) : token_value_m(token), line_position_m(line_position) {} @@ -105,8 +106,10 @@ struct lex_base_t { uchar_ptr_t first_m; uchar_ptr_t last_m; - std::streampos streampos_m; - line_position_t line_position_m; + + line_position_t start_token_position_m; + line_position_t current_position_m; + parse_token_proc_t parse_proc_m; bool skip_white_m; std::array putback_m; // stack-based is faster @@ -121,8 +124,8 @@ struct lex_base_t { template lex_base_t::lex_base_t(uchar_ptr_t first, uchar_ptr_t last, const line_position_t& position) - : first_m(first), last_m(last), streampos_m(0), line_position_m(position), skip_white_m(true), - index_m(0), last_token_m(S) {} + : first_m(first), last_m(last), current_position_m(position), skip_white_m(true), index_m(0), + last_token_m(S) {} /**************************************************************************************************/ @@ -139,7 +142,7 @@ const typename lex_base_t::token_type& lex_base_t::get_token() { if (skip_white_m) skip_white_space(); - line_position_m.position_m = streampos_m; // remember the start of the token position + start_token_position_m = current_position_m; // remember the start of the token position if (!is_eof()) parse_proc_m(); @@ -156,7 +159,7 @@ const typename lex_base_t::token_type& lex_base_t::get_token() { template void lex_base_t::put_token(const token_type& token) { - last_token_m.push_back(lex_fragment_t(token, line_position_m)); + last_token_m.push_back(lex_fragment_t(token, start_token_position_m)); } /**************************************************************************************************/ @@ -191,11 +194,12 @@ bool lex_base_t::is_line_end() { bool result(is_line_end(first_m, last_m)); if (result) { - ++line_position_m.line_number_m; + ++current_position_m.line_number_m; - streampos_m += static_cast(std::distance(old_first, first_m)); + current_position_m.position_m += + static_cast(std::distance(old_first, first_m)); - line_position_m.line_start_m = streampos_m; + current_position_m.line_start_m = current_position_m.position_m; } return result; @@ -219,7 +223,7 @@ template void lex_base_t::throw_parser_exception(const char* error_string) { using adobe::throw_parser_exception; - throw_parser_exception(error_string, line_position_m); + throw_parser_exception(error_string, start_token_position_m); } /**************************************************************************************************/ @@ -240,15 +244,11 @@ void lex_base_t::set_skip_white_space(bool skip) { template void lex_base_t::skip_white_space() { - while (true) { - (void)is_line_end(); + while (first_m != last_m) { + if (is_line_end()) + continue; - if (first_m == last_m) - break; - - char c; - - if (peek_char(c) && std::isspace(static_cast(c))) + if (char c; peek_char(c) && std::isspace(static_cast(c))) advance_lex(); else break; @@ -308,6 +308,7 @@ struct stream_lex_base_t { void throw_exception(const name_t& expected, const name_t& found); void throw_parser_exception(const char* error_string); + void throw_parser_exception(std::string&& error_string); void set_skip_white_space(bool skip); @@ -324,8 +325,10 @@ struct stream_lex_base_t { private: I first_m; I last_m; - std::streampos streampos_m; - line_position_t line_position_m; + + line_position_t start_token_position_m; + line_position_t current_position_m; + parse_token_proc_t parse_proc_m; bool skip_white_m; std::array putback_m; // stack-based is faster @@ -340,8 +343,8 @@ struct stream_lex_base_t { template stream_lex_base_t::stream_lex_base_t(I first, I last, const line_position_t& position) - : identifier_buffer_m(128), first_m(first), last_m(last), streampos_m(1), - line_position_m(position), skip_white_m(true), index_m(0), last_token_m(S) {} + : identifier_buffer_m(128), first_m(first), last_m(last), current_position_m(position), + skip_white_m(true), index_m(0), last_token_m(S) {} /**************************************************************************************************/ @@ -357,7 +360,7 @@ bool stream_lex_base_t::get_char(char& c) { --index_m; - streampos_m += 1; + current_position_m.position_m += 1; return true; } @@ -369,7 +372,7 @@ bool stream_lex_base_t::get_char(char& c) { ++first_m; - streampos_m += 1; + current_position_m.position_m += 1; return true; } @@ -380,7 +383,7 @@ template void stream_lex_base_t::putback_char(char c) { putback_m[++index_m] = c; - streampos_m -= 1; + current_position_m.position_m -= 1; } /**************************************************************************************************/ @@ -406,7 +409,7 @@ void stream_lex_base_t::ignore_char() { else ++first_m; - streampos_m += 1; + current_position_m.position_m += 1; } /**************************************************************************************************/ @@ -428,7 +431,7 @@ const stream_lex_token_t& stream_lex_base_t::get_token() { if (skip_white_m) skip_white_space(); - line_position_m.position_m = streampos_m; // remember the start of the token position + start_token_position_m = current_position_m; // remember the start of the token position /* REVISIT (sparent) : I don't like that eof is not handled as the other tokens are handled @@ -452,7 +455,8 @@ const stream_lex_token_t& stream_lex_base_t::get_token() { template void stream_lex_base_t::put_token(stream_lex_token_t token) { - last_token_m.push_back(implementation::lex_fragment_t(std::move(token), line_position_m)); + last_token_m.push_back( + implementation::lex_fragment_t(std::move(token), start_token_position_m)); } /**************************************************************************************************/ @@ -485,12 +489,12 @@ bool stream_lex_base_t::is_line_end(char c) { std::size_t num_chars_eaten(is_line_end(first_m, last_m, c)); if (num_chars_eaten != 0) { - ++line_position_m.line_number_m; + ++current_position_m.line_number_m; if (num_chars_eaten == 2) - streampos_m += 1; + current_position_m.position_m += 1; - line_position_m.line_start_m = streampos_m; + current_position_m.line_start_m = current_position_m.position_m; } return num_chars_eaten != 0; @@ -502,7 +506,16 @@ template void stream_lex_base_t::throw_parser_exception(const char* error_string) { using adobe::throw_parser_exception; - throw_parser_exception(error_string, line_position_m); + throw_parser_exception(error_string, start_token_position_m); +} + +/**************************************************************************************************/ + +template +void stream_lex_base_t::throw_parser_exception(std::string&& error_string) { + using adobe::throw_parser_exception; + + throw_parser_exception(std::move(error_string), start_token_position_m); } /**************************************************************************************************/ diff --git a/adobe/implementation/lex_shared_fwd.hpp b/adobe/implementation/lex_shared_fwd.hpp index 6d78fee1..90892969 100644 --- a/adobe/implementation/lex_shared_fwd.hpp +++ b/adobe/implementation/lex_shared_fwd.hpp @@ -276,13 +276,13 @@ inline token_range_t static_token_range(T* begin) { template // E models Enumeration struct lex_token_t { - lex_token_t() {} + lex_token_t() = default; explicit lex_token_t(E enumeration, uchar_ptr_t first = 0, uchar_ptr_t last = 0) : enum_m(enumeration), range_m(first, last) {} - E enum_m; - token_range_t range_m; + E enum_m{static_cast(0)}; + token_range_t range_m{nullptr, nullptr}; }; /**************************************************************************************************/ diff --git a/adobe/implementation/parser_shared.hpp b/adobe/implementation/parser_shared.hpp index 377fac0c..ad223978 100644 --- a/adobe/implementation/parser_shared.hpp +++ b/adobe/implementation/parser_shared.hpp @@ -10,7 +10,7 @@ /**************************************************************************************************/ -#include +#include #include #include @@ -22,6 +22,7 @@ namespace adobe { /**************************************************************************************************/ void throw_parser_exception(const char* error_string, const line_position_t& position); +void throw_parser_exception(std::string&& error_string, const line_position_t& position); void throw_parser_exception(const char* expected, const char* found, const line_position_t& position); diff --git a/adobe/implementation/token.hpp b/adobe/implementation/token.hpp index 1d770854..a7098f58 100644 --- a/adobe/implementation/token.hpp +++ b/adobe/implementation/token.hpp @@ -25,66 +25,65 @@ namespace adobe { /**************************************************************************************************/ -extern static_name_t ifelse_k; - -extern static_name_t number_k; -extern static_name_t identifier_k; -extern static_name_t string_k; -extern static_name_t lead_comment_k; -extern static_name_t trail_comment_k; - -extern static_name_t semicolon_k; -extern static_name_t comma_k; -extern static_name_t assign_k; -extern static_name_t question_k; -extern static_name_t colon_k; -extern static_name_t open_brace_k; -extern static_name_t close_brace_k; -extern static_name_t open_parenthesis_k; -extern static_name_t close_parenthesis_k; -extern static_name_t dot_k; -extern static_name_t open_bracket_k; -extern static_name_t close_bracket_k; -extern static_name_t at_k; -extern static_name_t is_k; -extern static_name_t to_k; - -extern static_name_t add_k; -extern static_name_t subtract_k; -extern static_name_t multiply_k; -extern static_name_t divide_k; -extern static_name_t modulus_k; - -extern static_name_t not_k; -extern static_name_t unary_negate_k; - -extern static_name_t less_k; -extern static_name_t greater_k; - -extern static_name_t and_k; -extern static_name_t or_k; -extern static_name_t less_equal_k; -extern static_name_t greater_equal_k; -extern static_name_t not_equal_k; -extern static_name_t equal_k; - -extern static_name_t keyword_k; -extern static_name_t empty_k; -extern static_name_t true_k; -extern static_name_t false_k; - -extern static_name_t function_k; -extern static_name_t variable_k; -extern static_name_t index_k; -extern static_name_t array_k; -extern static_name_t dictionary_k; - -extern static_name_t bitwise_and_k; -extern static_name_t bitwise_xor_k; -extern static_name_t bitwise_or_k; -extern static_name_t bitwise_rshift_k; -extern static_name_t bitwise_lshift_k; -extern static_name_t bitwise_negate_k; + +// Operators +constexpr static_name_t add_k = ".add"_name; +constexpr static_name_t and_k = ".and"_name; +constexpr static_name_t array_k = ".array"_name; +constexpr static_name_t at_k = ".at"_name; +constexpr static_name_t bitwise_and_k = ".bitwise_and"_name; +constexpr static_name_t bitwise_lshift_k = ".bitwise_lshift"_name; +constexpr static_name_t bitwise_negate_k = ".bitwise_negate"_name; +constexpr static_name_t bitwise_or_k = ".bitwise_or"_name; +constexpr static_name_t bitwise_rshift_k = ".bitwise_rshift"_name; +constexpr static_name_t bitwise_xor_k = ".bitwise_xor"_name; +constexpr static_name_t dictionary_k = ".dictionary"_name; +constexpr static_name_t divide_k = ".divide"_name; +constexpr static_name_t equal_k = ".equal"_name; +constexpr static_name_t function_k = ".function"_name; +constexpr static_name_t greater_equal_k = ".greater_equal"_name; +constexpr static_name_t greater_k = ".greater"_name; +constexpr static_name_t ifelse_k = ".ifelse"_name; +constexpr static_name_t index_k = ".index"_name; +constexpr static_name_t is_k = ".is"_name; +constexpr static_name_t less_equal_k = ".less_equal"_name; +constexpr static_name_t less_k = ".less"_name; +constexpr static_name_t modulus_k = ".modulus"_name; +constexpr static_name_t multiply_k = ".multiply"_name; +constexpr static_name_t not_equal_k = ".not_equal"_name; +constexpr static_name_t not_k = ".not"_name; +constexpr static_name_t or_k = ".or"_name; +constexpr static_name_t subtract_k = ".subtract"_name; +constexpr static_name_t to_k = ".to"_name; +constexpr static_name_t unary_negate_k = ".unary_negate"_name; +constexpr static_name_t variable_k = ".variable"_name; + +// tokens +constexpr static_name_t assign_k = "assign"_name; +constexpr static_name_t close_brace_k = "close_brace"_name; +constexpr static_name_t close_bracket_k = "close_bracket"_name; +constexpr static_name_t close_parenthesis_k = "close_parenthesis"_name; +constexpr static_name_t colon_k = "colon"_name; +constexpr static_name_t comma_k = "comma"_name; +constexpr static_name_t dot_k = "dot"_name; +constexpr static_name_t empty_k = "empty"_name; +constexpr static_name_t false_k = "false"_name; +constexpr static_name_t identifier_k = "identifier"_name; +constexpr static_name_t keyword_k = "keyword"_name; +constexpr static_name_t lead_comment_k = "lead_comment"_name; +constexpr static_name_t number_k = "number"_name; +constexpr static_name_t open_brace_k = "open_brace"_name; +constexpr static_name_t open_bracket_k = "open_bracket"_name; +constexpr static_name_t open_parenthesis_k = "open_parenthesis"_name; +constexpr static_name_t question_k = "question"_name; +constexpr static_name_t semicolon_k = "semicolon"_name; +constexpr static_name_t string_k = "string"_name; +constexpr static_name_t trail_comment_k = "trail_comment"_name; +constexpr static_name_t true_k = "true"_name; + +/// Given a name that represents a token, return the string representation of that token. +/// Precondition: `token` is a valid token name. +auto token_to_string(name_t token) -> const char*; /**************************************************************************************************/ diff --git a/adobe/istream.hpp b/adobe/istream.hpp index db6885cd..3795b581 100644 --- a/adobe/istream.hpp +++ b/adobe/istream.hpp @@ -11,8 +11,7 @@ #include #include -#include -#include +#include #include #include #include @@ -133,28 +132,8 @@ position) The vector of line_position_ts detailing the trace history of this exception. */ -//***************************************************************************// -//***************************************************************************// -//***************************************************************************// - -/*! -\fn std::string adobe::format_stream_error(std::istream& stream, const adobe::stream_error_t& -error); -\relates adobe::stream_error_t - -A function used to format data stored in an adobe::stream_error_t into something human-readable. - -\param stream The stream containing the parsing information from which the error came. -\param error The error detailing the cause for the parsing failure. - -\return - A string that presents the parsing failure in a human readable form. Note that the string is -intended to be displayed on multiple lines with a monospaced font. -*/ - // line_position_t is used to remember a position on a particular line of a file. - struct line_position_t { public: typedef std::function getline_proc_impl_t; @@ -164,8 +143,8 @@ struct line_position_t { line_position_t(name_t file_path, getline_proc_t getline_proc, int line_number = 1, std::streampos line_start = 0, std::streampos position = -1); - // This constructor is used with __FILE__ and __LINE__, line_index starts at 0 - explicit line_position_t(const char*, int line_index = 0); + // This constructor is used with __FILE__ and __LINE__, line_number starts at 1 + explicit line_position_t(const char*, int line_number = 1); #if !defined(ADOBE_NO_DOCUMENTATION) line_position_t(); @@ -196,7 +175,7 @@ std::ostream& operator<<(std::ostream&, const line_position_t&); class stream_error_t : public std::logic_error { public: - typedef std::vector position_set_t; + using position_set_t = std::vector; stream_error_t(const std::exception& base, const line_position_t& position) : std::logic_error(base.what()) { @@ -220,13 +199,18 @@ class stream_error_t : public std::logic_error { const position_set_t& line_position_set() const { return line_position_set_m; } #if !defined(ADOBE_NO_DOCUMENTATION) - ~stream_error_t() throw() {} + ~stream_error_t() {} private: position_set_t line_position_set_m; #endif // !defined(ADOBE_NO_DOCUMENTATION) }; + +/**************************************************************************************************/ + +std::ostream& operator<<(std::ostream&, const stream_error_t&); + /**************************************************************************************************/ /* diff --git a/adobe/istream_fwd.hpp b/adobe/istream_fwd.hpp index ca330cb2..deb36ec4 100644 --- a/adobe/istream_fwd.hpp +++ b/adobe/istream_fwd.hpp @@ -22,7 +22,7 @@ struct line_position_t; class stream_error_t; -std::string format_stream_error(std::istream&, const stream_error_t&); +[[deprecated]] std::string format_stream_error(std::istream&, const stream_error_t&); std::string format_stream_error(const stream_error_t&); diff --git a/adobe/sha.hpp b/adobe/sha.hpp index ebf1194a..7e231aa9 100644 --- a/adobe/sha.hpp +++ b/adobe/sha.hpp @@ -100,7 +100,7 @@ class byte_source_iterators { // byte_source_iterator_n. // The number available is not necessarily the total number, it is just // the number we can get hold of right now. - std::size_t bits_available() const { return (first_ == last_) ? 0 : 8; } + std::uint64_t bits_available() const { return (first_ == last_) ? 0 : 8; } std::uint8_t operator*() const { return *first_; } @@ -131,14 +131,14 @@ up front template class byte_source_iterator_n { public: - byte_source_iterator_n(I const& first, std::size_t num_bits) + byte_source_iterator_n(I const& first, std::uint64_t num_bits) : first_(first), num_bits_(num_bits) { static_assert(sizeof(typename std::iterator_traits::value_type) == sizeof(std::uint8_t), "Iterator must supply bytes."); } // The last byte might not be complete - std::size_t bits_available() const { return (num_bits_ > 8) ? 8 : num_bits_; } + std::uint64_t bits_available() const { return (num_bits_ > 8u) ? 8u : num_bits_; } std::uint8_t operator*() const { return *first_; } @@ -159,14 +159,14 @@ class byte_source_iterator_n { private: I first_; - std::size_t num_bits_; + std::uint64_t num_bits_; }; /**************************************************************************************************/ template std::uint64_t stuff_into_state(typename HashTraits::message_block_type& state, - std::uint16_t& stuff_bit_offset, std::size_t num_bits, + std::uint16_t& stuff_bit_offset, std::uint64_t num_bits, ByteSource& byte_source) { typedef HashTraits traits_type; diff --git a/adobe/virtual_machine.hpp b/adobe/virtual_machine.hpp index aae50630..84961679 100644 --- a/adobe/virtual_machine.hpp +++ b/adobe/virtual_machine.hpp @@ -12,8 +12,12 @@ #include #include +#include +#include +#include #include + #define BOOST_FUNCTION_NO_DEPRECATED #include @@ -26,6 +30,101 @@ namespace adobe { +/**************************************************************************************************/ + +/// Any function of the form `any_regular_t(const any_regular_t&)`. All function_t objects compare +/// equal so they can be placed in an `any_regular_t` object. +class function_t : public std::function { +public: + using base_t = std::function; + using base_t::function; + using base_t::operator=; + using base_t::swap; + using base_t::operator bool; + using base_t::target; + using base_t::target_type; + + function_t() noexcept = default; + function_t(std::nullptr_t) noexcept : base_t(nullptr) {} + function_t(const function_t&) = default; + function_t(function_t&&) noexcept = default; + template , function_t>, int> = 0> + function_t(F&& f) : base_t(std::forward(f)) {} + function_t& operator=(const function_t&) = default; + function_t& operator=(function_t&&) noexcept = default; + + bool operator==(const function_t&) const { return true; } + bool operator!=(const function_t&) const { return false; } +}; + +/**************************************************************************************************/ + +/// Returns a simple name for the core types used by the virtual machine. +const char* type_name(const std::type_info& type); + +/**************************************************************************************************/ + +/// Does a runtime cast on an any_regular_t with better error reporting. +template +auto cast(adobe::any_regular_t& x) -> decltype(x.cast()) { + if constexpr (!std::is_same_v) { + if (x.type_info() != typeid(promote_t)) + throw std::runtime_error(std::string{"expected `"} + type_name(typeid(promote_t)) + + "` found `" + type_name(x.type_info()) + "`"); + } + return x.cast(); +} + +/// Does a runtime cast on an any_regular_t with better error reporting. +template +auto cast(const adobe::any_regular_t& x) -> decltype(x.cast()) { + if constexpr (!std::is_same_v) { + if (x.type_info() != typeid(promote_t)) + throw std::runtime_error(std::string{"expected `"} + type_name(typeid(promote_t)) + + "` found `" + type_name(x.type_info()) + "`"); + } + return x.cast(); +} + +/**************************************************************************************************/ + +namespace detail { + +/// Provide an invoke function for an invocable F with a signature Sig, that will extract the +/// arguments from an array_t. An exception will be thrown if the number of items in the array +/// does not match the number of arguments in the signature, or if any of the argument types do +/// not match the corresponding array_t item type. +template +struct function_packager; + +template +struct function_packager { + template + static auto invoke(const F& f, const array_t& args, std::index_sequence) { + if (args.size() != sizeof...(Args)) + throw std::logic_error("expected `" + std::to_string(sizeof...(Args)) + + "` arguments, found `" + std::to_string(args.size()) + "`."); + // Use std::invoke to make it easier to handle member function pointers? + return f(cast(args[Indicies])...); + } + template + static auto invoke(const F& f, const array_t& args) { + return invoke(f, args, std::index_sequence_for()); + } +}; + +} // namespace detail + +/**************************************************************************************************/ + +/// Create a function_t from an invocable object. +template +any_regular_t make_function(F&& f) { + return function_t{[f = std::forward(f)](const any_regular_t& args) { + return detail::function_packager::invoke(f, args.cast()); + }}; +} + /**************************************************************************************************/ /* Note: For all bitwise operators the numeric data type (double) will be cast down to a @@ -42,9 +141,11 @@ class virtual_machine_t { typedef any_regular_t(numeric_index_lookup_signature_t)(const any_regular_t&, std::size_t index); + + using variable_scope_t = std::function(name_t)>; + using variable_lookup_t = std::function; - using dictionary_function_lookup_t = std::function; - using array_function_lookup_t = std::function; + using named_index_lookup_t = std::function; using numeric_index_lookup_t = std::function; @@ -71,14 +172,17 @@ class virtual_machine_t { any_regular_t& back(); void pop_back(); + void push_scope(variable_scope_t&& scope); + void set_variable_lookup(const variable_lookup_t&); - void set_array_function_lookup(const array_function_lookup_t&); - void set_dictionary_function_lookup(const dictionary_function_lookup_t&); + void set_named_index_lookup(const named_index_lookup_t&); void set_numeric_index_lookup(const numeric_index_lookup_t&); void override_operator(name_t, const binary_op_override_t&); + void add_variable(const name_t& name, const any_regular_t& value); + class implementation_t; private: diff --git a/cmake/CPM.cmake b/cmake/CPM.cmake new file mode 100644 index 00000000..4f47826f --- /dev/null +++ b/cmake/CPM.cmake @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: MIT +# +# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors + +set(CPM_DOWNLOAD_VERSION 0.40.3) +set(CPM_HASH_SUM "2e89a807e906eab270c68c531129dc6b7f989c79a38a4cca7dde0f3ec93d9c68") + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} +) + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/documentation/asl/related/reference_expression_section.dox b/documentation/asl/related/reference_expression_section.dox index 352a7db9..71edcd35 100644 --- a/documentation/asl/related/reference_expression_section.dox +++ b/documentation/asl/related/reference_expression_section.dox @@ -13,7 +13,7 @@ compound_token = "&&" | "||" | "<=" | ">=" | "==" | "!=". string = quoted_string { quoted_string }. lead_comment = "/*" {character} "*/". trail_comment = "//" {character} eol. -identifer = (letter | "_") {letter | "_" | digit}. +identifier = (letter | "_") {letter | "_" | digit}. keywords = "empty" | "true" | "false" | . number = digits ["e" ["+" | "-"]] digits. diff --git a/documentation/asl/virtual_machine_t.dox b/documentation/asl/virtual_machine_t.dox index f0993151..f6dcb90c 100644 --- a/documentation/asl/virtual_machine_t.dox +++ b/documentation/asl/virtual_machine_t.dox @@ -230,22 +230,6 @@ Sets the function callback for simple variable lookup. \param lookup The function to be used as the callback. */ -/*! -\fn void adobe::virtual_machine_t::set_array_function_lookup(const adobe::virtual_machine_t::array_function_lookup_t& function) - -Sets a client-side callback for use when looking up unnamed-argument functions. - -\param function The function to be used as the callback. -*/ - -/*! -\fn void adobe::virtual_machine_t::set_dictionary_function_lookup(const adobe::virtual_machine_t::dictionary_function_lookup_t& function) - -Sets a client-side callback for use when looking up named-argument functions. - -\param function The function to be used as the callback. -*/ - /*! \class adobe::virtual_machine_t::tracked_value_t virtual_machine.hpp diff --git a/source/adam.cpp b/source/adam.cpp index 335888d3..a6250d0b 100644 --- a/source/adam.cpp +++ b/source/adam.cpp @@ -83,8 +83,6 @@ typedef adobe::sheet_t sheet_t; typedef std::bitset<1024> cell_bits_t; typedef int priority_t; -struct compare_contributing_t; - enum access_specifier_t { access_input, access_interface_input, @@ -162,7 +160,7 @@ class sheet_t::implementation_t : boost::noncopyable { void set(name_t, const any_regular_t&); // input cell. void touch(const name_t*, const name_t*); // range of input cells. - any_regular_t get(name_t); + auto cell_value(name_t) -> optional; const any_regular_t& operator[](name_t) const; void add_input(name_t, const line_position_t&, const array_t& initializer); @@ -324,7 +322,6 @@ class sheet_t::implementation_t : boost::noncopyable { }; friend struct cell_t; - friend struct compare_contributing_t; any_regular_t calculate_expression(const line_position_t& position, const array_t& expression); @@ -609,7 +606,9 @@ void sheet_t::reinitialize() { object_m->reinitialize(); } void sheet_t::set(const dictionary_t& dictionary) { object_m->set(dictionary); } -any_regular_t sheet_t::get(name_t cell) { return object_m->get(cell); } +auto sheet_t::cell_value(name_t cell) -> optional { + return object_m->cell_value(cell); +} const any_regular_t& sheet_t::operator[](name_t x) const { return (*object_m)[x]; } @@ -788,9 +787,14 @@ void sheet_t::implementation_t::add_interface(name_t name, bool linked, std::ref(*this), position2, expression), cell_set_m.size(), &cell_set_m.back())); } else { - cell_set_m.push_back(cell_t(access_interface_output, name, - std::bind(&implementation_t::get, std::ref(*this), name), - cell_set_m.size(), &cell_set_m.back())); + cell_set_m.push_back(cell_t( + access_interface_output, name, + [this, name]() { + auto r = cell_value(name); + ADOBE_ASSERT(r && "cell not found"); + return std::move(*r); + }, + cell_set_m.size(), &cell_set_m.back())); } output_index_m.insert(cell_set_m.back()); @@ -812,9 +816,14 @@ void sheet_t::implementation_t::add_interface(name_t name, any_regular_t initial cell.state_m = std::move(initial); cell.priority_m = ++priority_high_m; - cell_set_m.push_back(cell_t(access_interface_output, name, - std::bind(&implementation_t::get, std::ref(*this), name), - cell_set_m.size(), &cell)); + cell_set_m.push_back(cell_t( + access_interface_output, name, + [this, name] { + auto r = cell_value(name); + ADOBE_ASSERT(r && "cell not found"); + return std::move(*r); + }, + cell_set_m.size(), &cell)); output_index_m.insert(cell_set_m.back()); @@ -1451,7 +1460,7 @@ const any_regular_t& sheet_t::implementation_t::operator[](name_t variable_name) /**************************************************************************************************/ -any_regular_t sheet_t::implementation_t::get(name_t variable_name) { +auto sheet_t::implementation_t::cell_value(name_t variable_name) -> optional { #if 0 // REVISIT (sparent) : I can't currently turn this assert on because of inspect. // However, it would be good to seperate out this get from the operator[] and @@ -1468,8 +1477,7 @@ any_regular_t sheet_t::implementation_t::get(name_t variable_name) { if (iter == input_index_m.end()) { iter = name_index_m.find(variable_name); if (iter == name_index_m.end() || iter->specifier_m != access_constant) { - throw std::logic_error( - make_string("variable ", variable_name.c_str(), " not found.")); + return nullopt; } } @@ -1479,6 +1487,12 @@ any_regular_t sheet_t::implementation_t::get(name_t variable_name) { return cell.state_m; } + + index_t::iterator iter = name_index_m.find(variable_name); + if (iter == name_index_m.end()) { + return nullopt; + } + scope_count scope(get_count_m); /* @@ -1490,12 +1504,6 @@ any_regular_t sheet_t::implementation_t::get(name_t variable_name) { throw std::logic_error(std::string("cycle detected, consider using a relate { } clause.")); } - index_t::iterator iter = name_index_m.find(variable_name); - - if (iter == name_index_m.end()) { - throw std::logic_error(make_string("variable ", variable_name.c_str(), " not found.")); - } - cell_t& cell = *iter; // If the variable is on the top of the stack then we assume it diff --git a/source/eve.cpp b/source/eve.cpp index c82c161b..77a1fd44 100644 --- a/source/eve.cpp +++ b/source/eve.cpp @@ -907,12 +907,13 @@ void view_proxy_t::layout_with(::child_iterator first, ::child_iterator last, } /* - REVISIT (sparent) : Filled items fill forward. The space to behind is going to + REVISIT (sparent) : Filled items fill forward. The space behind is going to be dead space... It would be good to allow it to be filled. */ if (iter_gslice.alignment_m == adobe::layout_attributes_t::align_reverse_fill) { - + ADOBE_ASSERT(padded_count && + "align_reverse_fill item not accounted for in padded_count."); int additional_length(remaining_additional_length / padded_count); --padded_count; @@ -953,6 +954,7 @@ void view_proxy_t::layout_with(::child_iterator first, ::child_iterator last, case adobe::layout_attributes_t::align_forward_fill: case adobe::layout_attributes_t::align_center: case adobe::layout_attributes_t::align_proportional: { + ADOBE_ASSERT(padded_count && "item not accounted for in padded_count."); additional_length = remaining_additional_length / padded_count; --padded_count; remaining_additional_length -= additional_length; diff --git a/source/eve_evaluate.cpp b/source/eve_evaluate.cpp index 93912ed5..0bd0a11e 100644 --- a/source/eve_evaluate.cpp +++ b/source/eve_evaluate.cpp @@ -372,7 +372,7 @@ adobe::any_regular_t layout_variables(adobe::sheet_t& layout_sheet, adobe::name_ if (found != reflected_range_g->second && *found == name) return adobe::any_regular_t(name); - return layout_sheet.get(name); + return *layout_sheet.cell_value(name); } diff --git a/source/eve_parser.cpp b/source/eve_parser.cpp index ad15ac9e..f32e5609 100644 --- a/source/eve_parser.cpp +++ b/source/eve_parser.cpp @@ -514,7 +514,11 @@ line_position_t parse(std::istream& in, const line_position_t& line_position, const eve_callback_suite_t::position_t& position, const eve_callback_suite_t& assembler) { eve_parser parser(assembler, in, line_position); - parser.parse(position); + try { + parser.parse(position); + } catch (const exception& error) { + throw stream_error_t(error, parser.next_position()); + } return parser.next_position(); } diff --git a/source/expression_parser.cpp b/source/expression_parser.cpp index f906110c..be272456 100644 --- a/source/expression_parser.cpp +++ b/source/expression_parser.cpp @@ -12,10 +12,12 @@ with regards to state. /**************************************************************************************************/ +#include #include #include #include #include +#include #include #include @@ -42,6 +44,85 @@ using ::isspace; #endif using namespace std; +using namespace adobe; + +/**************************************************************************************************/ + +namespace { + +/**************************************************************************************************/ + +// Handles short-circuit productions of the form: +// ` { ("&&" | "||") }.` +template +bool is_logical_expression(expression_parser& parser, array_t& expression_stack, + IsExpression is_expression, IsOperator is_operator, + const char* error_message) { + if (!is_expression(expression_stack)) + return false; + + for (auto oper = is_operator(); oper; oper = is_operator()) { + array_t operand2; + + if (!is_expression(operand2)) + parser.throw_exception(error_message); + + push_back(expression_stack, operand2); + expression_stack.push_back(*oper); + } + + return true; +} + +/**************************************************************************************************/ + +// Handles non-short-circuit productions of the form: +// ` { }.` +template +bool is_binary_expression(expression_parser& parser, array_t& expression_stack, + IsExpression is_expression, IsOperator is_operator, + const char* error_message) { + if (!is_expression(expression_stack)) + return false; + + for (auto oper = is_operator(); oper; oper = is_operator()) { + if (!is_expression(expression_stack)) + parser.throw_exception(error_message); + + expression_stack.push_back(*oper); + } + + return true; +} + + +/**************************************************************************************************/ + +class name_span_t { +public: + template + name_span_t(const T& a) : first_m(a.data()), last_m(a.data() + a.size()) {} + + const static_name_t* begin() const { return first_m; } + const static_name_t* end() const { return last_m; } + +private: + const static_name_t* first_m; + const static_name_t* last_m; +}; + +optional match_token(expression_parser& parser, name_span_t span) { + for (const auto& token : span) { + if (parser.is_token(token)) { + return token; + } + } + return nullopt; +} + +/**************************************************************************************************/ + +} // namespace /**************************************************************************************************/ @@ -130,7 +211,7 @@ bool expression_parser::is_expression(array_t& expression_stack) { void expression_parser::require_expression(array_t& expression_stack) { if (!is_expression(expression_stack)) { - throw_exception("Expression required."); + throw_exception("`expression` required."); } } @@ -138,195 +219,99 @@ void expression_parser::require_expression(array_t& expression_stack) { // or_expression = and_expression { "||" and_expression }. bool expression_parser::is_or_expression(array_t& expression_stack) { - if (!is_and_expression(expression_stack)) - return false; - - while (is_token(or_k)) { - array_t operand2; - - if (!is_and_expression(operand2)) - throw_exception("and_expression required"); - - push_back(expression_stack, operand2); - expression_stack.push_back(any_regular_t(or_k)); - } - - return true; + return is_logical_expression( + *this, expression_stack, [&](auto& stack) { return is_and_expression(stack); }, + [&] { return match_token(*this, array{or_k}); }, "`and_expression` required."); } /**************************************************************************************************/ // and_expression = bitwise_or_expression { "&&" bitwise_or_expression }. bool expression_parser::is_and_expression(array_t& expression_stack) { - if (!is_bitwise_or_expression(expression_stack)) - return false; - - while (is_token(and_k)) { - array_t operand2; - - if (!is_bitwise_or_expression(operand2)) - throw_exception("bitwise_or_expression required"); - - push_back(expression_stack, operand2); - expression_stack.push_back(any_regular_t(and_k)); - } - - return true; + return is_logical_expression( + *this, expression_stack, [&](auto& stack) { return is_bitwise_or_expression(stack); }, + [&] { return is_token(and_k) ? optional{and_k} : nullopt; }, + "`bitwise_or_expression` required."); } /**************************************************************************************************/ // bitwise_or_expression = bitwise_xor_expression { "|" bitwise_xor_expression }. bool expression_parser::is_bitwise_or_expression(array_t& expression_stack) { - if (!is_bitwise_xor_expression(expression_stack)) - return false; - - while (is_token(bitwise_or_k)) { - array_t operand2; - - if (!is_bitwise_xor_expression(operand2)) - throw_exception("bitwise_xor_expression required"); - - push_back(expression_stack, operand2); - expression_stack.push_back(any_regular_t(bitwise_or_k)); - } - - return true; + return is_binary_expression( + *this, expression_stack, [&](auto& stack) { return is_bitwise_xor_expression(stack); }, + [&] { return is_token(bitwise_or_k) ? optional{bitwise_or_k} : nullopt; }, + "`bitwise_xor_expression` required."); } /**************************************************************************************************/ // bitwise_xor_expression = bitwise_and_expression { "^" bitwise_and_expression }. bool expression_parser::is_bitwise_xor_expression(array_t& expression_stack) { - if (!is_bitwise_and_expression(expression_stack)) - return false; - - while (is_token(bitwise_xor_k)) { - array_t operand2; - - if (!is_bitwise_and_expression(operand2)) - throw_exception("bitwise_and_expression required"); - - push_back(expression_stack, operand2); - expression_stack.push_back(any_regular_t(bitwise_xor_k)); - } - - return true; + return is_binary_expression( + *this, expression_stack, [&](auto& stack) { return is_bitwise_and_expression(stack); }, + [&] { return is_token(bitwise_xor_k) ? optional{bitwise_xor_k} : nullopt; }, + "`bitwise_and_expression` required."); } /**************************************************************************************************/ // bitwise_and_expression = equality_expression { "&" equality_expression }. bool expression_parser::is_bitwise_and_expression(array_t& expression_stack) { - if (!is_equality_expression(expression_stack)) - return false; - - while (is_token(bitwise_and_k)) { - array_t operand2; - - if (!is_equality_expression(operand2)) - throw_exception("equality_expression required"); - - push_back(expression_stack, operand2); - expression_stack.push_back(any_regular_t(bitwise_and_k)); - } - - return true; + return is_binary_expression( + *this, expression_stack, [&](auto& stack) { return is_equality_expression(stack); }, + [&] { return is_token(bitwise_and_k) ? optional{bitwise_and_k} : nullopt; }, + "`equality_expression` required."); } /**************************************************************************************************/ // equality_expression = relational_expression { ("==" | "!=") relational_expression }. bool expression_parser::is_equality_expression(array_t& expression_stack) { - if (!is_relational_expression(expression_stack)) - return false; - - bool is_equal = false; - - while ((is_equal = is_token(equal_k)) || is_token(not_equal_k)) { - if (!is_relational_expression(expression_stack)) - throw_exception("Primary required."); - - expression_stack.push_back(is_equal ? any_regular_t(equal_k) : any_regular_t(not_equal_k)); - } - - return true; + return is_binary_expression( + *this, expression_stack, [&](auto& stack) { return is_relational_expression(stack); }, + [&] { return match_token(*this, array{equal_k, not_equal_k}); }, + "`relational_expression` required."); } /**************************************************************************************************/ // relational_expression = bitshift_expression { ("<" | ">" | "<=" | ">=") bitshift_expression }. bool expression_parser::is_relational_expression(array_t& expression_stack) { - if (!is_bitshift_expression(expression_stack)) - return false; - - name_t operator_l; - - while (is_relational_operator(operator_l)) { - if (!is_bitshift_expression(expression_stack)) - throw_exception("Primary required."); - - expression_stack.push_back(any_regular_t(operator_l)); - } - - return true; + return is_binary_expression( + *this, expression_stack, [&](auto& stack) { return is_bitshift_expression(stack); }, + [&] { return match_token(*this, array{less_k, greater_k, less_equal_k, greater_equal_k}); }, + "`bitshift_expression` required."); } /**************************************************************************************************/ // bitshift_expression = additive_expression { ("<<" | ">>") additive_expression }. bool expression_parser::is_bitshift_expression(array_t& expression_stack) { - if (!is_additive_expression(expression_stack)) - return false; - - name_t operator_l; - - while (is_bitshift_operator(operator_l)) { - if (!is_additive_expression(expression_stack)) - throw_exception("Primary required."); - - expression_stack.push_back(any_regular_t(operator_l)); - } - - return true; + return is_binary_expression( + *this, expression_stack, [&](auto& stack) { return is_additive_expression(stack); }, + [&] { return match_token(*this, array{bitwise_lshift_k, bitwise_rshift_k}); }, + "`additive_expression` required."); } /**************************************************************************************************/ // additive_expression = multiplicative_expression { additive_operator multiplicative_expression }. bool expression_parser::is_additive_expression(array_t& expression_stack) { - if (!is_multiplicative_expression(expression_stack)) - return false; - - name_t operator_l; - - while (is_additive_operator(operator_l)) { - if (!is_multiplicative_expression(expression_stack)) - throw_exception("Primary required."); - - expression_stack.push_back(any_regular_t(operator_l)); - } - - return true; + return is_binary_expression( + *this, expression_stack, [&](auto& stack) { return is_multiplicative_expression(stack); }, + [&] { return match_token(*this, array{add_k, subtract_k}); }, + "`multiplicative_expression` required."); } /**************************************************************************************************/ // multiplicative_expression = unary_expression { ("*" | "/" | "%") unary_expression }. bool expression_parser::is_multiplicative_expression(array_t& expression_stack) { - if (!is_unary_expression(expression_stack)) - return false; - - name_t operator_l; - - while (is_multiplicative_operator(operator_l)) { - if (!is_unary_expression(expression_stack)) - throw_exception("Primary required."); - - expression_stack.push_back(any_regular_t(operator_l)); - } - - return true; + return is_binary_expression( + *this, expression_stack, [&](auto& stack) { return is_unary_expression(stack); }, + [&] { return match_token(*this, array{multiply_k, divide_k, modulus_k}); }, + "`unary_expression` required."); } /**************************************************************************************************/ @@ -341,7 +326,7 @@ bool expression_parser::is_unary_expression(array_t& expression_stack) { if (is_unary_operator(operator_l)) { if (!is_unary_expression(expression_stack)) - throw_exception("Unary expression required."); + throw_exception("`unary_expression` required."); if (operator_l != add_k) expression_stack.push_back(any_regular_t(operator_l)); @@ -354,7 +339,8 @@ bool expression_parser::is_unary_expression(array_t& expression_stack) { /**************************************************************************************************/ -// postfix_expression = primary_expression { ("[" expression "]") | ("." identifier) }. +// postfix_expression = primary_expression { ("[" expression "]") | ("." identifier) +// | "(" [argument_expression_list] ")"}. bool expression_parser::is_postfix_expression(array_t& expression_stack) { if (!is_primary_expression(expression_stack)) @@ -364,14 +350,20 @@ bool expression_parser::is_postfix_expression(array_t& expression_stack) { if (is_token(open_bracket_k)) { require_expression(expression_stack); require_token(close_bracket_k); + expression_stack.push_back(index_k); } else if (is_token(dot_k)) { any_regular_t result; require_token(identifier_k, result); - expression_stack.push_back(result); + expression_stack.push_back(std::move(result)); + expression_stack.push_back(index_k); + } else if (is_token(open_parenthesis_k)) { + // If there are no parameters then set the parameters to an empty array. + if (!is_argument_expression_list(expression_stack)) + push_back(expression_stack, adobe::array_t()); + require_token(close_parenthesis_k); + expression_stack.push_back(function_k); } else break; - - expression_stack.push_back(any_regular_t(index_k)); } return true; @@ -454,11 +446,12 @@ bool expression_parser::is_named_argument(array_t& expression_stack) { /**************************************************************************************************/ -// primary_expression = name | number | boolean | string | "empty" | array | dictionary -// | variable_or_fuction | ( "(" expression ")" ). +// primary_expression = name | number | boolean | string | "empty" | array | dictionary +// | identifier | ( "(" expression ")" ). bool expression_parser::is_primary_expression(array_t& expression_stack) { any_regular_t result; // empty result used if is_keyword(empty_k) + name_t name_result; if (is_name(result) || is_token(number_k, result) || is_boolean(result) || is_token(string_k, result) || is_keyword(empty_k)) { @@ -468,9 +461,11 @@ bool expression_parser::is_primary_expression(array_t& expression_stack) { return true; else if (is_dictionary(expression_stack)) return true; - else if (is_variable_or_function(expression_stack)) + else if (is_identifier(name_result)) { + expression_stack.push_back(name_result); + expression_stack.push_back(any_regular_t(variable_k)); return true; - else if (is_token(open_parenthesis_k)) { + } else if (is_token(open_parenthesis_k)) { require_expression(expression_stack); require_token(close_parenthesis_k); return true; @@ -481,31 +476,6 @@ bool expression_parser::is_primary_expression(array_t& expression_stack) { /**************************************************************************************************/ -// variable_or_function = identifier ["(" [argument_expression_list] ")"]. -bool expression_parser::is_variable_or_function(array_t& expression_stack) { - any_regular_t result; - - if (!is_token(identifier_k, result)) - return false; - - if (is_token(open_parenthesis_k)) { - // If there are no parameters then set the parameters to an empty array. - if (!is_argument_expression_list(expression_stack)) - expression_stack.push_back(any_regular_t(adobe::array_t())); - - require_token(close_parenthesis_k); - expression_stack.push_back(result); - expression_stack.push_back(any_regular_t(function_k)); - } else { - expression_stack.push_back(result); - expression_stack.push_back(any_regular_t(variable_k)); - } - - return true; -} - -/**************************************************************************************************/ - bool expression_parser::is_dictionary(array_t& expression_stack) { if (!is_token(open_brace_k)) return false; @@ -539,7 +509,7 @@ bool expression_parser::is_name(any_regular_t& result) { return false; if (!is_token(keyword_k, result) && !is_token(identifier_k, result)) - throw_exception("identifier or keyword required."); + throw_exception("`identifier` or `keyword` required."); return true; } @@ -558,65 +528,6 @@ bool expression_parser::is_boolean(any_regular_t& result) { return false; } -/**************************************************************************************************/ - -// relational_operator = "<" | ">" | "<=" | ">=". -bool expression_parser::is_relational_operator(name_t& name_result) { - const stream_lex_token_t& result(get_token()); - - name_t name = result.first; - if (name == less_k || name == greater_k || name == less_equal_k || name == greater_equal_k) { - name_result = name; - return true; - } - putback(); - return false; -} - -/**************************************************************************************************/ - -// bitshift_operator = "<<" | ">>" -bool expression_parser::is_bitshift_operator(name_t& name_result) { - const stream_lex_token_t& result(get_token()); - - name_t name = result.first; - if (name == bitwise_rshift_k || name == bitwise_lshift_k) { - name_result = name; - return true; - } - putback(); - return false; -} - -/**************************************************************************************************/ - -// additive_operator = "+" | "-". -bool expression_parser::is_additive_operator(name_t& name_result) { - const stream_lex_token_t& result(get_token()); - - name_t name = result.first; - if (name == add_k || name == subtract_k) { - name_result = name; - return true; - } - putback(); - return false; -} - -/**************************************************************************************************/ - -// multiplicative_operator = "*" | "/" | "%". -bool expression_parser::is_multiplicative_operator(name_t& name_result) { - const stream_lex_token_t& result(get_token()); - - name_t name = result.first; - if (name == multiply_k || name == divide_k || name == modulus_k) { - name_result = name; - return true; - } - putback(); - return false; -} /**************************************************************************************************/ @@ -724,6 +635,7 @@ void expression_parser::putback() { object->token_stream_m.putback(); } void expression_parser::require_token(name_t tokenName, any_regular_t& tokenValue) { const stream_lex_token_t& result(get_token()); if (result.first != tokenName) { + putback(); throw_exception(tokenName, result.first); } @@ -734,10 +646,15 @@ void expression_parser::require_token(name_t tokenName, any_regular_t& tokenValu void expression_parser::require_keyword(name_t keyword_name) { const stream_lex_token_t& result(get_token()); - if (result.first == keyword_k && result.second.cast() == keyword_name) - return; - - throw_exception(keyword_name, result.second.cast()); + if (result.first != keyword_k) { + putback(); + throw_parser_exception(keyword_name.c_str(), token_to_string(result.first), + next_position()); + } + if (result.second.cast() != keyword_name) { + putback(); + throw_exception(keyword_name, result.second.cast()); + } } /**************************************************************************************************/ @@ -747,6 +664,7 @@ void expression_parser::require_token(name_t tokenName) { if (result.first == tokenName) return; + putback(); throw_exception(tokenName, result.first); } diff --git a/source/istream.cpp b/source/istream.cpp index a720cf92..a6b1471d 100644 --- a/source/istream.cpp +++ b/source/istream.cpp @@ -10,6 +10,7 @@ /**************************************************************************************************/ +#include #include #include #include @@ -17,18 +18,17 @@ #include #include -/**************************************************************************************************/ - -namespace adobe { +using namespace std; +using namespace adobe; /**************************************************************************************************/ -std::ostream& operator<<(std::ostream& result, const line_position_t& position) { - typedef std::streampos pos_t; - - const char* current_file(""); +namespace { +auto format_snippet(ostream& out, const line_position_t& position) -> ostream& { std::string line_string(position.file_snippet()); + if (line_string.empty()) + return out; // Replace any tabs with spaces std::replace(line_string.begin(), line_string.end(), '\t', ' '); @@ -36,46 +36,47 @@ std::ostream& operator<<(std::ostream& result, const line_position_t& position) // Count any leading spaces std::string::size_type leading_spaces(line_string.find_first_not_of(' ')); + // Trim leading spaces if (leading_spaces != std::string::npos) line_string.erase(0, leading_spaces); + // Trim trailing nulls (sean-parent: why?) std::string::size_type trailing_nulls(line_string.find_last_not_of((char)0)); if (trailing_nulls != std::string::npos) line_string.erase(trailing_nulls + 1); - // Determine the carret position - pos_t width = (position.position_m == pos_t(-1)) - ? pos_t(std::streamoff(line_string.size())) - : pos_t(position.position_m - position.line_start_m); + // Determine the caret position + auto width = (position.position_m == streampos(-1)) + ? streampos(std::streamoff(line_string.size())) + : streampos(position.position_m - position.line_start_m); width -= std::streamoff(leading_spaces); size_t width_int(static_cast(std::streamoff(width))); // convert to size_t - // If the file name has changed then output it + out << line_string << '\n'; + out << std::setw(width_int) << '^' << '\n'; + return out; +} + +} // namespace - if (position.stream_name() && (std::strcmp(position.stream_name(), current_file) != 0)) { - current_file = position.stream_name(); - result << "File: " << current_file << '\n'; - } +/**************************************************************************************************/ - // By capitalizing Line and keeping char lowercase, it allows things to - // line up in most proportional fonts. By using the good part of the - // line instead of spaces or other character constant, we allow the - // arrows to be under the offending characters, even for a proportional - // font. +namespace adobe { - result << "Line " << std::setw(5) << std::setfill('0') << position.line_number_m; - result << ": " << line_string << "\nchar "; - result << std::setw(5) << std::setfill('0') << width_int; +/**************************************************************************************************/ - // This used to crash when line_string is empty from the start - // putting a more defensive check. - if (line_string.size() > width_int) - line_string.erase(width_int); +std::ostream& operator<<(std::ostream& result, const line_position_t& position) { + if (position.stream_name() && *position.stream_name()) { + result << position.stream_name(); + } else { + result << ""; + } - result << ": " << line_string << "^^^\n"; + result << ':' << position.line_number_m << ':' + << (position.position_m - position.line_start_m + 1); return result; } @@ -88,8 +89,8 @@ line_position_t::line_position_t(adobe::name_t file_path, getline_proc_t getline : line_number_m(line_number), line_start_m(line_start), position_m(position), file_name_m(file_path), getline_proc_m(getline_proc) {} -line_position_t::line_position_t(const char* stream_name, int line_index) - : line_number_m(line_index + 1), line_start_m(0), position_m(-1), +line_position_t::line_position_t(const char* stream_name, int line_number) + : line_number_m(line_number), line_start_m(0), position_m(-1), file_name_m(adobe::name_t(stream_name)) {} #if !defined(ADOBE_NO_DOCUMENTATION) @@ -99,35 +100,36 @@ line_position_t::line_position_t() : line_number_m(1), line_start_m(0), position /**************************************************************************************************/ std::string format_stream_error(const stream_error_t& error) { - std::ostringstream result; + std::ostringstream out; + + if (!error.line_position_set().empty()) { + out << error.line_position_set().front() << ": "; + } - result << error.what() << '\n'; + out << "error: " << error.what() << '\n'; - for (stream_error_t::position_set_t::const_iterator iter(error.line_position_set().begin()), - last(error.line_position_set().end()); + format_snippet(out, error.line_position_set().front()); + + for (auto iter(error.line_position_set().begin() + 1), last(error.line_position_set().end()); iter != last; ++iter) { - result << *iter; + out << *iter << '\n'; + format_snippet(out, *iter); } - return result.str(); + return out.str(); } /**************************************************************************************************/ -std::string format_stream_error(std::istream&, const stream_error_t& error) { - // Format the error - - std::ostringstream result; - - result << error.what() << '\n'; +std::ostream& operator<<(std::ostream& out, const stream_error_t& error) { + out << format_stream_error(error); + return out; +} - for (stream_error_t::position_set_t::const_iterator iter(error.line_position_set().begin()), - last(error.line_position_set().end()); - iter != last; ++iter) { - result << *iter; - } +/**************************************************************************************************/ - return result.str(); +std::string format_stream_error(std::istream&, const stream_error_t& error) { + return format_stream_error(error); } /**************************************************************************************************/ diff --git a/source/lex_stream.cpp b/source/lex_stream.cpp index e0459ebb..3288cb97 100644 --- a/source/lex_stream.cpp +++ b/source/lex_stream.cpp @@ -251,8 +251,8 @@ namespace adobe { /**************************************************************************************************/ -struct lex_stream_t::implementation_t : stream_lex_base_t<2, std::istream_iterator> { - typedef stream_lex_base_t<2, std::istream_iterator> _super; +struct lex_stream_t::implementation_t : stream_lex_base_t<2, std::istreambuf_iterator> { + typedef stream_lex_base_t<2, std::istreambuf_iterator> _super; public: typedef std::istream::pos_type pos_type; @@ -326,9 +326,9 @@ void lex_stream_t::set_comment_bypass(bool bypass) { return object_m->set_commen /**************************************************************************************************/ lex_stream_t::implementation_t::implementation_t(std::istream& in, const line_position_t& position) - : _super(std::istream_iterator(in), std::istream_iterator(), position), + : _super(std::istreambuf_iterator(in), std::istreambuf_iterator(), + position), comment_bypass_m(false) { - in.unsetf(std::ios_base::skipws); _super::set_parse_token_proc( std::bind(&lex_stream_t::implementation_t::parse_token, std::ref(*this), _1)); @@ -479,7 +479,7 @@ bool lex_stream_t::implementation_t::is_comment(char c, stream_lex_token_t& resu { while (true) { if (!_super::get_char(c)) - throw_parser_exception("Unexpected EOF in comment."); + throw_parser_exception("unexpected `eof` in comment."); if (c == '*') { peek_c = _super::peek_char(); @@ -522,7 +522,7 @@ bool lex_stream_t::implementation_t::is_string(char c, stream_lex_token_t& resul } if (c != end_char) - throw_parser_exception("Unexpected EOF in string."); + throw_parser_exception("unexpected `eof` in string."); if (!skip_space(c)) break; @@ -629,13 +629,16 @@ void lex_stream_t::implementation_t::parse_token(char c) { if (!(is_number(c, result) || is_identifier_or_keyword(c, result) || found_comment || is_string(c, result) || is_compound(c, result) || is_simple(c, result))) { - throw_parser_exception("Syntax Error"); + throw_parser_exception("unexpected character `"s + c + "`."); } if (!found_comment || !comment_bypass_m) put_token(std::move(result)); } + +const char* token_name_to_string(name_t token_name); + /**************************************************************************************************/ } // namespace adobe diff --git a/source/parser_shared.cpp b/source/parser_shared.cpp index 30d3250d..cfbed05f 100644 --- a/source/parser_shared.cpp +++ b/source/parser_shared.cpp @@ -25,10 +25,15 @@ void throw_parser_exception(const char* error_string, const line_position_t& pos /**************************************************************************************************/ +void throw_parser_exception(std::string&& error_string, const line_position_t& position) { + throw stream_error_t(std::move(error_string), position); +} + +/**************************************************************************************************/ + void throw_parser_exception(const char* expected, const char* found, const adobe::line_position_t& position) { - throw stream_error_t(string() + "Expected \"" + expected + "\", Found \"" + found + "\"", - position); + throw stream_error_t(string{"expected `"} + expected + "`, found `" + found + "`.", position); } /**************************************************************************************************/ diff --git a/source/token.cpp b/source/token.cpp index e3752d67..949ba21f 100644 --- a/source/token.cpp +++ b/source/token.cpp @@ -6,6 +6,10 @@ /**************************************************************************************************/ #include + +#include +#include +#include #include /**************************************************************************************************/ @@ -14,67 +18,74 @@ namespace adobe { /**************************************************************************************************/ -static_name_t ifelse_k = ".ifelse"_name; -static_name_t number_k = "number"_name; - -static_name_t identifier_k = "identifier"_name; -static_name_t string_k = "string"_name; -static_name_t lead_comment_k = "lead_comment"_name; -static_name_t trail_comment_k = "trail_comment"_name; - -static_name_t semicolon_k = "semicolon"_name; -static_name_t comma_k = "comma"_name; -static_name_t assign_k = "assign"_name; -static_name_t question_k = "question"_name; -static_name_t colon_k = "colon"_name; -static_name_t open_brace_k = "open_brace"_name; -static_name_t close_brace_k = "close_brace"_name; -static_name_t open_parenthesis_k = "open_parenthesis"_name; -static_name_t close_parenthesis_k = "close_parenthesis"_name; -static_name_t dot_k = "dot"_name; -static_name_t open_bracket_k = "open_bracket"_name; -static_name_t close_bracket_k = "close_bracket"_name; -static_name_t at_k = ".at"_name; -static_name_t is_k = ".is"_name; -static_name_t to_k = ".to"_name; - -static_name_t add_k = ".add"_name; -static_name_t subtract_k = ".subtract"_name; -static_name_t multiply_k = ".multiply"_name; -static_name_t divide_k = ".divide"_name; -static_name_t modulus_k = ".modulus"_name; - -static_name_t not_k = ".not"_name; -static_name_t unary_negate_k = ".unary_negate"_name; - -static_name_t less_k = ".less"_name; -static_name_t greater_k = ".greater"_name; - -static_name_t and_k = ".and"_name; -static_name_t or_k = ".or"_name; -static_name_t less_equal_k = ".less_equal"_name; -static_name_t greater_equal_k = ".greater_equal"_name; -static_name_t not_equal_k = ".not_equal"_name; -static_name_t equal_k = ".equal"_name; - -static_name_t keyword_k = "keyword"_name; - -static_name_t empty_k = "empty"_name; -static_name_t true_k = "true"_name; -static_name_t false_k = "false"_name; - -static_name_t function_k = ".function"_name; -static_name_t variable_k = ".variable"_name; -static_name_t index_k = ".index"_name; -static_name_t array_k = ".array"_name; -static_name_t dictionary_k = ".dictionary"_name; - -static_name_t bitwise_and_k = ".bitwise_and"_name; -static_name_t bitwise_xor_k = ".bitwise_xor"_name; -static_name_t bitwise_or_k = ".bitwise_or"_name; -static_name_t bitwise_rshift_k = ".bitwise_rshift"_name; -static_name_t bitwise_lshift_k = ".bitwise_lshift"_name; -static_name_t bitwise_negate_k = ".bitwise_negate"_name; +auto token_to_string(name_t token) -> const char* { + using table_t = std::pair; + constexpr table_t token_table[] = {// Operators + {add_k, "+"}, + {and_k, "&&"}, + {array_k, "array"}, + {at_k, "@"}, + {bitwise_and_k, "&"}, + {bitwise_lshift_k, "<<"}, + {bitwise_negate_k, "~"}, + {bitwise_or_k, "|"}, + {bitwise_rshift_k, ">>"}, + {bitwise_xor_k, "^"}, + {dictionary_k, "dictionary"}, + {divide_k, "/"}, + {equal_k, "=="}, + {function_k, "function"}, + {greater_equal_k, ">="}, + {greater_k, ">"}, + {ifelse_k, "?:"}, + {index_k, "[]"}, + {is_k, "<=="}, + {less_equal_k, "<="}, + {less_k, "<"}, + {modulus_k, "%"}, + {multiply_k, "*"}, + {not_equal_k, "!="}, + {not_k, "!"}, + {or_k, "||"}, + {subtract_k, "-"}, + {to_k, "->"}, + {unary_negate_k, "-"}, + {variable_k, "variable"}, + // Tokens + {assign_k, "="}, + {close_brace_k, "}"}, + {close_bracket_k, "]"}, + {close_parenthesis_k, ")"}, + {colon_k, ":"}, + {comma_k, ","}, + {dot_k, "."}, + {empty_k, "empty"}, + {false_k, "false"}, + {identifier_k, "identifier"}, + {keyword_k, "keyword"}, + {lead_comment_k, "lead_comment"}, + {number_k, "number"}, + {open_brace_k, "{"}, + {open_bracket_k, "["}, + {open_parenthesis_k, "("}, + {question_k, "?"}, + {semicolon_k, ";"}, + {string_k, "string"}, + {trail_comment_k, "trail_comment"}, + {true_k, "true"}}; + +#ifndef NDEBUG + static int x = [&] { + ADOBE_ASSERT(is_sorted(token_table, less{}, &table_t::first) && + "token_table must be sorted"); + return 0; + }(); +#endif + + auto p = binary_search(token_table, token, less{}, &table_t::first); + ADOBE_ASSERT(p != std::end(token_table)); + return p->second; +} /**************************************************************************************************/ diff --git a/source/virtual_machine.cpp b/source/virtual_machine.cpp index 028a986b..9f3c5e6a 100644 --- a/source/virtual_machine.cpp +++ b/source/virtual_machine.cpp @@ -5,18 +5,16 @@ */ /**************************************************************************************************/ -#include - +#include #include #include +#include #include #include #include #include #include -#include - #include #include #include @@ -33,14 +31,11 @@ #include #include -#ifndef NDEBUG -#include -#endif - /**************************************************************************************************/ using namespace std; using namespace std::placeholders; +using namespace adobe; /**************************************************************************************************/ @@ -50,10 +45,10 @@ namespace adobe { template struct static_table_traits { - typedef bool result_type; - typedef const std::type_info* key_type; - typedef ValueType value_type; - typedef std::pair entry_type; + using result_type = bool; + using key_type = const std::type_info*; + using value_type = ValueType; + using entry_type = std::pair; result_type operator()(const entry_type& x, const entry_type& y) const { return (*this)(x, y.first); @@ -84,25 +79,25 @@ namespace { using namespace adobe::literals; -typedef void (adobe::virtual_machine_t::implementation_t::*operator_t)(); +using stack_type = vector; // REVISIT (sparent) : GCC 3.1 the symbol `stack_t` + // conflicts with a symbol in signal.h +using operator_t = void (adobe::virtual_machine_t::implementation_t::*)(); using array_function_t = std::function; using dictionary_function_t = std::function; -typedef vector stack_type; // REVISIT (sparent) : GCC 3.1 the symbol stack_t -// conflicts with a symbol in signal.h - #if !defined(ADOBE_NO_DOCUMENTATION) -typedef adobe::static_table operator_table_t; -typedef adobe::static_table array_function_table_t; -typedef adobe::static_table dictionary_function_table_t; -typedef adobe::static_table type_table_t; + +using operator_table_t = adobe::static_table; +using variable_table_t = adobe::static_table; +using type_table_t = adobe::static_table; + #endif // !defined(ADOBE_NO_DOCUMENTATION) /**************************************************************************************************/ template struct make { - typedef Result result_type; + using result_type = Result; template Result operator()(const T& x) { @@ -112,45 +107,33 @@ struct make { /**************************************************************************************************/ -static type_table_t* type_table_g; - -/**************************************************************************************************/ - -void get_type_name_init_() { - static type_table_t type_table_s = { - {type_table_t::entry_type(&typeid(double), "number"_name), - type_table_t::entry_type(&typeid(bool), "boolean"_name), - type_table_t::entry_type(&typeid(adobe::empty_t), "empty"_name), - type_table_t::entry_type(&typeid(string), "string"_name), - type_table_t::entry_type(&typeid(adobe::array_t), "array"_name), - type_table_t::entry_type(&typeid(adobe::dictionary_t), "dictionary"_name), - type_table_t::entry_type(&typeid(adobe::name_t), "name"_name)}}; +name_t known_type_name(const std::type_info& type) { + static type_table_t type_table = [] { + type_table_t result = { + {type_table_t::entry_type(&typeid(double), "number"_name), + type_table_t::entry_type(&typeid(bool), "boolean"_name), + type_table_t::entry_type(&typeid(adobe::empty_t), "empty"_name), + type_table_t::entry_type(&typeid(string), "string"_name), + type_table_t::entry_type(&typeid(adobe::array_t), "array"_name), + type_table_t::entry_type(&typeid(adobe::dictionary_t), "dictionary"_name), + type_table_t::entry_type(&typeid(adobe::function_t), "function"_name), + type_table_t::entry_type(&typeid(adobe::name_t), "name"_name)}}; - type_table_s.sort(); - - type_table_g = &type_table_s; -} - -/**************************************************************************************************/ - -once_flag get_type_name_flag; -void get_type_name_init() { call_once(get_type_name_flag, &get_type_name_init_); } - -/**************************************************************************************************/ - -adobe::name_t get_type_name(const adobe::any_regular_t& val) { - get_type_name_init(); + result.sort(); + return result; + }(); adobe::name_t result; - - (*type_table_g)(&val.type_info(), result); - - if (!result) - result = "unknown"_name; - + type_table(&type, result); return result; } +adobe::name_t type_name(const adobe::any_regular_t& val) { + if (name_t result = known_type_name(val.type_info()); result) + return result; + return name_t{val.type_info().name()}; +} + /**************************************************************************************************/ #if 0 @@ -159,74 +142,81 @@ adobe::name_t get_type_name(const adobe::any_regular_t& val) { /**************************************************************************************************/ -adobe::any_regular_t xml_escape_function(const adobe::array_t& parameters) { +adobe::any_regular_t xml_escape_function(const adobe::any_regular_t& arg) { + const auto& parameters{cast(arg)}; if (parameters.size() != 1 || parameters[0].type_info() != typeid(string)) throw std::runtime_error("xml_escape: parameter error"); - return adobe::any_regular_t(adobe::entity_escape(parameters[0].cast())); + return adobe::any_regular_t(adobe::entity_escape(cast(parameters[0]))); } /**************************************************************************************************/ -adobe::any_regular_t xml_unescape_function(const adobe::array_t& parameters) { +adobe::any_regular_t xml_unescape_function(const adobe::any_regular_t& arg) { + const auto& parameters{cast(arg)}; if (parameters.size() != 1 || parameters[0].type_info() != typeid(string)) throw std::runtime_error("xml_unescape: parameter error"); - return adobe::any_regular_t(adobe::entity_unescape(parameters[0].cast())); + return adobe::any_regular_t(adobe::entity_unescape(cast(parameters[0]))); } /**************************************************************************************************/ -adobe::any_regular_t localize_function(const adobe::array_t& parameters) { +adobe::any_regular_t localize_function(const adobe::any_regular_t& arg) { + const auto& parameters{cast(arg)}; if (parameters.size() != 1) throw std::runtime_error("localize: parameter error"); return adobe::any_regular_t( adobe::localization_ready() - ? adobe::localization_invoke(parameters.front().cast()) - : parameters.front().cast()); + ? adobe::localization_invoke(cast(parameters.front())) + : cast(parameters.front())); } /**************************************************************************************************/ -adobe::any_regular_t round_function(const adobe::array_t& parameters) { +adobe::any_regular_t round_function(const adobe::any_regular_t& arg) { + const auto& parameters{cast(arg)}; if (parameters.size() == 0) throw std::runtime_error("round: parameter error"); - return adobe::any_regular_t(adobe::round(parameters.front().cast())); + return adobe::any_regular_t(adobe::round(cast(parameters.front()))); } /**************************************************************************************************/ -adobe::any_regular_t min_function(const adobe::array_t& parameters) { +adobe::any_regular_t min_function(const adobe::any_regular_t& arg) { + const auto& parameters{cast(arg)}; if (parameters.size() == 0) throw std::runtime_error("min: parameter error"); - return *adobe::min_element( - parameters, - std::bind(std::less(), std::bind(adobe::any_regular_t::transform(), _1), - std::bind(adobe::any_regular_t::transform(), _2))); + return *adobe::min_element(parameters, + std::bind(std::less(), + std::bind(adobe::any_regular_t::transform(), _1), + std::bind(adobe::any_regular_t::transform(), _2))); } /**************************************************************************************************/ -adobe::any_regular_t max_function(const adobe::array_t& parameters) { +adobe::any_regular_t max_function(const adobe::any_regular_t& arg) { + const auto& parameters{cast(arg)}; if (parameters.size() == 0) throw std::runtime_error("max: parameter error"); - return *adobe::max_element( - parameters, - std::bind(std::less(), std::bind(adobe::any_regular_t::transform(), _1), - std::bind(adobe::any_regular_t::transform(), _2))); + return *adobe::max_element(parameters, + std::bind(std::less(), + std::bind(adobe::any_regular_t::transform(), _1), + std::bind(adobe::any_regular_t::transform(), _2))); } /**************************************************************************************************/ -adobe::any_regular_t typeof_function(const adobe::array_t& parameters) { +adobe::any_regular_t typeof_function(const adobe::any_regular_t& arg) { + const auto& parameters{cast(arg)}; if (parameters.size() == 0) throw std::runtime_error("typeof: parameter error"); - return adobe::any_regular_t(get_type_name(parameters.front())); + return adobe::any_regular_t(type_name(parameters.front())); } /**************************************************************************************************/ @@ -237,7 +227,8 @@ adobe::any_regular_t typeof_function(const adobe::array_t& parameters) { /**************************************************************************************************/ -adobe::any_regular_t scale_function(const adobe::dictionary_t& parameters) { +adobe::any_regular_t scale_function(const adobe::any_regular_t& arg) { + const auto& parameters{cast(arg)}; double m(1.0); double x(0.0); double b(0.0); @@ -257,9 +248,14 @@ adobe::any_regular_t scale_function(const adobe::dictionary_t& parameters) { /**************************************************************************************************/ -void throw_function_not_defined(adobe::name_t function_name) { +[[noreturn]] void throw_function_not_defined(adobe::name_t function_name) { + throw std::logic_error( + adobe::make_string("function \'", function_name.c_str(), "\' not defined.")); +} + +[[noreturn]] void throw_variable_not_defined(adobe::name_t variable_name) { throw std::logic_error( - adobe::make_string("Function \'", function_name.c_str(), "\' not defined.")); + adobe::make_string("variable \'", variable_name.c_str(), "\' not defined.")); } /**************************************************************************************************/ @@ -271,7 +267,9 @@ void throw_function_not_defined(adobe::name_t function_name) { /**************************************************************************************************/ struct bitwise_and_t { - inline std::uint32_t operator()(std::uint32_t x, std::uint32_t y) const { return x & y; } + inline std::uint32_t operator()(std::uint32_t x, std::uint32_t y) const { + return x & y; + } // namespace }; /**************************************************************************************************/ @@ -314,8 +312,16 @@ namespace adobe { /**************************************************************************************************/ +const char* type_name(const std::type_info& type) { + if (name_t result = known_type_name(type); result) + return result.c_str(); + return type.name(); +} + +/**************************************************************************************************/ + class virtual_machine_t::implementation_t { - typedef std::map binary_op_override_map_t; + using binary_op_override_map_t = std::map; public: implementation_t(); @@ -326,15 +332,41 @@ class virtual_machine_t::implementation_t { any_regular_t& back(); void pop_back(); + template + auto pop_as() -> T { + if constexpr (std::is_same_v) { + auto result = std::move(back()); + pop_back(); + return result; + } else { + auto result = std::move(cast(back())); + pop_back(); + return result; + } + } + + + vector variable_scope_m; + + void push_scope(variable_scope_t&& scope) { variable_scope_m.push_back(std::move(scope)); } + variable_lookup_t variable_lookup_m; - array_function_lookup_t array_function_lookup_m; - dictionary_function_lookup_t dictionary_function_lookup_m; named_index_lookup_t named_index_lookup_m; numeric_index_lookup_t numeric_index_lookup_m; // override maps binary_op_override_map_t binary_op_override_map_m; + any_regular_t variable_lookup(name_t name) const { + + for (auto iter = variable_scope_m.rbegin(); iter != variable_scope_m.rend(); ++iter) { + if (auto result = (*iter)(name)) + return *result; + } + + throw_variable_not_defined(name); + } + private: stack_type value_stack_m; @@ -368,15 +400,13 @@ class virtual_machine_t::implementation_t { void dictionary_operator(); static operator_table_t* operator_table_g; - static array_function_table_t* array_function_table_g; - static dictionary_function_table_t* dictionary_function_table_g; + static variable_table_t* variable_table_g; }; /**************************************************************************************************/ operator_table_t* virtual_machine_t::implementation_t::operator_table_g; -array_function_table_t* virtual_machine_t::implementation_t::array_function_table_g; -dictionary_function_table_t* virtual_machine_t::implementation_t::dictionary_function_table_g; +variable_table_t* virtual_machine_t::implementation_t::variable_table_g; /**************************************************************************************************/ @@ -395,8 +425,8 @@ namespace { /**************************************************************************************************/ void virtual_machine_init_() { - typedef operator_table_t::entry_type op_entry_type; - typedef adobe::virtual_machine_t::implementation_t implementation_t; + using op_entry_type = operator_table_t::entry_type; + using implementation_t = adobe::virtual_machine_t::implementation_t; static operator_table_t operator_table_s = { {op_entry_type(adobe::not_k, &implementation_t::unary_operator), @@ -439,26 +469,33 @@ void virtual_machine_init_() { op_entry_type(adobe::bitwise_negate_k, &implementation_t::bitwise_unary_operator)}}; - static array_function_table_t array_function_table_s = { - {array_function_table_t::entry_type("typeof"_name, &typeof_function), - array_function_table_t::entry_type("min"_name, &min_function), - array_function_table_t::entry_type("max"_name, &max_function), - array_function_table_t::entry_type("round"_name, &round_function), - array_function_table_t::entry_type("localize"_name, &localize_function), - array_function_table_t::entry_type("xml_escape"_name, &xml_escape_function), - array_function_table_t::entry_type("xml_unescape"_name, &xml_unescape_function)}}; - static dictionary_function_table_t dictionary_function_table_s = { - {dictionary_function_table_t::entry_type("scale"_name, &scale_function)}}; + static variable_table_t variable_table_s = { + {variable_table_t::entry_type("typeof"_name, function_t{&typeof_function}), + variable_table_t::entry_type("min"_name, function_t{&min_function}), + variable_table_t::entry_type("max"_name, function_t{&max_function}), + variable_table_t::entry_type( + "round"_name, make_function((double (*)(double))std::round)), + variable_table_t::entry_type("localize"_name, function_t{&localize_function}), + variable_table_t::entry_type("xml_escape"_name, function_t{&xml_escape_function}), + variable_table_t::entry_type("xml_unescape"_name, function_t{&xml_unescape_function}), + variable_table_t::entry_type("scale"_name, function_t{&scale_function}), + variable_table_t::entry_type( + "std"_name, dictionary_t{{"typeof"_name, function_t{&typeof_function}}, + {"min"_name, function_t{&min_function}}, + {"max"_name, function_t{&max_function}}, + {"round"_name, function_t{&round_function}}, + {"localize"_name, function_t{&localize_function}}, + {"xml_escape"_name, function_t{&xml_escape_function}}, + {"xml_unescape"_name, function_t{&xml_unescape_function}}, + {"scale"_name, function_t{&scale_function}}})}}; + operator_table_s.sort(); - array_function_table_s.sort(); - dictionary_function_table_s.sort(); + variable_table_s.sort(); adobe::virtual_machine_t::implementation_t::operator_table_g = &operator_table_s; - adobe::virtual_machine_t::implementation_t::array_function_table_g = &array_function_table_s; - adobe::virtual_machine_t::implementation_t::dictionary_function_table_g = - &dictionary_function_table_s; + adobe::virtual_machine_t::implementation_t::variable_table_g = &variable_table_s; } /**************************************************************************************************/ @@ -482,21 +519,54 @@ namespace adobe { /**************************************************************************************************/ -virtual_machine_t::implementation_t::implementation_t() { virtual_machine_init(); } +virtual_machine_t::implementation_t::implementation_t() { + virtual_machine_init(); + + push_scope([this](name_t name) -> optional { + // This section handles the legacy lookup mechanism. The old callback simply failed if not + // found. + + if (any_regular_t value; (*variable_table_g)(name, value)) { + // legacy functions sorted by name + constexpr static_name_t legacy_functions_s[]{ + "localize"_name, "max"_name, "min"_name, "round"_name, + "scale"_name, "typeof"_name, "xml_escape"_name, "xml_unescape"_name}; + + // todo: (sean-parent) This should report the source file/line/character when that + // information is integrated into the virtual machine. + if (binary_search(legacy_functions_s, name, less{}, make{}) != + std::end(legacy_functions_s)) { + clog << "warning: deprecated legacy function `" << name + << "` may hide a variable.\n"; + clog << "note: use `std." << name << "` for the function or rename the variable.\n"; + } + return value; + } + + + // The legacy variable lookup function must be last. It will throw if the variable is not + // found. Because the throw may have terminated a evaluation within a sheet, we cannot + // squelch the error and continue. + if (variable_lookup_m) { + return variable_lookup_m(name); + } + return nullopt; + }); +} /**************************************************************************************************/ void virtual_machine_t::implementation_t::evaluate(const array_t& expression) { - for (expression_t::const_iterator iter(expression.begin()); iter != expression.end(); ++iter) { + for (const auto& e : expression) { adobe::name_t op_name; - iter->cast(op_name); + e.cast(op_name); if (op_name && op_name.c_str()[0] == '.') { if (!operator_override(op_name)) ((*this).*(find_operator(op_name)))(); } else { - value_stack_m.push_back(*iter); + value_stack_m.push_back(e); } } } @@ -533,8 +603,8 @@ operator_t virtual_machine_t::implementation_t::find_operator(adobe::name_t oper template