diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddfcf..000000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.clang-format b/.clang-format index d912fdc66..80f4f718b 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,5 @@ --- -BasedOnStyle: Google +BasedOnStyle: Google AccessModifierOffset: -2 ConstructorInitializerIndentWidth: 2 AlignEscapedNewlinesLeft: false @@ -8,15 +8,13 @@ AllowAllParametersOfDeclarationOnNextLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AllowShortFunctionsOnASingleLine: None -AllowShortLoopsOnASingleLine: false AlwaysBreakTemplateDeclarations: true AlwaysBreakBeforeMultilineStrings: false BreakBeforeBinaryOperators: false BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: AfterColon +BreakConstructorInitializers: BeforeComma BinPackParameters: true -ColumnLimit: 90 +ColumnLimit: 90 ConstructorInitializerAllOnOneLineOrOnePerLine: true DerivePointerBinding: false PointerBindsToType: true @@ -31,19 +29,20 @@ PenaltyBreakString: 1 PenaltyBreakFirstLessLess: 1000 PenaltyExcessCharacter: 1000 PenaltyReturnTypeOnItsOwnLine: 90 -SpacesBeforeTrailingComments: 3 -Cpp11BracedListStyle: true -Standard: Auto -IndentWidth: 2 -TabWidth: 2 -UseTab: Never +SpacesBeforeTrailingComments: 2 +Cpp11BracedListStyle: false +Standard: Auto +IndentWidth: 2 +TabWidth: 2 +UseTab: Never IndentFunctionDeclarationAfterType: false SpacesInParentheses: false -SpacesInAngles: false +SpacesInAngles: false SpaceInEmptyParentheses: false SpacesInCStyleCastParentheses: false SpaceAfterControlStatementKeyword: true SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: Never ContinuationIndentWidth: 4 SortIncludes: false SpaceAfterCStyleCast: false @@ -54,17 +53,16 @@ BreakBeforeBraces: Custom # Control of individual brace wrapping cases BraceWrapping: { - AfterClass: 'true' - AfterControlStatement: 'true' - AfterEnum : 'true' - AfterFunction : 'true' - AfterNamespace : 'true' - AfterStruct : 'true' - AfterUnion : 'true' - BeforeCatch : 'true' - BeforeElse : 'true' - IndentBraces : 'false' + AfterClass: 'true', + AfterControlStatement: 'true', + AfterEnum : 'true', + AfterFunction : 'true', + AfterNamespace : 'true', + AfterStruct : 'true', + AfterUnion : 'true', + BeforeCatch : 'true', + BeforeElse : 'true', + IndentBraces : 'false', SplitEmptyFunction: 'false' } ... - diff --git a/.codespell_ignore_words b/.codespell_ignore_words new file mode 100644 index 000000000..ab09b3c2f --- /dev/null +++ b/.codespell_ignore_words @@ -0,0 +1,7 @@ +INOUT +InOut +delimeter +Succesful +worl +valu +Exeption diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..9eb010608 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,19 @@ +--- +name: Bug report +about: Help me help you... +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +If you are experiencing a crash, provide a backtrace (GDB or similar). + +*How to Reproduce** + +Please provide a specific description of how to reproduce the issue or source code that can be compiled and executed. Please attach a file/project that is easy to compile, don't copy and paste code snippets! + +Even better, create a Pull Request with a failing unit test. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..021458556 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..27c6ae66a --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ + diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake_ubuntu.yml similarity index 53% rename from .github/workflows/cmake.yml rename to .github/workflows/cmake_ubuntu.yml index d192e3515..41ed9a196 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake_ubuntu.yml @@ -1,6 +1,11 @@ -name: cmake +name: cmake Ubuntu -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) @@ -15,38 +20,40 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-22.04] steps: - uses: actions/checkout@v2 - - - name: Install Dependencies (Linux) - run: sudo apt-get install libboost-dev libzmq3-dev - if: matrix.os == 'ubuntu-latest' + + - name: Install Conan + id: conan + uses: turtlebrowser/get-conan@main + + - name: Create default profile + run: conan profile detect - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory # We'll use this as our working directory for all subsequent commands run: cmake -E make_directory ${{github.workspace}}/build + - name: Install conan dependencies + working-directory: ${{github.workspace}}/build + run: conan install ${{github.workspace}}/conanfile.txt -s build_type=${{env.BUILD_TYPE}} --build=missing + - name: Configure CMake - # Use a bash shell so we can use the same syntax for environment variable - # access regardless of the host operating system shell: bash working-directory: ${{github.workspace}}/build - # Note the current convention is to use the -S and -B options here to specify source - # and build directories, but this is only available with CMake 3.13 and higher. - # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON + run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake - name: Build - working-directory: ${{github.workspace}}/build shell: bash - # Execute the build. You can specify a specific target with "--target " - run: cmake --build . --config $BUILD_TYPE - - - name: run test (Linux) - if: matrix.os == 'ubuntu-latest' working-directory: ${{github.workspace}}/build - run: ./tests/behaviortree_cpp_test - + run: cmake --build . --config ${{env.BUILD_TYPE}} + + - name: run test (Linux) + working-directory: ${{github.workspace}}/build/tests + run: ctest + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 diff --git a/.github/workflows/cmake_windows.yml b/.github/workflows/cmake_windows.yml new file mode 100644 index 000000000..34f4f97ce --- /dev/null +++ b/.github/workflows/cmake_windows.yml @@ -0,0 +1,56 @@ +name: cmake Windows + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest] + + steps: + - uses: actions/checkout@v4 + + - name: Install Conan + id: conan + uses: turtlebrowser/get-conan@main + + - name: Create default profile + run: conan profile detect + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Install conan dependencies + working-directory: ${{github.workspace}}/build + run: conan install ${{github.workspace}}/conanfile.txt -s build_type=${{env.BUILD_TYPE}} --build=missing + + - name: Configure CMake + shell: bash + working-directory: ${{github.workspace}}/build + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake + + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + run: cmake --build . --config ${{env.BUILD_TYPE}} + + - name: run test (Windows) + working-directory: ${{github.workspace}}/build + run: $env:PATH+=";${{env.BUILD_TYPE}}"; tests/${{env.BUILD_TYPE}}/behaviortree_cpp_test.exe diff --git a/.github/workflows/codeql.yml.bkp b/.github/workflows/codeql.yml.bkp new file mode 100644 index 000000000..a76a623ca --- /dev/null +++ b/.github/workflows/codeql.yml.bkp @@ -0,0 +1,63 @@ +name: "CodeQL" + +on: + push: + branches: [ 'master' ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ 'master' ] + schedule: + - cron: '36 19 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + queries: +security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/doxygen-gh-pages.yml b/.github/workflows/doxygen-gh-pages.yml new file mode 100644 index 000000000..b8fb8f46b --- /dev/null +++ b/.github/workflows/doxygen-gh-pages.yml @@ -0,0 +1,18 @@ +name: Doxygen GitHub Pages Deploy Action + +on: + push: + branches: + - main + - master + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: DenverCoder1/doxygen-github-pages-action@v2.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + folder: doc/html diff --git a/.github/workflows/pixi.yaml b/.github/workflows/pixi.yaml new file mode 100644 index 000000000..ddd1cbfb8 --- /dev/null +++ b/.github/workflows/pixi.yaml @@ -0,0 +1,27 @@ +name: Pixi (conda) + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + pixi_conda_build: + strategy: + matrix: + os: + - windows-latest + - ubuntu-latest + runs-on: ${{ matrix.os }} + steps: + # Pixi is the tool used to create/manage conda environment + - uses: actions/checkout@v3 + - uses: prefix-dev/setup-pixi@v0.8.1 + with: + pixi-version: v0.40.3 + - name: Build + run: pixi run build + - name: Run tests + run: pixi run test diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 000000000..ee7fa9229 --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,16 @@ +name: pre-commit + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/ros1.yaml b/.github/workflows/ros1.yaml deleted file mode 100644 index 9d244f60f..000000000 --- a/.github/workflows/ros1.yaml +++ /dev/null @@ -1,17 +0,0 @@ -name: ros1 - -on: [push, pull_request] - -jobs: - industrial_ci: - strategy: - matrix: - env: - - {ROS_DISTRO: noetic, ROS_REPO: main} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: 'ros-industrial/industrial_ci@master' - env: ${{matrix.env}} - with: - package-name: behaviortree_cpp diff --git a/.github/workflows/ros2-rolling.yaml b/.github/workflows/ros2-rolling.yaml new file mode 100644 index 000000000..446c49879 --- /dev/null +++ b/.github/workflows/ros2-rolling.yaml @@ -0,0 +1,22 @@ +name: ros2-rolling + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + industrial_ci: + strategy: + matrix: + env: + - {ROS_DISTRO: rolling, ROS_REPO: main} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: 'ros-industrial/industrial_ci@master' + env: ${{matrix.env}} + with: + package-name: plotjuggler diff --git a/.github/workflows/ros2.yaml b/.github/workflows/ros2.yaml index cfe066d87..099cc04f2 100644 --- a/.github/workflows/ros2.yaml +++ b/.github/workflows/ros2.yaml @@ -1,20 +1,23 @@ name: ros2 -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] jobs: industrial_ci: strategy: matrix: env: - - {ROS_DISTRO: foxy, ROS_REPO: main} - - {ROS_DISTRO: galactic, ROS_REPO: main} - {ROS_DISTRO: humble, ROS_REPO: main} - - {ROS_DISTRO: rolling, ROS_REPO: main} + - {ROS_DISTRO: jazzy, ROS_REPO: main} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - uses: 'ros-industrial/industrial_ci@master' env: ${{matrix.env}} with: - package-name: behaviortree_cpp + package-name: plotjuggler diff --git a/.github/workflows/sonarcube.yml.bkp b/.github/workflows/sonarcube.yml.bkp new file mode 100644 index 000000000..926306125 --- /dev/null +++ b/.github/workflows/sonarcube.yml.bkp @@ -0,0 +1,42 @@ +name: Sonarcube Scan + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + name: Build + runs-on: ubuntu-latest + env: + BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Install Build Wrapper + uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v4 + + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install -y libzmq3-dev libsqlite3-dev + + - name: Install googletest + uses: Bacondish2023/setup-googletest@v1 + + - name: Run Build Wrapper + run: | + mkdir build + cmake -S . -B build + build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Release + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@v4 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Put the name of your token here + with: + args: > + --define sonar.cfamily.compile-commands="${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json" diff --git a/.gitignore b/.gitignore index 1dd3ab710..9d5bd4326 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,18 @@ build* site/* /.vscode/ +.vs/ # clangd cache /.cache/* +CMakeSettings.json + +# OSX junk +.DS_Store + +# pixi environments +.pixi + +CMakeUserPresets.json + +tags diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..d491f36d9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,54 @@ + +# To use: +# +# pre-commit run -a +# +# Or: +# +# pre-commit install # (runs every time you commit in git) +# +# To update this file: +# +# pre-commit autoupdate +# +# See https://github.com/pre-commit/pre-commit + +exclude: ^3rdparty/|3rdparty|^include/behaviortree_cpp/contrib/ +repos: + + # Standard hooks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-added-large-files + - id: check-ast + - id: check-case-conflict + - id: check-docstring-first + - id: check-merge-conflict + - id: check-symlinks + - id: check-xml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + exclude_types: [svg] + - id: mixed-line-ending + - id: trailing-whitespace + exclude_types: [svg] + - id: fix-byte-order-marker + + # CPP hooks + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v17.0.6 + hooks: + - id: clang-format + args: ['-fallback-style=none', '-i'] + + # Spell check + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + hooks: + - id: codespell + additional_dependencies: + - tomli + args: + [--toml=./pyproject.toml] diff --git a/3rdparty/cpp-sqlite/README.md b/3rdparty/cpp-sqlite/README.md new file mode 100644 index 000000000..913354947 --- /dev/null +++ b/3rdparty/cpp-sqlite/README.md @@ -0,0 +1,37 @@ +## Single file header only sqlite wrapper for C++ + +## Example +```cpp +#include "sqlite.hpp" +#include + +int main() +{ + sqlite::Connection connection("example.db"); + + sqlite::Statement(connection, "CREATE TABLE IF NOT EXISTS exampleTable (" + "textData TEXT, " + "intData INTEGER, " + "floatData REAL)"); + + sqlite::Statement(connection, + "INSERT INTO exampleTable VALUES (?, ?, ?)", + "Hello world", + 1234, + 5.6789); + + sqlite::Result res = sqlite::Query(connection, "SELECT * FROM exampleTable"); + + while(res.Next()) + { + std::string textData = res.Get(0); + int intData = res.Get(1); + float floatData = res.Get(2); + + std::cout << textData << " " << intData << " " << floatData << std::endl; + } + + return 0; +} + +``` diff --git a/3rdparty/cpp-sqlite/sqlite.hpp b/3rdparty/cpp-sqlite/sqlite.hpp new file mode 100644 index 000000000..c512d891c --- /dev/null +++ b/3rdparty/cpp-sqlite/sqlite.hpp @@ -0,0 +1,603 @@ +/** + Copyright (C) 2023 Toni Lipponen + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +#if __cplusplus > 201402L + #define CPP_SQLITE_NODISCARD [[nodiscard]] +#else + #define CPP_SQLITE_NODISCARD +#endif + +#if defined(CPP_SQLITE_NOTHROW) + #define CPP_SQLITE_THROW(...) return false +#else + #define CPP_SQLITE_THROW(...) throw sqlite::Error(__VA_ARGS__) +#endif + +namespace sqlite +{ + class Error : public std::runtime_error + { + public: + explicit Error(const char* message, int errorCode = SQLITE_ERROR) + : std::runtime_error(message), m_errorCode(errorCode) + { + + } + + explicit Error(const std::string& message, int errorCode = SQLITE_ERROR) + : std::runtime_error(message), m_errorCode(errorCode) + { + + } + + CPP_SQLITE_NODISCARD + int GetCode() const + { + return m_errorCode; + } + + private: + int m_errorCode; + }; + + namespace Priv + { + inline bool CheckError(sqlite3* db, int code) + { + if(code != SQLITE_OK && code != SQLITE_DONE) + { + const int extendedCode = sqlite3_extended_errcode(db); + std::string errstr = sqlite3_errstr(extendedCode); + std::string errmsg = sqlite3_errmsg(db); + + CPP_SQLITE_THROW(errstr + ": " + errmsg, extendedCode); + } + + return true; + } + + inline bool CheckError(int code) + { + if(code != SQLITE_OK && code != SQLITE_DONE) + { + std::string errstr = std::string("SQL error: ") + sqlite3_errstr(code); + CPP_SQLITE_THROW(errstr, code); + } + + return true; + } + } + + class Connection + { + public: + Connection() : m_connection(nullptr) {} + + explicit Connection(const std::string& filename) + { + this->Open(filename); + } + + Connection(const Connection&) = delete; + + Connection(Connection&& other) noexcept + { + this->m_connection = other.m_connection; + other.m_connection = nullptr; + } + + virtual ~Connection() noexcept + { + try + { + this->Close(); + } + catch(...) + { + + } + } + + Connection& operator=(const Connection&) = delete; + + Connection& operator=(Connection&& other) noexcept + { + if(&other != this) + { + this->m_connection = other.m_connection; + other.m_connection = nullptr; + } + + return *this; + } + + bool Open(const std::string& filename) + { + return sqlite::Priv::CheckError(sqlite3_open(filename.data(), &m_connection)); + } + + bool Close() + { + const auto result = Priv::CheckError(sqlite3_close(m_connection)); + m_connection = nullptr; + + return result; + } + + CPP_SQLITE_NODISCARD + int GetExtendedResult() const + { + return sqlite3_extended_errcode(m_connection); + } + + CPP_SQLITE_NODISCARD + sqlite3* GetPtr() + { + return m_connection; + } + + private: + sqlite3* m_connection = nullptr; + }; + + class Blob + { + public: + Blob(const void* data, int32_t bytes) + { + m_data.resize(bytes); + std::memcpy(&m_data.at(0), data, bytes); + } + + explicit Blob(std::vector data) + : m_data(std::move(data)) + { + + } + + CPP_SQLITE_NODISCARD + uint32_t GetSize() const + { + return m_data.size(); + } + + CPP_SQLITE_NODISCARD + unsigned char* GetData() + { + return m_data.data(); + } + + CPP_SQLITE_NODISCARD + const unsigned char* GetData() const + { + return m_data.data(); + } + + private: + std::vector m_data; + }; + + /** Non-owning blob*/ + class NOBlob + { + public: + NOBlob(const void* ptr, uint32_t bytes) + : m_ptr(ptr), m_bytes(bytes) + { + + } + + CPP_SQLITE_NODISCARD + uint32_t GetSize() const + { + return m_bytes; + } + + const void* GetData() + { + return m_ptr; + } + + CPP_SQLITE_NODISCARD + const void* GetData() const + { + return m_ptr; + } + + private: + const void* m_ptr; + uint32_t m_bytes; + }; + + namespace Priv + { + inline void Append(sqlite3_stmt* statement, int index, const int32_t& data) + { + sqlite::Priv::CheckError(sqlite3_bind_int(statement, index, data)); + } + + inline void Append(sqlite3_stmt* statement, int index, const int64_t& data) + { + sqlite::Priv::CheckError(sqlite3_bind_int64(statement, index, data)); + } + + inline void Append(sqlite3_stmt* statement, int index, const float& data) + { + sqlite::Priv::CheckError(sqlite3_bind_double(statement, index, static_cast(data))); + } + + inline void Append(sqlite3_stmt* statement, int index, const double& data) + { + sqlite::Priv::CheckError(sqlite3_bind_double(statement, index, data)); + } + + inline void Append(sqlite3_stmt* statement, int index, const std::string& data) + { + sqlite::Priv::CheckError(sqlite3_bind_text(statement, index, data.data(), static_cast(data.size()), nullptr)); + } + + inline void Append(sqlite3_stmt* statement, int index, const char* data) + { + sqlite::Priv::CheckError(sqlite3_bind_text(statement, index, data, static_cast(std::strlen(data)), nullptr)); + } + + inline void Append(sqlite3_stmt* statement, int index, const sqlite::Blob& blob) + { + sqlite::Priv::CheckError(sqlite3_bind_blob(statement, index, blob.GetData(), static_cast(blob.GetSize()), nullptr)); + } + + inline void Append(sqlite3_stmt* statement, int index, const sqlite::NOBlob& blob) + { + sqlite::Priv::CheckError(sqlite3_bind_blob(statement, index, blob.GetData(), static_cast(blob.GetSize()), nullptr)); + } + + template + inline void AppendToQuery(sqlite3_stmt* statement, int index, const Arg& arg) + { + sqlite::Priv::Append(statement, index, arg); + } + + template + inline void AppendToQuery(sqlite3_stmt* statement, int index, const First& first, const Args&... args) + { + sqlite::Priv::Append(statement, index, first); + sqlite::Priv::AppendToQuery(statement, ++index, args...); + } + + struct Statement + { + Statement() : handle(nullptr) {} + + Statement(sqlite::Connection& connection, const std::string& command) + { + auto* db = connection.GetPtr(); + + const int code = sqlite3_prepare_v2( + db, + command.data(), + static_cast(command.size()), + &handle, + nullptr); + + Priv::CheckError(db, code); + } + + Statement(Statement&& other) noexcept + { + std::swap(handle, other.handle); + } + + ~Statement() + { + sqlite::Priv::CheckError(sqlite3_finalize(handle)); + } + + Statement& operator=(Statement&& other) noexcept + { + handle = other.handle; + other.handle = nullptr; + + return *this; + } + + CPP_SQLITE_NODISCARD + bool Advance() const + { + const int code = sqlite3_step(handle); + + if(code == SQLITE_ROW) + { + return true; + } + + sqlite::Priv::CheckError(code); + Reset(); + + return false; + } + + bool Reset() const + { + return sqlite::Priv::CheckError(sqlite3_reset(handle)); + } + + CPP_SQLITE_NODISCARD + int ColumnCount() const + { + Reset(); + + if(!Advance()) + { + return 0; + } + + const int count = sqlite3_column_count(handle); + Reset(); + + return count; + } + + CPP_SQLITE_NODISCARD + std::string GetColumnName(int columnIndex) const + { + Reset(); + + if(!Advance()) + { +#ifndef CPP_SQLITE_NOTHROW + throw sqlite::Error("SQL error: invalid column index"); +#endif + } + + std::string name = sqlite3_column_name(handle, columnIndex); + + if(name.empty()) + { +#ifndef CPP_SQLITE_NOTHROW + throw sqlite::Error("SQL error: failed to get column name at index " + std::to_string(columnIndex)); +#endif + } + + Reset(); + + return name; + } + + template + CPP_SQLITE_NODISCARD + T Get(int) const + { + static_assert(sizeof(T) == -1, "SQL error: invalid column data type"); + } + + sqlite3_stmt* handle = nullptr; + }; + + template<> + inline float Statement::Get(int col) const + { + return static_cast(sqlite3_column_double(handle, col)); + } + + template<> + inline double Statement::Get(int col) const + { + return sqlite3_column_double(handle, col); + } + + template<> + inline int32_t Statement::Get(int col) const + { + return sqlite3_column_int(handle, col); + } + + template<> + inline int64_t Statement::Get(int col) const + { + return sqlite3_column_int64(handle, col); + } + + template<> + inline std::string Statement::Get(int col) const + { + const unsigned char* bytes = sqlite3_column_text(handle, col); + const int size = sqlite3_column_bytes(handle, col); + + if(size == 0) + { + return ""; + } + + return {reinterpret_cast(bytes), static_cast(size)}; + } + + template<> + inline sqlite::Blob Statement::Get(int col) const + { + const void* bytes = sqlite3_column_blob(handle, col); + const int size = sqlite3_column_bytes(handle, col); + + return {bytes, size}; + } + } + + class Type + { + private: + Type(const sqlite::Priv::Statement& statement, int col) + : m_statement(statement), m_columnIndex(col) + { + + } + public: + template + operator T() const + { + return m_statement.Get(m_columnIndex); + } + + friend class Result; + + private: + const sqlite::Priv::Statement& m_statement; + const int m_columnIndex; + }; + + class Result + { + explicit Result(sqlite::Priv::Statement&& statement) + : m_statement(std::move(statement)) + { + + } + + public: + Result() = default; + + Result(Result&& other) noexcept + { + m_statement = std::move(other.m_statement); + } + + Result& operator=(Result&& other) noexcept + { + m_statement = std::move(other.m_statement); + + return *this; + } + + CPP_SQLITE_NODISCARD + bool HasData() const + { + return ColumnCount() > 0; + } + + CPP_SQLITE_NODISCARD + int ColumnCount() const + { + return m_statement.ColumnCount(); + } + + bool Reset() const + { + return m_statement.Reset(); + } + + CPP_SQLITE_NODISCARD + bool Next() const + { + return m_statement.Advance(); + } + + CPP_SQLITE_NODISCARD + Type Get(int columnIndex) const + { + return {m_statement, columnIndex}; + } + + CPP_SQLITE_NODISCARD + std::string GetColumnName(int columnIndex) const + { + return m_statement.GetColumnName(columnIndex); + } + + friend void Statement(sqlite::Connection&, const std::string&); + + template + friend Result Query(sqlite::Connection& connection, const std::string& command, const First& first, const Args... args); + friend Result Query(sqlite::Connection& connection, const std::string& command); + + private: + sqlite::Priv::Statement m_statement; + }; + + template + inline void Statement(sqlite::Connection& connection, const std::string& command, const First& first, const Args... args) + { + sqlite::Priv::Statement statement(connection, command); + sqlite::Priv::AppendToQuery(statement.handle, 1, first, args...); + + (void)statement.Advance(); + } + + inline void Statement(sqlite::Connection& connection, const std::string& command) + { + sqlite::Priv::Statement statement(connection, command); + + (void)statement.Advance(); + } + + template + CPP_SQLITE_NODISCARD + inline Result Query(sqlite::Connection& connection, const std::string& command, const First& first, const Args... args) + { + sqlite::Priv::Statement statement(connection, command); + sqlite::Priv::AppendToQuery(statement.handle, 1, first, args...); + + return Result(std::move(statement)); + } + + CPP_SQLITE_NODISCARD + inline Result Query(sqlite::Connection& connection, const std::string& command) + { + sqlite::Priv::Statement statement(connection, command); + + return Result(std::move(statement)); + } + + inline bool Backup(sqlite::Connection& from, sqlite::Connection& to) + { + sqlite3_backup* backup = sqlite3_backup_init(to.GetPtr(), "main", from.GetPtr(), "main"); + + if(!backup) + { + CPP_SQLITE_THROW("SQL error: failed to initialize backup"); + } + + if(!Priv::CheckError(sqlite3_backup_step(backup, -1))) + { + return false; + } + + if(!Priv::CheckError(sqlite3_backup_finish(backup))) + { + return false; + } + + return true; + } + + inline bool Backup(sqlite::Connection& from, const std::string& filename) + { + sqlite::Connection to(filename); + + return sqlite::Backup(from, to); + } +} diff --git a/3rdparty/cppzmq/LICENSE b/3rdparty/cppzmq/LICENSE new file mode 100644 index 000000000..ae98bd859 --- /dev/null +++ b/3rdparty/cppzmq/LICENSE @@ -0,0 +1,17 @@ + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. diff --git a/3rdparty/cppzmq/README.md b/3rdparty/cppzmq/README.md new file mode 100644 index 000000000..e2bea0b63 --- /dev/null +++ b/3rdparty/cppzmq/README.md @@ -0,0 +1,196 @@ +[![CI](https://github.com/zeromq/cppzmq/actions/workflows/ci.yml/badge.svg)](https://github.com/zeromq/cppzmq/actions) +[![Coverage Status](https://coveralls.io/repos/github/zeromq/cppzmq/badge.svg?branch=master)](https://coveralls.io/github/zeromq/cppzmq?branch=master) +[![License](https://img.shields.io/github/license/zeromq/cppzmq.svg)](https://github.com/zeromq/cppzmq/blob/master/LICENSE) + +Introduction & Design Goals +=========================== + +cppzmq is a C++ binding for libzmq. It has the following design goals: + - cppzmq maps the libzmq C API to C++ concepts. In particular: + - it is type-safe (the libzmq C API exposes various class-like concepts as void*) + - it provides exception-based error handling (the libzmq C API provides errno-based error handling) + - it provides RAII-style classes that automate resource management (the libzmq C API requires the user to take care to free resources explicitly) + - cppzmq is a light-weight, header-only binding. You only need to include the header file zmq.hpp (and maybe zmq_addon.hpp) to use it. + - zmq.hpp is meant to contain direct mappings of the abstractions provided by the libzmq C API, while zmq_addon.hpp provides additional higher-level abstractions. + +There are other C++ bindings for ZeroMQ with different design goals. In particular, none of the following bindings are header-only: + - [zmqpp](https://github.com/zeromq/zmqpp) is a high-level binding to libzmq. + - [czmqpp](https://github.com/zeromq/czmqpp) is a binding based on the high-level czmq API. + - [fbzmq](https://github.com/facebook/fbzmq) is a binding that integrates with Apache Thrift and provides higher-level abstractions in addition. It requires C++14. + +Supported platforms +=================== + + - Only a subset of the platforms that are supported by libzmq itself are supported. Some features already require a compiler supporting C++11. In the future, probably all features will require C++11. To build and run the tests, CMake and Catch are required. + - Any libzmq 4.x version is expected to work. DRAFT features may only work for the most recent tested version. Currently explicitly tested libzmq versions are + - 4.2.0 (without DRAFT API) + - 4.3.4 (with and without DRAFT API) + - Platforms with full support (i.e. CI executing build and tests) + - Ubuntu 18.04 x64 (with gcc 4.8.5, 5.5.0, 7.5.0) + - Ubuntu 20.04 x64 (with gcc 9.3.0, 10.3.0 and clang 12) + - Visual Studio 2017 x64 + - Visual Studio 2019 x64 + - macOS 10.15 (with clang 12, without DRAFT API) + - Additional platforms that are known to work: + - We have no current reports on additional platforms that are known to work yet. Please add your platform here. If CI can be provided for them with a cloud-based CI service working with GitHub, you are invited to add CI, and make it possible to be included in the list above. + - Additional platforms that probably work: + - Any platform supported by libzmq that provides a sufficiently recent gcc (4.8.1 or newer) or clang (3.4.1 or newer) + - Visual Studio 2012+ x86/x64 + +Examples +======== +These examples require at least C++11. +```c++ +#include + +int main() +{ + zmq::context_t ctx; + zmq::socket_t sock(ctx, zmq::socket_type::push); + sock.bind("inproc://test"); + sock.send(zmq::str_buffer("Hello, world"), zmq::send_flags::dontwait); +} +``` +This a more complex example where we send and receive multi-part messages over TCP with a wildcard port. +```c++ +#include +#include + +int main() +{ + zmq::context_t ctx; + zmq::socket_t sock1(ctx, zmq::socket_type::push); + zmq::socket_t sock2(ctx, zmq::socket_type::pull); + sock1.bind("tcp://127.0.0.1:*"); + const std::string last_endpoint = + sock1.get(zmq::sockopt::last_endpoint); + std::cout << "Connecting to " + << last_endpoint << std::endl; + sock2.connect(last_endpoint); + + std::array send_msgs = { + zmq::str_buffer("foo"), + zmq::str_buffer("bar!") + }; + if (!zmq::send_multipart(sock1, send_msgs)) + return 1; + + std::vector recv_msgs; + const auto ret = zmq::recv_multipart( + sock2, std::back_inserter(recv_msgs)); + if (!ret) + return 1; + std::cout << "Got " << *ret + << " messages" << std::endl; + return 0; +} +``` + +See the `examples` directory for more examples. When the project is compiled with tests enabled, each example gets compiled to an executable. + + +API Overview +============ + +For an extensive overview of the `zmq.hpp` API in use, see this [Tour of CPPZMQ by @brettviren](https://brettviren.github.io/cppzmq-tour/index.html). + +Bindings for libzmq in `zmq.hpp`: + +Types: +* class `zmq::context_t` +* enum `zmq::ctxopt` +* class `zmq::socket_t` +* class `zmq::socket_ref` +* enum `zmq::socket_type` +* enum `zmq::sockopt` +* enum `zmq::send_flags` +* enum `zmq::recv_flags` +* class `zmq::message_t` +* class `zmq::const_buffer` +* class `zmq::mutable_buffer` +* struct `zmq::recv_buffer_size` +* alias `zmq::send_result_t` +* alias `zmq::recv_result_t` +* alias `zmq::recv_buffer_result_t` +* class `zmq::error_t` +* class `zmq::monitor_t` +* struct `zmq_event_t`, +* alias `zmq::free_fn`, +* alias `zmq::pollitem_t`, +* alias `zmq::fd_t` +* class `zmq::poller_t` DRAFT +* enum `zmq::event_flags` DRAFT +* enum `zmq::poller_event` DRAFT + +Functions: +* `zmq::version` +* `zmq::poll` +* `zmq::proxy` +* `zmq::proxy_steerable` +* `zmq::buffer` +* `zmq::str_buffer` + +Extra high-level types and functions `zmq_addon.hpp`: + +Types: +* class `zmq::multipart_t` +* class `zmq::active_poller_t` DRAFT + +Functions: +* `zmq::recv_multipart` +* `zmq::send_multipart` +* `zmq::send_multipart_n` +* `zmq::encode` +* `zmq::decode` + +Compatibility Guidelines +======================== + +The users of cppzmq are expected to follow the guidelines below to ensure not to break when upgrading cppzmq to newer versions (non-exhaustive list): + +* Do not depend on any macros defined in cppzmq unless explicitly declared public here. + +The following macros may be used by consumers of cppzmq: `CPPZMQ_VERSION`, `CPPZMQ_VERSION_MAJOR`, `CPPZMQ_VERSION_MINOR`, `CPPZMQ_VERSION_PATCH`. + +Contribution policy +=================== + +The contribution policy is at: http://rfc.zeromq.org/spec:22 + +Build instructions +================== + +Build steps: + +1. Build [libzmq](https://github.com/zeromq/libzmq) via cmake. This does an out of source build and installs the build files + - download and unzip the lib, cd to directory + - mkdir build + - cd build + - cmake .. + - sudo make -j4 install + +2. Build cppzmq via cmake. This does an out of source build and installs the build files + - download and unzip the lib, cd to directory + - mkdir build + - cd build + - cmake .. + - sudo make -j4 install + +3. Build cppzmq via [vcpkg](https://github.com/Microsoft/vcpkg/). This does an out of source build and installs the build files + - git clone https://github.com/Microsoft/vcpkg.git + - cd vcpkg + - ./bootstrap-vcpkg.sh # bootstrap-vcpkg.bat for Powershell + - ./vcpkg integrate install + - ./vcpkg install cppzmq + +Using this: + +A cmake find package scripts is provided for you to easily include this library. +Add these lines in your CMakeLists.txt to include the headers and library files of +cpp zmq (which will also include libzmq for you). + +``` +#find cppzmq wrapper, installed by make of cppzmq +find_package(cppzmq) +target_link_libraries(*Your Project Name* cppzmq) +``` diff --git a/3rdparty/cppzmq/zmq.hpp b/3rdparty/cppzmq/zmq.hpp index d59eb55e5..3fa484c6c 100644 --- a/3rdparty/cppzmq/zmq.hpp +++ b/3rdparty/cppzmq/zmq.hpp @@ -67,6 +67,8 @@ #define ZMQ_DEPRECATED(msg) __declspec(deprecated(msg)) #elif defined(__GNUC__) #define ZMQ_DEPRECATED(msg) __attribute__((deprecated(msg))) +#else +#define ZMQ_DEPRECATED(msg) #endif #if defined(ZMQ_CPP17) @@ -92,7 +94,7 @@ #define ZMQ_CONSTEXPR_VAR const #define ZMQ_CPP11_DEPRECATED(msg) #endif -#if defined(ZMQ_CPP14) && (!defined(_MSC_VER) || _MSC_VER > 1900) +#if defined(ZMQ_CPP14) && (!defined(_MSC_VER) || _MSC_VER > 1900) && (!defined(__GNUC__) || __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ > 3)) #define ZMQ_EXTENDED_CONSTEXPR #endif #if defined(ZMQ_CPP17) @@ -145,7 +147,7 @@ /* Version macros for compile-time API version detection */ #define CPPZMQ_VERSION_MAJOR 4 -#define CPPZMQ_VERSION_MINOR 8 +#define CPPZMQ_VERSION_MINOR 10 #define CPPZMQ_VERSION_PATCH 0 #define CPPZMQ_VERSION \ @@ -299,18 +301,30 @@ class error_t : public std::exception int errnum; }; -inline int poll(zmq_pollitem_t *items_, size_t nitems_, long timeout_ = -1) +namespace detail { +inline int poll(zmq_pollitem_t *items_, size_t nitems_, long timeout_) { int rc = zmq_poll(items_, static_cast(nitems_), timeout_); if (rc < 0) throw error_t(); return rc; } +} + +#ifdef ZMQ_CPP11 +ZMQ_DEPRECATED("from 4.8.0, use poll taking std::chrono::duration instead of long") +inline int poll(zmq_pollitem_t *items_, size_t nitems_, long timeout_) +#else +inline int poll(zmq_pollitem_t *items_, size_t nitems_, long timeout_ = -1) +#endif +{ + return detail::poll(items_, nitems_, timeout_); +} ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") inline int poll(zmq_pollitem_t const *items_, size_t nitems_, long timeout_ = -1) { - return poll(const_cast(items_), nitems_, timeout_); + return detail::poll(const_cast(items_), nitems_, timeout_); } #ifdef ZMQ_CPP11 @@ -318,7 +332,7 @@ ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") inline int poll(zmq_pollitem_t const *items, size_t nitems, std::chrono::milliseconds timeout) { - return poll(const_cast(items), nitems, + return detail::poll(const_cast(items), nitems, static_cast(timeout.count())); } @@ -326,39 +340,39 @@ ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") inline int poll(std::vector const &items, std::chrono::milliseconds timeout) { - return poll(const_cast(items.data()), items.size(), + return detail::poll(const_cast(items.data()), items.size(), static_cast(timeout.count())); } ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") inline int poll(std::vector const &items, long timeout_ = -1) { - return poll(const_cast(items.data()), items.size(), timeout_); + return detail::poll(const_cast(items.data()), items.size(), timeout_); } inline int -poll(zmq_pollitem_t *items, size_t nitems, std::chrono::milliseconds timeout) +poll(zmq_pollitem_t *items, size_t nitems, std::chrono::milliseconds timeout = std::chrono::milliseconds{-1}) { - return poll(items, nitems, static_cast(timeout.count())); + return detail::poll(items, nitems, static_cast(timeout.count())); } inline int poll(std::vector &items, - std::chrono::milliseconds timeout) + std::chrono::milliseconds timeout = std::chrono::milliseconds{-1}) { - return poll(items.data(), items.size(), static_cast(timeout.count())); + return detail::poll(items.data(), items.size(), static_cast(timeout.count())); } -ZMQ_DEPRECATED("from 4.3.1, use poll taking std::chrono instead of long") -inline int poll(std::vector &items, long timeout_ = -1) +ZMQ_DEPRECATED("from 4.3.1, use poll taking std::chrono::duration instead of long") +inline int poll(std::vector &items, long timeout_) { - return poll(items.data(), items.size(), timeout_); + return detail::poll(items.data(), items.size(), timeout_); } template inline int poll(std::array &items, - std::chrono::milliseconds timeout) + std::chrono::milliseconds timeout = std::chrono::milliseconds{-1}) { - return poll(items.data(), items.size(), static_cast(timeout.count())); + return detail::poll(items.data(), items.size(), static_cast(timeout.count())); } #endif @@ -524,6 +538,11 @@ class message_t throw error_t(); memcpy(data(), data_, size_); } + + void rebuild(const std::string &str) + { + rebuild(str.data(), str.size()); + } void rebuild(void *data_, size_t size_, free_fn *ffn_, void *hint_ = ZMQ_NULLPTR) { @@ -851,7 +870,7 @@ class context_t int rc; do { - rc = zmq_ctx_destroy(ptr); + rc = zmq_ctx_term(ptr); } while (rc == -1 && errno == EINTR); ZMQ_ASSERT(rc == 0); @@ -1362,6 +1381,39 @@ constexpr const_buffer operator"" _zbuf(const char32_t *str, size_t len) noexcep } } +#ifdef ZMQ_CPP11 +enum class socket_type : int +{ + req = ZMQ_REQ, + rep = ZMQ_REP, + dealer = ZMQ_DEALER, + router = ZMQ_ROUTER, + pub = ZMQ_PUB, + sub = ZMQ_SUB, + xpub = ZMQ_XPUB, + xsub = ZMQ_XSUB, + push = ZMQ_PUSH, + pull = ZMQ_PULL, +#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0) + server = ZMQ_SERVER, + client = ZMQ_CLIENT, + radio = ZMQ_RADIO, + dish = ZMQ_DISH, + gather = ZMQ_GATHER, + scatter = ZMQ_SCATTER, + dgram = ZMQ_DGRAM, +#endif +#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 3) + peer = ZMQ_PEER, + channel = ZMQ_CHANNEL, +#endif +#if ZMQ_VERSION_MAJOR >= 4 + stream = ZMQ_STREAM, +#endif + pair = ZMQ_PAIR +}; +#endif + namespace sockopt { // There are two types of options, @@ -1430,6 +1482,9 @@ ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CURVE_SERVER, curve_server, int); #ifdef ZMQ_CURVE_SERVERKEY ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SERVERKEY, curve_serverkey); #endif +#ifdef ZMQ_DISCONNECT_MSG +ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_DISCONNECT_MSG, disconnect_msg); +#endif #ifdef ZMQ_EVENTS ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_EVENTS, events, int); #endif @@ -1470,6 +1525,9 @@ ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TIMEOUT, heartbeat_timeout, int); #ifdef ZMQ_HEARTBEAT_TTL ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TTL, heartbeat_ttl, int); #endif +#ifdef ZMQ_HELLO_MSG +ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_HELLO_MSG, hello_msg); +#endif #ifdef ZMQ_IMMEDIATE ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IMMEDIATE, immediate, int); #endif @@ -1503,6 +1561,9 @@ ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_MULTICAST_LOOP, multicast_loop, int); #ifdef ZMQ_MULTICAST_MAXTPDU ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_MAXTPDU, multicast_maxtpdu, int); #endif +#ifdef ZMQ_ONLY_FIRST_SUBSCRIBE +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ONLY_FIRST_SUBSCRIBE, only_first_subscribe, int); +#endif #ifdef ZMQ_PLAIN_SERVER ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PLAIN_SERVER, plain_server, int); #endif @@ -1512,6 +1573,9 @@ ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_PASSWORD, plain_password); #ifdef ZMQ_PLAIN_USERNAME ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_USERNAME, plain_username); #endif +#ifdef ZMQ_PRIORITY +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_PRIORITY, priority, int); +#endif #ifdef ZMQ_USE_FD ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_USE_FD, use_fd, int); #endif @@ -1539,6 +1603,9 @@ ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL, reconnect_ivl, int); #ifdef ZMQ_RECONNECT_IVL_MAX ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL_MAX, reconnect_ivl_max, int); #endif +#ifdef ZMQ_RECONNECT_STOP +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_STOP, reconnect_stop, int); +#endif #ifdef ZMQ_RECOVERY_IVL ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECOVERY_IVL, recovery_ivl, int); #endif @@ -1569,9 +1636,15 @@ ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDHWM, sndhwm, int); #ifdef ZMQ_SNDTIMEO ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDTIMEO, sndtimeo, int); #endif +#ifdef ZMQ_SOCKS_PASSWORD +ZMQ_DEFINE_ARRAY_OPT(ZMQ_SOCKS_PASSWORD, socks_password); +#endif #ifdef ZMQ_SOCKS_PROXY ZMQ_DEFINE_ARRAY_OPT(ZMQ_SOCKS_PROXY, socks_proxy); #endif +#ifdef ZMQ_SOCKS_USERNAME +ZMQ_DEFINE_ARRAY_OPT(ZMQ_SOCKS_USERNAME, socks_username); +#endif #ifdef ZMQ_STREAM_NOTIFY ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_STREAM_NOTIFY, stream_notify, int); #endif @@ -1601,7 +1674,10 @@ ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TOS, tos, int); #endif #ifdef ZMQ_TYPE ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TYPE, type, int); -#endif +#ifdef ZMQ_CPP11 +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TYPE, socket_type, socket_type); +#endif // ZMQ_CPP11 +#endif // ZMQ_TYPE #ifdef ZMQ_UNSUBSCRIBE ZMQ_DEFINE_ARRAY_OPT(ZMQ_UNSUBSCRIBE, unsubscribe); #endif @@ -1626,6 +1702,9 @@ ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSER, xpub_verboser, int); #ifdef ZMQ_XPUB_MANUAL ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_MANUAL, xpub_manual, int); #endif +#ifdef ZMQ_XPUB_MANUAL_LAST_VALUE +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_MANUAL_LAST_VALUE, xpub_manual_last_value, int); +#endif #ifdef ZMQ_XPUB_NODROP ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_NODROP, xpub_nodrop, int); #endif @@ -1743,7 +1822,7 @@ class socket_base template ZMQ_NODISCARD T get(sockopt::integral_option) const { - static_assert(std::is_integral::value, "T must be integral"); + static_assert(std::is_scalar::value, "T must be scalar"); T val; size_t size = sizeof val; get_option(Opt, &val, &size); @@ -2012,32 +2091,6 @@ class socket_base }; } // namespace detail -#ifdef ZMQ_CPP11 -enum class socket_type : int -{ - req = ZMQ_REQ, - rep = ZMQ_REP, - dealer = ZMQ_DEALER, - router = ZMQ_ROUTER, - pub = ZMQ_PUB, - sub = ZMQ_SUB, - xpub = ZMQ_XPUB, - xsub = ZMQ_XSUB, - push = ZMQ_PUSH, - pull = ZMQ_PULL, -#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0) - server = ZMQ_SERVER, - client = ZMQ_CLIENT, - radio = ZMQ_RADIO, - dish = ZMQ_DISH, -#endif -#if ZMQ_VERSION_MAJOR >= 4 - stream = ZMQ_STREAM, -#endif - pair = ZMQ_PAIR -}; -#endif - struct from_handle_t { struct _private @@ -2315,7 +2368,11 @@ class monitor_t {_monitor_socket.handle(), 0, ZMQ_POLLIN, 0}, }; + #ifdef ZMQ_CPP11 + zmq::poll(&items[0], 1, std::chrono::milliseconds(timeout)); + #else zmq::poll(&items[0], 1, timeout); + #endif if (items[0].revents & ZMQ_POLLIN) { int rc = zmq_msg_recv(eventMsg.handle(), _monitor_socket.handle(), 0); @@ -2390,8 +2447,7 @@ class monitor_t case ZMQ_EVENT_DISCONNECTED: on_event_disconnected(*event, address.c_str()); break; -#ifdef ZMQ_BUILD_DRAFT_API -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3) +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 0) || (defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)) case ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL: on_event_handshake_failed_no_detail(*event, address.c_str()); break; @@ -2404,14 +2460,13 @@ class monitor_t case ZMQ_EVENT_HANDSHAKE_SUCCEEDED: on_event_handshake_succeeded(*event, address.c_str()); break; -#elif ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1) +#elif defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1) case ZMQ_EVENT_HANDSHAKE_FAILED: on_event_handshake_failed(*event, address.c_str()); break; case ZMQ_EVENT_HANDSHAKE_SUCCEED: on_event_handshake_succeed(*event, address.c_str()); break; -#endif #endif default: on_event_unknown(*event, address.c_str()); @@ -2608,6 +2663,17 @@ template class poller_t add_impl(socket, events, nullptr); } + template< + typename Dummy = void, + typename = + typename std::enable_if::value, Dummy>::type> + void add(fd_t fd, event_flags events, T *user_data) + { + add_impl(fd, events, user_data); + } + + void add(fd_t fd, event_flags events) { add_impl(fd, events, nullptr); } + void remove(zmq::socket_ref socket) { if (0 != zmq_poller_remove(poller_ptr.get(), socket.handle())) { @@ -2674,6 +2740,15 @@ template class poller_t throw error_t(); } } + + void add_impl(fd_t fd, event_flags events, T *user_data) + { + if (0 + != zmq_poller_add_fd(poller_ptr.get(), fd, user_data, + static_cast(events))) { + throw error_t(); + } + } }; #endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) @@ -2685,4 +2760,3 @@ inline std::ostream &operator<<(std::ostream &os, const message_t &msg) } // namespace zmq #endif // __ZMQ_HPP_INCLUDED__ - diff --git a/3rdparty/cppzmq/zmq_addon.hpp b/3rdparty/cppzmq/zmq_addon.hpp new file mode 100644 index 000000000..958eec56d --- /dev/null +++ b/3rdparty/cppzmq/zmq_addon.hpp @@ -0,0 +1,753 @@ +/* + Copyright (c) 2016-2017 ZeroMQ community + Copyright (c) 2016 VOCA AS / Harald Nøkland + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef __ZMQ_ADDON_HPP_INCLUDED__ +#define __ZMQ_ADDON_HPP_INCLUDED__ + +#include "zmq.hpp" + +#include +#include +#include +#include +#ifdef ZMQ_CPP11 +#include +#include +#include +#endif + +namespace zmq +{ +#ifdef ZMQ_CPP11 + +namespace detail +{ +template +recv_result_t +recv_multipart_n(socket_ref s, OutputIt out, size_t n, recv_flags flags) +{ + size_t msg_count = 0; + message_t msg; + while (true) { + if ZMQ_CONSTEXPR_IF (CheckN) { + if (msg_count >= n) + throw std::runtime_error( + "Too many message parts in recv_multipart_n"); + } + if (!s.recv(msg, flags)) { + // zmq ensures atomic delivery of messages + assert(msg_count == 0); + return {}; + } + ++msg_count; + const bool more = msg.more(); + *out++ = std::move(msg); + if (!more) + break; + } + return msg_count; +} + +inline bool is_little_endian() +{ + const uint16_t i = 0x01; + return *reinterpret_cast(&i) == 0x01; +} + +inline void write_network_order(unsigned char *buf, const uint32_t value) +{ + if (is_little_endian()) { + ZMQ_CONSTEXPR_VAR uint32_t mask = (std::numeric_limits::max)(); + *buf++ = static_cast((value >> 24) & mask); + *buf++ = static_cast((value >> 16) & mask); + *buf++ = static_cast((value >> 8) & mask); + *buf++ = static_cast(value & mask); + } else { + std::memcpy(buf, &value, sizeof(value)); + } +} + +inline uint32_t read_u32_network_order(const unsigned char *buf) +{ + if (is_little_endian()) { + return (static_cast(buf[0]) << 24) + + (static_cast(buf[1]) << 16) + + (static_cast(buf[2]) << 8) + + static_cast(buf[3]); + } else { + uint32_t value; + std::memcpy(&value, buf, sizeof(value)); + return value; + } +} +} // namespace detail + +/* Receive a multipart message. + + Writes the zmq::message_t objects to OutputIterator out. + The out iterator must handle an unspecified number of writes, + e.g. by using std::back_inserter. + + Returns: the number of messages received or nullopt (on EAGAIN). + Throws: if recv throws. Any exceptions thrown + by the out iterator will be propagated and the message + may have been only partially received with pending + message parts. It is adviced to close this socket in that event. +*/ +template +ZMQ_NODISCARD recv_result_t recv_multipart(socket_ref s, + OutputIt out, + recv_flags flags = recv_flags::none) +{ + return detail::recv_multipart_n(s, std::move(out), 0, flags); +} + +/* Receive a multipart message. + + Writes at most n zmq::message_t objects to OutputIterator out. + If the number of message parts of the incoming message exceeds n + then an exception will be thrown. + + Returns: the number of messages received or nullopt (on EAGAIN). + Throws: if recv throws. Throws std::runtime_error if the number + of message parts exceeds n (exactly n messages will have been written + to out). Any exceptions thrown + by the out iterator will be propagated and the message + may have been only partially received with pending + message parts. It is adviced to close this socket in that event. +*/ +template +ZMQ_NODISCARD recv_result_t recv_multipart_n(socket_ref s, + OutputIt out, + size_t n, + recv_flags flags = recv_flags::none) +{ + return detail::recv_multipart_n(s, std::move(out), n, flags); +} + +/* Send a multipart message. + + The range must be a ForwardRange of zmq::message_t, + zmq::const_buffer or zmq::mutable_buffer. + The flags may be zmq::send_flags::sndmore if there are + more message parts to be sent after the call to this function. + + Returns: the number of messages sent (exactly msgs.size()) or nullopt (on EAGAIN). + Throws: if send throws. Any exceptions thrown + by the msgs range will be propagated and the message + may have been only partially sent. It is adviced to close this socket in that event. +*/ +template::value + && (std::is_same, message_t>::value + || detail::is_buffer>::value)>::type +#endif + > +send_result_t +send_multipart(socket_ref s, Range &&msgs, send_flags flags = send_flags::none) +{ + using std::begin; + using std::end; + auto it = begin(msgs); + const auto end_it = end(msgs); + size_t msg_count = 0; + while (it != end_it) { + const auto next = std::next(it); + const auto msg_flags = + flags | (next == end_it ? send_flags::none : send_flags::sndmore); + if (!s.send(*it, msg_flags)) { + // zmq ensures atomic delivery of messages + assert(it == begin(msgs)); + return {}; + } + ++msg_count; + it = next; + } + return msg_count; +} + +/* Encode a multipart message. + + The range must be a ForwardRange of zmq::message_t. A + zmq::multipart_t or STL container may be passed for encoding. + + Returns: a zmq::message_t holding the encoded multipart data. + + Throws: std::range_error is thrown if the size of any single part + can not fit in an unsigned 32 bit integer. + + The encoding is compatible with that used by the CZMQ function + zmsg_encode(), see https://rfc.zeromq.org/spec/50/. + Each part consists of a size followed by the data. + These are placed contiguously into the output message. A part of + size less than 255 bytes will have a single byte size value. + Larger parts will have a five byte size value with the first byte + set to 0xFF and the remaining four bytes holding the size of the + part's data. +*/ +template::value + && (std::is_same, message_t>::value + || detail::is_buffer>::value)>::type +#endif + > +message_t encode(const Range &parts) +{ + size_t mmsg_size = 0; + + // First pass check sizes + for (const auto &part : parts) { + const size_t part_size = part.size(); + if (part_size > (std::numeric_limits::max)()) { + // Size value must fit into uint32_t. + throw std::range_error("Invalid size, message part too large"); + } + const size_t count_size = + part_size < (std::numeric_limits::max)() ? 1 : 5; + mmsg_size += part_size + count_size; + } + + message_t encoded(mmsg_size); + unsigned char *buf = encoded.data(); + for (const auto &part : parts) { + const uint32_t part_size = static_cast(part.size()); + const unsigned char *part_data = + static_cast(part.data()); + + if (part_size < (std::numeric_limits::max)()) { + // small part + *buf++ = (unsigned char) part_size; + } else { + // big part + *buf++ = (std::numeric_limits::max)(); + detail::write_network_order(buf, part_size); + buf += sizeof(part_size); + } + std::memcpy(buf, part_data, part_size); + buf += part_size; + } + + assert(static_cast(buf - encoded.data()) == mmsg_size); + return encoded; +} + +/* Decode an encoded message to multiple parts. + + The given output iterator must be a ForwardIterator to a container + holding zmq::message_t such as a zmq::multipart_t or various STL + containers. + + Returns the ForwardIterator advanced once past the last decoded + part. + + Throws: a std::out_of_range is thrown if the encoded part sizes + lead to exceeding the message data bounds. + + The decoding assumes the message is encoded in the manner + performed by zmq::encode(), see https://rfc.zeromq.org/spec/50/. + */ +template OutputIt decode(const message_t &encoded, OutputIt out) +{ + const unsigned char *source = encoded.data(); + const unsigned char *const limit = source + encoded.size(); + + while (source < limit) { + size_t part_size = *source++; + if (part_size == (std::numeric_limits::max)()) { + if (static_cast(limit - source) < sizeof(uint32_t)) { + throw std::out_of_range( + "Malformed encoding, overflow in reading size"); + } + part_size = detail::read_u32_network_order(source); + // the part size is allowed to be less than 0xFF + source += sizeof(uint32_t); + } + + if (static_cast(limit - source) < part_size) { + throw std::out_of_range("Malformed encoding, overflow in reading part"); + } + *out = message_t(source, part_size); + ++out; + source += part_size; + } + + assert(source == limit); + return out; +} + +#endif + + +#ifdef ZMQ_HAS_RVALUE_REFS + +/* + This class handles multipart messaging. It is the C++ equivalent of zmsg.h, + which is part of CZMQ (the high-level C binding). Furthermore, it is a major + improvement compared to zmsg.hpp, which is part of the examples in the ØMQ + Guide. Unnecessary copying is avoided by using move semantics to efficiently + add/remove parts. +*/ +class multipart_t +{ + private: + std::deque m_parts; + + public: + typedef std::deque::value_type value_type; + + typedef std::deque::iterator iterator; + typedef std::deque::const_iterator const_iterator; + + typedef std::deque::reverse_iterator reverse_iterator; + typedef std::deque::const_reverse_iterator const_reverse_iterator; + + // Default constructor + multipart_t() {} + + // Construct from socket receive + multipart_t(socket_ref socket) { recv(socket); } + + // Construct from memory block + multipart_t(const void *src, size_t size) { addmem(src, size); } + + // Construct from string + multipart_t(const std::string &string) { addstr(string); } + + // Construct from message part + multipart_t(message_t &&message) { add(std::move(message)); } + + // Move constructor + multipart_t(multipart_t &&other) ZMQ_NOTHROW { m_parts = std::move(other.m_parts); } + + // Move assignment operator + multipart_t &operator=(multipart_t &&other) ZMQ_NOTHROW + { + m_parts = std::move(other.m_parts); + return *this; + } + + // Destructor + virtual ~multipart_t() { clear(); } + + message_t &operator[](size_t n) { return m_parts[n]; } + + const message_t &operator[](size_t n) const { return m_parts[n]; } + + message_t &at(size_t n) { return m_parts.at(n); } + + const message_t &at(size_t n) const { return m_parts.at(n); } + + iterator begin() { return m_parts.begin(); } + + const_iterator begin() const { return m_parts.begin(); } + + const_iterator cbegin() const { return m_parts.cbegin(); } + + reverse_iterator rbegin() { return m_parts.rbegin(); } + + const_reverse_iterator rbegin() const { return m_parts.rbegin(); } + + iterator end() { return m_parts.end(); } + + const_iterator end() const { return m_parts.end(); } + + const_iterator cend() const { return m_parts.cend(); } + + reverse_iterator rend() { return m_parts.rend(); } + + const_reverse_iterator rend() const { return m_parts.rend(); } + + // Delete all parts + void clear() { m_parts.clear(); } + + // Get number of parts + size_t size() const { return m_parts.size(); } + + // Check if number of parts is zero + bool empty() const { return m_parts.empty(); } + + // Receive multipart message from socket + bool recv(socket_ref socket, int flags = 0) + { + clear(); + bool more = true; + while (more) { + message_t message; +#ifdef ZMQ_CPP11 + if (!socket.recv(message, static_cast(flags))) + return false; +#else + if (!socket.recv(&message, flags)) + return false; +#endif + more = message.more(); + add(std::move(message)); + } + return true; + } + + // Send multipart message to socket + bool send(socket_ref socket, int flags = 0) + { + flags &= ~(ZMQ_SNDMORE); + bool more = size() > 0; + while (more) { + message_t message = pop(); + more = size() > 0; +#ifdef ZMQ_CPP11 + if (!socket.send(message, static_cast( + (more ? ZMQ_SNDMORE : 0) | flags))) + return false; +#else + if (!socket.send(message, (more ? ZMQ_SNDMORE : 0) | flags)) + return false; +#endif + } + clear(); + return true; + } + + // Concatenate other multipart to front + void prepend(multipart_t &&other) + { + while (!other.empty()) + push(other.remove()); + } + + // Concatenate other multipart to back + void append(multipart_t &&other) + { + while (!other.empty()) + add(other.pop()); + } + + // Push memory block to front + void pushmem(const void *src, size_t size) + { + m_parts.push_front(message_t(src, size)); + } + + // Push memory block to back + void addmem(const void *src, size_t size) + { + m_parts.push_back(message_t(src, size)); + } + + // Push string to front + void pushstr(const std::string &string) + { + m_parts.push_front(message_t(string.data(), string.size())); + } + + // Push string to back + void addstr(const std::string &string) + { + m_parts.push_back(message_t(string.data(), string.size())); + } + + // Push type (fixed-size) to front + template void pushtyp(const T &type) + { + static_assert(!std::is_same::value, + "Use pushstr() instead of pushtyp()"); + m_parts.push_front(message_t(&type, sizeof(type))); + } + + // Push type (fixed-size) to back + template void addtyp(const T &type) + { + static_assert(!std::is_same::value, + "Use addstr() instead of addtyp()"); + m_parts.push_back(message_t(&type, sizeof(type))); + } + + // Push message part to front + void push(message_t &&message) { m_parts.push_front(std::move(message)); } + + // Push message part to back + void add(message_t &&message) { m_parts.push_back(std::move(message)); } + + // Alias to allow std::back_inserter() + void push_back(message_t &&message) { m_parts.push_back(std::move(message)); } + + // Pop string from front + std::string popstr() + { + std::string string(m_parts.front().data(), m_parts.front().size()); + m_parts.pop_front(); + return string; + } + + // Pop type (fixed-size) from front + template T poptyp() + { + static_assert(!std::is_same::value, + "Use popstr() instead of poptyp()"); + if (sizeof(T) != m_parts.front().size()) + throw std::runtime_error( + "Invalid type, size does not match the message size"); + T type = *m_parts.front().data(); + m_parts.pop_front(); + return type; + } + + // Pop message part from front + message_t pop() + { + message_t message = std::move(m_parts.front()); + m_parts.pop_front(); + return message; + } + + // Pop message part from back + message_t remove() + { + message_t message = std::move(m_parts.back()); + m_parts.pop_back(); + return message; + } + + // get message part from front + const message_t &front() { return m_parts.front(); } + + // get message part from back + const message_t &back() { return m_parts.back(); } + + // Get pointer to a specific message part + const message_t *peek(size_t index) const { return &m_parts[index]; } + + // Get a string copy of a specific message part + std::string peekstr(size_t index) const + { + std::string string(m_parts[index].data(), m_parts[index].size()); + return string; + } + + // Peek type (fixed-size) from front + template T peektyp(size_t index) const + { + static_assert(!std::is_same::value, + "Use peekstr() instead of peektyp()"); + if (sizeof(T) != m_parts[index].size()) + throw std::runtime_error( + "Invalid type, size does not match the message size"); + T type = *m_parts[index].data(); + return type; + } + + // Create multipart from type (fixed-size) + template static multipart_t create(const T &type) + { + multipart_t multipart; + multipart.addtyp(type); + return multipart; + } + + // Copy multipart + multipart_t clone() const + { + multipart_t multipart; + for (size_t i = 0; i < size(); i++) + multipart.addmem(m_parts[i].data(), m_parts[i].size()); + return multipart; + } + + // Dump content to string + std::string str() const + { + std::stringstream ss; + for (size_t i = 0; i < m_parts.size(); i++) { + const unsigned char *data = m_parts[i].data(); + size_t size = m_parts[i].size(); + + // Dump the message as text or binary + bool isText = true; + for (size_t j = 0; j < size; j++) { + if (data[j] < 32 || data[j] > 127) { + isText = false; + break; + } + } + ss << "\n[" << std::dec << std::setw(3) << std::setfill('0') << size + << "] "; + if (size >= 1000) { + ss << "... (too big to print)"; + continue; + } + for (size_t j = 0; j < size; j++) { + if (isText) + ss << static_cast(data[j]); + else + ss << std::hex << std::setw(2) << std::setfill('0') + << static_cast(data[j]); + } + } + return ss.str(); + } + + // Check if equal to other multipart + bool equal(const multipart_t *other) const ZMQ_NOTHROW + { + return *this == *other; + } + + bool operator==(const multipart_t &other) const ZMQ_NOTHROW + { + if (size() != other.size()) + return false; + for (size_t i = 0; i < size(); i++) + if (at(i) != other.at(i)) + return false; + return true; + } + + bool operator!=(const multipart_t &other) const ZMQ_NOTHROW + { + return !(*this == other); + } + +#ifdef ZMQ_CPP11 + + // Return single part message_t encoded from this multipart_t. + message_t encode() const { return zmq::encode(*this); } + + // Decode encoded message into multiple parts and append to self. + void decode_append(const message_t &encoded) + { + zmq::decode(encoded, std::back_inserter(*this)); + } + + // Return a new multipart_t containing the decoded message_t. + static multipart_t decode(const message_t &encoded) + { + multipart_t tmp; + zmq::decode(encoded, std::back_inserter(tmp)); + return tmp; + } + +#endif + + private: + // Disable implicit copying (moving is more efficient) + multipart_t(const multipart_t &other) ZMQ_DELETED_FUNCTION; + void operator=(const multipart_t &other) ZMQ_DELETED_FUNCTION; +}; // class multipart_t + +inline std::ostream &operator<<(std::ostream &os, const multipart_t &msg) +{ + return os << msg.str(); +} + +#endif // ZMQ_HAS_RVALUE_REFS + +#if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) +class active_poller_t +{ + public: + active_poller_t() = default; + ~active_poller_t() = default; + + active_poller_t(const active_poller_t &) = delete; + active_poller_t &operator=(const active_poller_t &) = delete; + + active_poller_t(active_poller_t &&src) = default; + active_poller_t &operator=(active_poller_t &&src) = default; + + using handler_type = std::function; + + void add(zmq::socket_ref socket, event_flags events, handler_type handler) + { + if (!handler) + throw std::invalid_argument("null handler in active_poller_t::add"); + auto ret = handlers.emplace( + socket, std::make_shared(std::move(handler))); + if (!ret.second) + throw error_t(EINVAL); // already added + try { + base_poller.add(socket, events, ret.first->second.get()); + need_rebuild = true; + } + catch (...) { + // rollback + handlers.erase(socket); + throw; + } + } + + void remove(zmq::socket_ref socket) + { + base_poller.remove(socket); + handlers.erase(socket); + need_rebuild = true; + } + + void modify(zmq::socket_ref socket, event_flags events) + { + base_poller.modify(socket, events); + } + + size_t wait(std::chrono::milliseconds timeout) + { + if (need_rebuild) { + poller_events.resize(handlers.size()); + poller_handlers.clear(); + poller_handlers.reserve(handlers.size()); + for (const auto &handler : handlers) { + poller_handlers.push_back(handler.second); + } + need_rebuild = false; + } + const auto count = base_poller.wait_all(poller_events, timeout); + std::for_each(poller_events.begin(), + poller_events.begin() + static_cast(count), + [](decltype(base_poller)::event_type &event) { + assert(event.user_data != nullptr); + (*event.user_data)(event.events); + }); + return count; + } + + ZMQ_NODISCARD bool empty() const noexcept { return handlers.empty(); } + + size_t size() const noexcept { return handlers.size(); } + + private: + bool need_rebuild{false}; + + poller_t base_poller{}; + std::unordered_map> handlers{}; + std::vector poller_events{}; + std::vector> poller_handlers{}; +}; // class active_poller_t +#endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) + + +} // namespace zmq + +#endif // __ZMQ_ADDON_HPP_INCLUDED__ diff --git a/include/behaviortree_cpp/flatbuffers/base.h b/3rdparty/flatbuffers/base.h similarity index 76% rename from include/behaviortree_cpp/flatbuffers/base.h rename to 3rdparty/flatbuffers/base.h index 54a51aacb..1c19dde98 100644 --- a/include/behaviortree_cpp/flatbuffers/base.h +++ b/3rdparty/flatbuffers/base.h @@ -32,7 +32,7 @@ #include #include -#if defined(ARDUINO) && !defined(ARDUINOSTL_M_H) +#if defined(ARDUINO) && !defined(ARDUINOSTL_M_H) && defined(__AVR__) #include #else #include @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -50,10 +51,6 @@ #include #endif -#ifdef _STLPORT_VERSION - #define FLATBUFFERS_CPP98_STL -#endif - #ifdef __ANDROID__ #include #endif @@ -142,9 +139,9 @@ #endif #endif // !defined(FLATBUFFERS_LITTLEENDIAN) -#define FLATBUFFERS_VERSION_MAJOR 1 -#define FLATBUFFERS_VERSION_MINOR 12 -#define FLATBUFFERS_VERSION_REVISION 0 +#define FLATBUFFERS_VERSION_MAJOR 24 +#define FLATBUFFERS_VERSION_MINOR 3 +#define FLATBUFFERS_VERSION_REVISION 25 #define FLATBUFFERS_STRING_EXPAND(X) #X #define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X) namespace flatbuffers { @@ -158,7 +155,7 @@ namespace flatbuffers { #define FLATBUFFERS_FINAL_CLASS final #define FLATBUFFERS_OVERRIDE override #define FLATBUFFERS_EXPLICIT_CPP11 explicit - #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : flatbuffers::voffset_t + #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : ::flatbuffers::voffset_t #else #define FLATBUFFERS_FINAL_CLASS #define FLATBUFFERS_OVERRIDE @@ -237,16 +234,26 @@ namespace flatbuffers { } #define FLATBUFFERS_HAS_STRING_VIEW 1 // Check for absl::string_view - #elif __has_include("absl/strings/string_view.h") - #include "absl/strings/string_view.h" - namespace flatbuffers { - typedef absl::string_view string_view; - } - #define FLATBUFFERS_HAS_STRING_VIEW 1 + #elif __has_include("absl/strings/string_view.h") && \ + __has_include("absl/base/config.h") && \ + (__cplusplus >= 201411) + #include "absl/base/config.h" + #if !defined(ABSL_USES_STD_STRING_VIEW) + #include "absl/strings/string_view.h" + namespace flatbuffers { + typedef absl::string_view string_view; + } + #define FLATBUFFERS_HAS_STRING_VIEW 1 + #endif #endif #endif // __has_include #endif // !FLATBUFFERS_HAS_STRING_VIEW +#ifndef FLATBUFFERS_GENERAL_HEAP_ALLOC_OK + // Allow heap allocations to be used + #define FLATBUFFERS_GENERAL_HEAP_ALLOC_OK 1 +#endif // !FLATBUFFERS_GENERAL_HEAP_ALLOC_OK + #ifndef FLATBUFFERS_HAS_NEW_STRTOD // Modern (C++11) strtod and strtof functions are available for use. // 1) nan/inf strings as argument of strtod; @@ -259,9 +266,12 @@ namespace flatbuffers { #endif // !FLATBUFFERS_HAS_NEW_STRTOD #ifndef FLATBUFFERS_LOCALE_INDEPENDENT - // Enable locale independent functions {strtof_l, strtod_l,strtoll_l, strtoull_l}. - #if ((defined(_MSC_VER) && _MSC_VER >= 1800) || \ - (defined(_XOPEN_VERSION) && (_XOPEN_VERSION>=700)) && (!defined(__ANDROID_API__) || (defined(__ANDROID_API__) && (__ANDROID_API__>=21)))) + // Enable locale independent functions {strtof_l, strtod_l,strtoll_l, + // strtoull_l}. + #if (defined(_MSC_VER) && _MSC_VER >= 1800) || \ + (defined(__ANDROID_API__) && __ANDROID_API__>= 21) || \ + (defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 700)) && \ + (!defined(__Fuchsia__) && !defined(__ANDROID_API__)) #define FLATBUFFERS_LOCALE_INDEPENDENT 1 #else #define FLATBUFFERS_LOCALE_INDEPENDENT 0 @@ -269,27 +279,29 @@ namespace flatbuffers { #endif // !FLATBUFFERS_LOCALE_INDEPENDENT // Suppress Undefined Behavior Sanitizer (recoverable only). Usage: -// - __supress_ubsan__("undefined") -// - __supress_ubsan__("signed-integer-overflow") +// - FLATBUFFERS_SUPPRESS_UBSAN("undefined") +// - FLATBUFFERS_SUPPRESS_UBSAN("signed-integer-overflow") #if defined(__clang__) && (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >=7)) - #define __supress_ubsan__(type) __attribute__((no_sanitize(type))) + #define FLATBUFFERS_SUPPRESS_UBSAN(type) __attribute__((no_sanitize(type))) #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 409) - #define __supress_ubsan__(type) __attribute__((no_sanitize_undefined)) + #define FLATBUFFERS_SUPPRESS_UBSAN(type) __attribute__((no_sanitize_undefined)) #else - #define __supress_ubsan__(type) + #define FLATBUFFERS_SUPPRESS_UBSAN(type) #endif -// This is constexpr function used for checking compile-time constants. -// Avoid `#pragma warning(disable: 4127) // C4127: expression is constant`. -template FLATBUFFERS_CONSTEXPR inline bool IsConstTrue(T t) { - return !!t; +namespace flatbuffers { + // This is constexpr function used for checking compile-time constants. + // Avoid `#pragma warning(disable: 4127) // C4127: expression is constant`. + template FLATBUFFERS_CONSTEXPR inline bool IsConstTrue(T t) { + return !!t; + } } // Enable C++ attribute [[]] if std:c++17 or higher. #if ((__cplusplus >= 201703L) \ || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L))) // All attributes unknown to an implementation are ignored without causing an error. - #define FLATBUFFERS_ATTRIBUTE(attr) [[attr]] + #define FLATBUFFERS_ATTRIBUTE(attr) attr #define FLATBUFFERS_FALLTHROUGH() [[fallthrough]] #else @@ -314,9 +326,11 @@ namespace flatbuffers { // Also, using a consistent offset type maintains compatibility of serialized // offset values between 32bit and 64bit systems. typedef uint32_t uoffset_t; +typedef uint64_t uoffset64_t; // Signed offsets for references that can go in both directions. typedef int32_t soffset_t; +typedef int64_t soffset64_t; // Offset/index used in v-tables, can be changed to uint8_t in // format forks to save a bit of space if desired. @@ -325,10 +339,23 @@ typedef uint16_t voffset_t; typedef uintmax_t largest_scalar_t; // In 32bits, this evaluates to 2GB - 1 -#define FLATBUFFERS_MAX_BUFFER_SIZE ((1ULL << (sizeof(::flatbuffers::soffset_t) * 8 - 1)) - 1) +#define FLATBUFFERS_MAX_BUFFER_SIZE std::numeric_limits<::flatbuffers::soffset_t>::max() +#define FLATBUFFERS_MAX_64_BUFFER_SIZE std::numeric_limits<::flatbuffers::soffset64_t>::max() + +// The minimum size buffer that can be a valid flatbuffer. +// Includes the offset to the root table (uoffset_t), the offset to the vtable +// of the root table (soffset_t), the size of the vtable (uint16_t), and the +// size of the referring table (uint16_t). +#define FLATBUFFERS_MIN_BUFFER_SIZE sizeof(uoffset_t) + sizeof(soffset_t) + \ + sizeof(uint16_t) + sizeof(uint16_t) // We support aligning the contents of buffers up to this size. -#define FLATBUFFERS_MAX_ALIGNMENT 16 +#ifndef FLATBUFFERS_MAX_ALIGNMENT + #define FLATBUFFERS_MAX_ALIGNMENT 32 +#endif + +/// @brief The length of a FlatBuffer file header. +static const size_t kFileIdentifierLength = 4; inline bool VerifyAlignmentRequirements(size_t align, size_t min_align = 1) { return (min_align <= align) && (align <= (FLATBUFFERS_MAX_ALIGNMENT)) && @@ -336,7 +363,6 @@ inline bool VerifyAlignmentRequirements(size_t align, size_t min_align = 1) { } #if defined(_MSC_VER) - #pragma warning(disable: 4351) // C4351: new behavior: elements of array ... will be default initialized #pragma warning(push) #pragma warning(disable: 4127) // C4127: conditional expression is constant #endif @@ -397,7 +423,7 @@ template T EndianScalar(T t) { template // UBSAN: C++ aliasing type rules, see std::bit_cast<> for details. -__supress_ubsan__("alignment") +FLATBUFFERS_SUPPRESS_UBSAN("alignment") T ReadScalar(const void *p) { return EndianScalar(*reinterpret_cast(p)); } @@ -411,13 +437,13 @@ T ReadScalar(const void *p) { template // UBSAN: C++ aliasing type rules, see std::bit_cast<> for details. -__supress_ubsan__("alignment") +FLATBUFFERS_SUPPRESS_UBSAN("alignment") void WriteScalar(void *p, T t) { *reinterpret_cast(p) = EndianScalar(t); } template struct Offset; -template __supress_ubsan__("alignment") void WriteScalar(void *p, Offset t) { +template FLATBUFFERS_SUPPRESS_UBSAN("alignment") void WriteScalar(void *p, Offset t) { *reinterpret_cast(p) = EndianScalar(t.o); } @@ -428,10 +454,43 @@ template __supress_ubsan__("alignment") void WriteScalar(void *p, Of // Computes how many bytes you'd have to pad to be able to write an // "scalar_size" scalar if the buffer had grown to "buf_size" (downwards in // memory). -__supress_ubsan__("unsigned-integer-overflow") +FLATBUFFERS_SUPPRESS_UBSAN("unsigned-integer-overflow") inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) { return ((~buf_size) + 1) & (scalar_size - 1); } +// Generic 'operator==' with conditional specialisations. +// T e - new value of a scalar field. +// T def - default of scalar (is known at compile-time). +template inline bool IsTheSameAs(T e, T def) { return e == def; } + +#if defined(FLATBUFFERS_NAN_DEFAULTS) && \ + defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) +// Like `operator==(e, def)` with weak NaN if T=(float|double). +template inline bool IsFloatTheSameAs(T e, T def) { + return (e == def) || ((def != def) && (e != e)); +} +template<> inline bool IsTheSameAs(float e, float def) { + return IsFloatTheSameAs(e, def); +} +template<> inline bool IsTheSameAs(double e, double def) { + return IsFloatTheSameAs(e, def); +} +#endif + +// Check 'v' is out of closed range [low; high]. +// Workaround for GCC warning [-Werror=type-limits]: +// comparison is always true due to limited range of data type. +template +inline bool IsOutRange(const T &v, const T &low, const T &high) { + return (v < low) || (high < v); +} + +// Check 'v' is in closed range [low; high]. +template +inline bool IsInRange(const T &v, const T &low, const T &high) { + return !IsOutRange(v, low, high); +} + } // namespace flatbuffers #endif // FLATBUFFERS_BASE_H_ diff --git a/3rdparty/lexy/CMakeLists.txt b/3rdparty/lexy/CMakeLists.txt index f24a3feb5..a76693a9e 100644 --- a/3rdparty/lexy/CMakeLists.txt +++ b/3rdparty/lexy/CMakeLists.txt @@ -1,11 +1,79 @@ -# Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +# Copyright (C) 2020-2024 Jonathan Müller and lexy contributors # SPDX-License-Identifier: BSL-1.0 cmake_minimum_required(VERSION 3.8) -project(lexy VERSION 2022.05.1 LANGUAGES CXX) +project(lexy VERSION 2022.12.1 LANGUAGES CXX) set(LEXY_USER_CONFIG_HEADER "" CACHE FILEPATH "The user config header for lexy.") option(LEXY_FORCE_CPP17 "Whether or not lexy should use C++17 even if compiler supports C++20." OFF) add_subdirectory(src) +option(LEXY_ENABLE_INSTALL "whether or not to enable the install rule" ON) +if(LEXY_ENABLE_INSTALL) + include(CMakePackageConfigHelpers) + include(GNUInstallDirs) + + install(TARGETS lexy lexy_core lexy_file lexy_unicode lexy_ext _lexy_base lexy_dev + EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + + install(EXPORT ${PROJECT_NAME}Targets + NAMESPACE foonathan:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + configure_package_config_file( + cmake/lexyConfig.cmake.in + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + # YYYY.MM.N1 is compatible with YYYY.MM.N2. + write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + COMPATIBILITY SameMinorVersion) + + install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + install(DIRECTORY include/lexy include/lexy_ext + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN "*.hpp") +endif() + +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + cmake_minimum_required(VERSION 3.18) + option(LEXY_BUILD_BENCHMARKS "whether or not benchmarks should be built" OFF) + option(LEXY_BUILD_EXAMPLES "whether or not examples should be built" ON) + option(LEXY_BUILD_TESTS "whether or not tests should be built" ON) + option(LEXY_BUILD_DOCS "whether or not docs should be built" OFF) + option(LEXY_BUILD_PACKAGE "whether or not the package should be built" ON) + + if(LEXY_BUILD_PACKAGE) + set(package_files include/ src/ cmake/ CMakeLists.txt LICENSE) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip + COMMAND ${CMAKE_COMMAND} -E tar c ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip --format=zip -- ${package_files} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${package_files}) + add_custom_target(lexy_package DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip) + endif() + + if(LEXY_BUILD_EXAMPLES) + add_subdirectory(examples) + endif() + if(LEXY_BUILD_BENCHMARKS) + add_subdirectory(benchmarks) + endif() + if(LEXY_BUILD_TESTS) + set(DOCTEST_NO_INSTALL ON) + enable_testing() + add_subdirectory(tests) + endif() + if(LEXY_BUILD_DOCS) + add_subdirectory(docs EXCLUDE_FROM_ALL) + endif() +endif() diff --git a/3rdparty/lexy/README.adoc b/3rdparty/lexy/README.adoc index 069ab5216..6bc88487f 100644 --- a/3rdparty/lexy/README.adoc +++ b/3rdparty/lexy/README.adoc @@ -113,8 +113,9 @@ Why should I use lexy over XYZ?:: http://boost-spirit.com/home/[Boost.Spirit]::: The main difference: it is not a Boost library. - Otherwise, it is just a different implementation with a different flavor. - Use lexy if you like lexy more. + In addition, Boost.Spirit is quite old and doesn't support e.g. non-common ranges as input. + Boost.Spirit also eagerly creates attributes from the rules, which can lead to nested tuples/variants while lexy uses callbacks which enables zero-copy parsing directly into your own data structure. + However, lexy's grammar is more verbose and designed to parser bigger grammars instead of the small one-off rules that Boost.Spirit is good at. https://github.com/taocpp/PEGTL[PEGTL]::: PEGTL is very similar and was a big inspiration. The biggest difference is that lexy uses an operator based DSL instead of inheriting from templated classes as PEGTL does; diff --git a/3rdparty/lexy/cmake/lexyConfig.cmake.in b/3rdparty/lexy/cmake/lexyConfig.cmake.in new file mode 100644 index 000000000..e6dc89d30 --- /dev/null +++ b/3rdparty/lexy/cmake/lexyConfig.cmake.in @@ -0,0 +1,8 @@ +# Copyright (C) 2020-2024 Jonathan Müller and lexy contributors +# SPDX-License-Identifier: BSL-1.0 + +# lexy CMake configuration file. + +@PACKAGE_INIT@ + +include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") diff --git a/3rdparty/lexy/include/lexy/_detail/any_ref.hpp b/3rdparty/lexy/include/lexy/_detail/any_ref.hpp new file mode 100644 index 000000000..9eca714b2 --- /dev/null +++ b/3rdparty/lexy/include/lexy/_detail/any_ref.hpp @@ -0,0 +1,68 @@ +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors +// SPDX-License-Identifier: BSL-1.0 + +#ifndef LEXY_DETAIL_ANY_REF_HPP_INCLUDED +#define LEXY_DETAIL_ANY_REF_HPP_INCLUDED + +#include + +// Essentially a void*, but we can cast it in a constexpr context. +// The cost is an extra layer of indirection. + +namespace lexy::_detail +{ +template +class any_holder; + +// Store a pointer to this instead of a void*. +class any_base +{ +public: + any_base(const any_base&) = delete; + any_base& operator=(const any_base&) = delete; + + template + constexpr T& get() noexcept + { + return static_cast*>(this)->get(); + } + template + constexpr const T& get() const noexcept + { + return static_cast*>(this)->get(); + } + +private: + constexpr any_base() = default; + ~any_base() = default; + + template + friend class any_holder; +}; + +using any_ref = any_base*; +using any_cref = const any_base*; + +// Need to store the object in here. +template +class any_holder : public any_base +{ +public: + constexpr explicit any_holder(T&& obj) : _obj(LEXY_MOV(obj)) {} + + constexpr T& get() noexcept + { + return _obj; + } + constexpr const T& get() const noexcept + { + return _obj; + } + +private: + T _obj; +}; +} // namespace lexy::_detail + +#endif // LEXY_DETAIL_ANY_REF_HPP_INCLUDED + diff --git a/3rdparty/lexy/include/lexy/_detail/assert.hpp b/3rdparty/lexy/include/lexy/_detail/assert.hpp index a2b7f7f7a..52aa115de 100644 --- a/3rdparty/lexy/include/lexy/_detail/assert.hpp +++ b/3rdparty/lexy/include/lexy/_detail/assert.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_ASSERT_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/_detail/buffer_builder.hpp b/3rdparty/lexy/include/lexy/_detail/buffer_builder.hpp index bfa7c4902..94ba1fd27 100644 --- a/3rdparty/lexy/include/lexy/_detail/buffer_builder.hpp +++ b/3rdparty/lexy/include/lexy/_detail/buffer_builder.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED @@ -38,7 +38,7 @@ class buffer_builder ::operator delete(_data); } - buffer_builder(const buffer_builder&) = delete; + buffer_builder(const buffer_builder&) = delete; buffer_builder& operator=(const buffer_builder&) = delete; // The total capacity: read + write. diff --git a/3rdparty/lexy/include/lexy/_detail/code_point.hpp b/3rdparty/lexy/include/lexy/_detail/code_point.hpp index 0e1c72204..bc805b11e 100644 --- a/3rdparty/lexy/include/lexy/_detail/code_point.hpp +++ b/3rdparty/lexy/include/lexy/_detail/code_point.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_CODE_POINT_HPP_INCLUDED @@ -133,9 +133,9 @@ enum class cp_error template struct cp_result { - char32_t cp; - cp_error error; - typename Reader::iterator end; + char32_t cp; + cp_error error; + typename Reader::marker end; }; template @@ -144,20 +144,21 @@ constexpr cp_result parse_code_point(Reader reader) if constexpr (std::is_same_v) { if (reader.peek() == Reader::encoding::eof()) - return {{}, cp_error::eof, reader.position()}; + return {{}, cp_error::eof, reader.current()}; auto cur = reader.peek(); reader.bump(); auto cp = static_cast(cur); if (cp <= 0x7F) - return {cp, cp_error::success, reader.position()}; + return {cp, cp_error::success, reader.current()}; else - return {cp, cp_error::out_of_range, reader.position()}; + return {cp, cp_error::out_of_range, reader.current()}; } else if constexpr (std::is_same_v // || std::is_same_v) { + using uchar_t = unsigned char; constexpr auto payload_lead1 = 0b0111'1111; constexpr auto payload_lead2 = 0b0001'1111; constexpr auto payload_lead3 = 0b0000'1111; @@ -170,24 +171,24 @@ constexpr cp_result parse_code_point(Reader reader) constexpr auto pattern_lead4 = 0b11110 << 3; constexpr auto pattern_cont = 0b10 << 6; - auto first = reader.peek(); + auto first = uchar_t(reader.peek()); if ((first & ~payload_lead1) == pattern_lead1) { // ASCII character. reader.bump(); - return {first, cp_error::success, reader.position()}; + return {first, cp_error::success, reader.current()}; } else if ((first & ~payload_cont) == pattern_cont) { - return {{}, cp_error::leads_with_trailing, reader.position()}; + return {{}, cp_error::leads_with_trailing, reader.current()}; } else if ((first & ~payload_lead2) == pattern_lead2) { reader.bump(); - auto second = reader.peek(); + auto second = uchar_t(reader.peek()); if ((second & ~payload_cont) != pattern_cont) - return {{}, cp_error::missing_trailing, reader.position()}; + return {{}, cp_error::missing_trailing, reader.current()}; reader.bump(); auto result = char32_t(first & payload_lead2); @@ -196,22 +197,22 @@ constexpr cp_result parse_code_point(Reader reader) // C0 and C1 are overlong ASCII. if (first == 0xC0 || first == 0xC1) - return {result, cp_error::overlong_sequence, reader.position()}; + return {result, cp_error::overlong_sequence, reader.current()}; else - return {result, cp_error::success, reader.position()}; + return {result, cp_error::success, reader.current()}; } else if ((first & ~payload_lead3) == pattern_lead3) { reader.bump(); - auto second = reader.peek(); + auto second = uchar_t(reader.peek()); if ((second & ~payload_cont) != pattern_cont) - return {{}, cp_error::missing_trailing, reader.position()}; + return {{}, cp_error::missing_trailing, reader.current()}; reader.bump(); - auto third = reader.peek(); + auto third = uchar_t(reader.peek()); if ((third & ~payload_cont) != pattern_cont) - return {{}, cp_error::missing_trailing, reader.position()}; + return {{}, cp_error::missing_trailing, reader.current()}; reader.bump(); auto result = char32_t(first & payload_lead3); @@ -222,29 +223,29 @@ constexpr cp_result parse_code_point(Reader reader) auto cp = result; if (0xD800 <= cp && cp <= 0xDFFF) - return {cp, cp_error::surrogate, reader.position()}; + return {cp, cp_error::surrogate, reader.current()}; else if (first == 0xE0 && second < 0xA0) - return {cp, cp_error::overlong_sequence, reader.position()}; + return {cp, cp_error::overlong_sequence, reader.current()}; else - return {cp, cp_error::success, reader.position()}; + return {cp, cp_error::success, reader.current()}; } else if ((first & ~payload_lead4) == pattern_lead4) { reader.bump(); - auto second = reader.peek(); + auto second = uchar_t(reader.peek()); if ((second & ~payload_cont) != pattern_cont) - return {{}, cp_error::missing_trailing, reader.position()}; + return {{}, cp_error::missing_trailing, reader.current()}; reader.bump(); - auto third = reader.peek(); + auto third = uchar_t(reader.peek()); if ((third & ~payload_cont) != pattern_cont) - return {{}, cp_error::missing_trailing, reader.position()}; + return {{}, cp_error::missing_trailing, reader.current()}; reader.bump(); - auto fourth = reader.peek(); + auto fourth = uchar_t(reader.peek()); if ((fourth & ~payload_cont) != pattern_cont) - return {{}, cp_error::missing_trailing, reader.position()}; + return {{}, cp_error::missing_trailing, reader.current()}; reader.bump(); auto result = char32_t(first & payload_lead4); @@ -257,15 +258,15 @@ constexpr cp_result parse_code_point(Reader reader) auto cp = result; if (cp > 0x10'FFFF) - return {cp, cp_error::out_of_range, reader.position()}; + return {cp, cp_error::out_of_range, reader.current()}; else if (first == 0xF0 && second < 0x90) - return {cp, cp_error::overlong_sequence, reader.position()}; + return {cp, cp_error::overlong_sequence, reader.current()}; else - return {cp, cp_error::success, reader.position()}; + return {cp, cp_error::success, reader.current()}; } else // FE or FF { - return {{}, cp_error::eof, reader.position()}; + return {{}, cp_error::eof, reader.current()}; } } else if constexpr (std::is_same_v) @@ -277,18 +278,18 @@ constexpr cp_result parse_code_point(Reader reader) constexpr auto pattern2 = 0b110111 << 10; if (reader.peek() == Reader::encoding::eof()) - return {{}, cp_error::eof, reader.position()}; + return {{}, cp_error::eof, reader.current()}; auto first = char16_t(reader.peek()); if ((first & ~payload1) == pattern1) { reader.bump(); if (reader.peek() == Reader::encoding::eof()) - return {{}, cp_error::missing_trailing, reader.position()}; + return {{}, cp_error::missing_trailing, reader.current()}; auto second = char16_t(reader.peek()); if ((second & ~payload2) != pattern2) - return {{}, cp_error::missing_trailing, reader.position()}; + return {{}, cp_error::missing_trailing, reader.current()}; reader.bump(); // We've got a valid code point. @@ -296,34 +297,34 @@ constexpr cp_result parse_code_point(Reader reader) result <<= 10; result |= char32_t(second & payload2); result |= 0x10000; - return {result, cp_error::success, reader.position()}; + return {result, cp_error::success, reader.current()}; } else if ((first & ~payload2) == pattern2) { - return {{}, cp_error::leads_with_trailing, reader.position()}; + return {{}, cp_error::leads_with_trailing, reader.current()}; } else { // Single code unit code point; always valid. reader.bump(); - return {first, cp_error::success, reader.position()}; + return {first, cp_error::success, reader.current()}; } } else if constexpr (std::is_same_v) { if (reader.peek() == Reader::encoding::eof()) - return {{}, cp_error::eof, reader.position()}; + return {{}, cp_error::eof, reader.current()}; auto cur = reader.peek(); reader.bump(); auto cp = cur; if (cp > 0x10'FFFF) - return {cp, cp_error::out_of_range, reader.position()}; + return {cp, cp_error::out_of_range, reader.current()}; else if (0xD800 <= cp && cp <= 0xDFFF) - return {cp, cp_error::surrogate, reader.position()}; + return {cp, cp_error::surrogate, reader.current()}; else - return {cp, cp_error::success, reader.position()}; + return {cp, cp_error::success, reader.current()}; } else { @@ -340,7 +341,7 @@ constexpr void recover_code_point(Reader& reader, cp_result result) { case cp_error::success: // Consume the entire code point. - reader.set_position(result.end); + reader.reset(result.end); break; case cp_error::eof: // We don't need to do anything to "recover" from EOF. @@ -348,7 +349,7 @@ constexpr void recover_code_point(Reader& reader, cp_result result) case cp_error::leads_with_trailing: // Invalid code unit, consume to recover. - LEXY_PRECONDITION(result.end == reader.position()); + LEXY_PRECONDITION(result.end.position() == reader.position()); reader.bump(); break; @@ -357,7 +358,7 @@ constexpr void recover_code_point(Reader& reader, cp_result result) case cp_error::out_of_range: case cp_error::overlong_sequence: // Consume all the invalid code units to recover. - reader.set_position(result.end); + reader.reset(result.end); break; } } diff --git a/3rdparty/lexy/include/lexy/_detail/config.hpp b/3rdparty/lexy/include/lexy/_detail/config.hpp index 4263cff7b..4aa40135b 100644 --- a/3rdparty/lexy/include/lexy/_detail/config.hpp +++ b/3rdparty/lexy/include/lexy/_detail/config.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_CONFIG_HPP_INCLUDED @@ -17,6 +17,14 @@ # endif #endif +#ifndef LEXY_HAS_UNICODE_DATABASE +# define LEXY_HAS_UNICODE_DATABASE 0 +#endif + +#ifndef LEXY_EXPERIMENTAL +# define LEXY_EXPERIMENTAL 0 +#endif + //=== utility traits===// #define LEXY_MOV(...) static_cast&&>(__VA_ARGS__) #define LEXY_FWD(...) static_cast(__VA_ARGS__) @@ -90,6 +98,21 @@ using type_or = std::conditional_t, Fallback, T>; # define LEXY_CONSTEVAL constexpr #endif +//=== constexpr ===// +#ifndef LEXY_HAS_CONSTEXPR_DTOR +# if __cpp_constexpr_dynamic_alloc +# define LEXY_HAS_CONSTEXPR_DTOR 1 +# else +# define LEXY_HAS_CONSTEXPR_DTOR 0 +# endif +#endif + +#if LEXY_HAS_CONSTEXPR_DTOR +# define LEXY_CONSTEXPR_DTOR constexpr +#else +# define LEXY_CONSTEXPR_DTOR +#endif + //=== char8_t ===// #ifndef LEXY_HAS_CHAR8_T # if __cpp_char8_t diff --git a/3rdparty/lexy/include/lexy/_detail/detect.hpp b/3rdparty/lexy/include/lexy/_detail/detect.hpp index 9aae72b6e..7534c44c4 100644 --- a/3rdparty/lexy/include/lexy/_detail/detect.hpp +++ b/3rdparty/lexy/include/lexy/_detail/detect.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Müller +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_DETECT_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/_detail/integer_sequence.hpp b/3rdparty/lexy/include/lexy/_detail/integer_sequence.hpp index cf0bb7be5..36e3cb08c 100644 --- a/3rdparty/lexy/include/lexy/_detail/integer_sequence.hpp +++ b/3rdparty/lexy/include/lexy/_detail/integer_sequence.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_INTEGER_SEQUENCE_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/_detail/invoke.hpp b/3rdparty/lexy/include/lexy/_detail/invoke.hpp index 2301726e9..8604e582e 100644 --- a/3rdparty/lexy/include/lexy/_detail/invoke.hpp +++ b/3rdparty/lexy/include/lexy/_detail/invoke.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_INVOKE_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/_detail/iterator.hpp b/3rdparty/lexy/include/lexy/_detail/iterator.hpp index a058b51ac..42ec995a4 100644 --- a/3rdparty/lexy/include/lexy/_detail/iterator.hpp +++ b/3rdparty/lexy/include/lexy/_detail/iterator.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_ITERATOR_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/_detail/lazy_init.hpp b/3rdparty/lexy/include/lexy/_detail/lazy_init.hpp index eef769936..29fcfa308 100644 --- a/3rdparty/lexy/include/lexy/_detail/lazy_init.hpp +++ b/3rdparty/lexy/include/lexy/_detail/lazy_init.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_LAZY_INIT_HPP_INCLUDED @@ -6,6 +6,7 @@ #include #include +#include namespace lexy::_detail { @@ -25,6 +26,12 @@ struct _lazy_init_storage_trivial constexpr _lazy_init_storage_trivial(int, Args&&... args) : _init(true), _value(LEXY_FWD(args)...) {} + + template + constexpr void _construct(Args&&... args) + { + *this = _lazy_init_storage_trivial(0, LEXY_FWD(args)...); + } }; template @@ -40,24 +47,29 @@ struct _lazy_init_storage_non_trivial constexpr _lazy_init_storage_non_trivial() noexcept : _init(false), _empty() {} template - constexpr _lazy_init_storage_non_trivial(int, Args&&... args) - : _init(true), _value(LEXY_FWD(args)...) - {} + LEXY_CONSTEXPR_DTOR void _construct(Args&&... args) + { + _detail::construct_at(&_value, LEXY_FWD(args)...); + _init = true; + } - ~_lazy_init_storage_non_trivial() noexcept + // Cannot add noexcept due to https://github.com/llvm/llvm-project/issues/59854. + LEXY_CONSTEXPR_DTOR ~_lazy_init_storage_non_trivial() /* noexcept */ { if (_init) _value.~T(); } - _lazy_init_storage_non_trivial(_lazy_init_storage_non_trivial&& other) noexcept + LEXY_CONSTEXPR_DTOR _lazy_init_storage_non_trivial( + _lazy_init_storage_non_trivial&& other) noexcept : _init(other._init), _empty() { if (_init) - ::new (static_cast(&_value)) T(LEXY_MOV(other._value)); + _detail::construct_at(&_value, LEXY_MOV(other._value)); } - _lazy_init_storage_non_trivial& operator=(_lazy_init_storage_non_trivial&& other) noexcept + LEXY_CONSTEXPR_DTOR _lazy_init_storage_non_trivial& operator=( + _lazy_init_storage_non_trivial&& other) noexcept { if (_init && other._init) _value = LEXY_MOV(other._value); @@ -68,7 +80,7 @@ struct _lazy_init_storage_non_trivial } else if (!_init && other._init) { - ::new (static_cast(&_value)) T(LEXY_MOV(other._value)); + _detail::construct_at(&_value, LEXY_MOV(other._value)); _init = true; } else @@ -104,9 +116,11 @@ class lazy_init : _lazy_init_storage template constexpr T& emplace(Args&&... args) { - LEXY_PRECONDITION(!*this); + if (*this) + this->_value = T(LEXY_FWD(args)...); + else + this->_construct(LEXY_FWD(args)...); - *this = lazy_init(0, LEXY_FWD(args)...); return this->_value; } @@ -169,7 +183,6 @@ class lazy_init constexpr T& emplace(T& ref) { - LEXY_PRECONDITION(!*this); _ptr = &ref; return ref; } @@ -210,7 +223,6 @@ class lazy_init constexpr void emplace() { - LEXY_PRECONDITION(!*this); _init = true; } template diff --git a/3rdparty/lexy/include/lexy/_detail/memory_resource.hpp b/3rdparty/lexy/include/lexy/_detail/memory_resource.hpp index bae7160ba..324a96c84 100644 --- a/3rdparty/lexy/include/lexy/_detail/memory_resource.hpp +++ b/3rdparty/lexy/include/lexy/_detail/memory_resource.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_MEMORY_RESOURCE_HPP_INCLUDED @@ -29,7 +29,7 @@ class default_memory_resource static void* allocate(std::size_t bytes, std::size_t alignment) { if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - return ::operator new (bytes, std::align_val_t{alignment}); + return ::operator new(bytes, std::align_val_t{alignment}); else return ::operator new(bytes); } @@ -47,14 +47,14 @@ class default_memory_resource #ifdef __cpp_sized_deallocation if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - ::operator delete (ptr, bytes, std::align_val_t{alignment}); + ::operator delete(ptr, bytes, std::align_val_t{alignment}); else ::operator delete(ptr, bytes); #else (void)bytes; if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - ::operator delete (ptr, std::align_val_t{alignment}); + ::operator delete(ptr, std::align_val_t{alignment}); else ::operator delete(ptr); #endif @@ -140,9 +140,8 @@ using memory_resource_ptr _memory_resource_ptr>>; // clang-format on -template || std::is_empty_v>> +template + || std::is_empty_v>> constexpr MemoryResource* get_memory_resource() { return nullptr; diff --git a/3rdparty/lexy/include/lexy/_detail/nttp_string.hpp b/3rdparty/lexy/include/lexy/_detail/nttp_string.hpp index 3b44e5fd3..7301914a8 100644 --- a/3rdparty/lexy/include/lexy/_detail/nttp_string.hpp +++ b/3rdparty/lexy/include/lexy/_detail/nttp_string.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_NTTP_STRING_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/_detail/stateless_lambda.hpp b/3rdparty/lexy/include/lexy/_detail/stateless_lambda.hpp index d7f7f409f..63c8dc7e5 100644 --- a/3rdparty/lexy/include/lexy/_detail/stateless_lambda.hpp +++ b/3rdparty/lexy/include/lexy/_detail/stateless_lambda.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_STATELESS_LAMBDA_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/_detail/std.hpp b/3rdparty/lexy/include/lexy/_detail/std.hpp index aa386e56c..bb3381f08 100644 --- a/3rdparty/lexy/include/lexy/_detail/std.hpp +++ b/3rdparty/lexy/include/lexy/_detail/std.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_STD_HPP_INCLUDED @@ -6,6 +6,7 @@ #include +//=== iterator tags ===// #if defined(__GLIBCXX__) namespace std @@ -35,5 +36,63 @@ struct bidirectional_iterator_tag; #endif +//=== (constexpr) construct_at ===// +#if !LEXY_HAS_CONSTEXPR_DTOR + +namespace lexy::_detail +{ +// We don't have constexpr dtor's, so this is just a regular function. +template +T* construct_at(T* ptr, Args&&... args) +{ + return ::new ((void*)ptr) T(LEXY_FWD(args)...); +} +} // namespace lexy::_detail + +#elif defined(_MSC_VER) + +namespace lexy::_detail +{ +// MSVC can make it constexpr if marked with an attribute given by a macro. +template +constexpr T* construct_at(T* ptr, Args&&... args) +{ +# if defined(_MSVC_CONSTEXPR) + _MSVC_CONSTEXPR +# endif + return ::new ((void*)ptr) T(LEXY_FWD(args)...); +} +} // namespace lexy::_detail + +#else + +namespace lexy::_detail +{ +struct _construct_at_tag +{}; +} // namespace lexy::_detail + +namespace std +{ +// GCC only allows constexpr placement new inside a function called `std::construct_at`. +// So we write our own. +template +constexpr T* construct_at(lexy::_detail::_construct_at_tag, T* ptr, Args&&... args) +{ + return ::new ((void*)ptr) T(LEXY_FWD(args)...); +} +} // namespace std + +namespace lexy::_detail +{ +template +constexpr T* construct_at(T* ptr, Args&&... args) +{ + return std::construct_at(lexy::_detail::_construct_at_tag{}, ptr, LEXY_FWD(args)...); +} +} // namespace lexy::_detail + +#endif + #endif // LEXY_DETAIL_STD_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/_detail/string_view.hpp b/3rdparty/lexy/include/lexy/_detail/string_view.hpp index 9a1d9896e..41d42bc42 100644 --- a/3rdparty/lexy/include/lexy/_detail/string_view.hpp +++ b/3rdparty/lexy/include/lexy/_detail/string_view.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED @@ -136,10 +136,18 @@ class basic_string_view } } - constexpr bool starts_with(basic_string_view prefix) const + constexpr bool starts_with(basic_string_view prefix) const noexcept { return substr(0, prefix.size()) == prefix; } + constexpr bool try_remove_prefix(basic_string_view prefix) noexcept + { + if (!starts_with(prefix)) + return false; + + remove_prefix(prefix.length()); + return true; + } constexpr std::size_t find(basic_string_view str, std::size_t pos = 0) const noexcept { @@ -197,7 +205,7 @@ struct _string_view_holder> }; template -constexpr const auto* make_cstr = _string_view_holder::value; +inline constexpr const auto* make_cstr = _string_view_holder::value; } // namespace lexy::_detail #endif // LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/_detail/swar.hpp b/3rdparty/lexy/include/lexy/_detail/swar.hpp new file mode 100644 index 000000000..d7734d0f4 --- /dev/null +++ b/3rdparty/lexy/include/lexy/_detail/swar.hpp @@ -0,0 +1,247 @@ +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors +// SPDX-License-Identifier: BSL-1.0 + +#ifndef LEXY_DETAIL_SWAR_HPP_INCLUDED +#define LEXY_DETAIL_SWAR_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# include +#endif + +namespace lexy::_detail +{ +// Contains the chars in little endian order; rightmost bits are first char. +using swar_int = std::uintmax_t; + +// The number of chars that can fit into one SWAR. +template +constexpr auto swar_length = [] { + static_assert(sizeof(CharT) < sizeof(swar_int) && sizeof(swar_int) % sizeof(CharT) == 0); + return sizeof(swar_int) / sizeof(CharT); +}(); + +template +constexpr auto char_bit_size = sizeof(CharT) * CHAR_BIT; + +template +constexpr auto make_uchar(CharT c) +{ + if constexpr (std::is_same_v) + // Not all libstdc++ support char8_t and std::make_unsigned_t. + return c; + else + return std::make_unsigned_t(c); +} +template +using uchar_t = decltype(make_uchar(CharT())); + +// Returns a swar_int filled with the specific char. +template +constexpr swar_int swar_fill(CharT _c) +{ + auto c = make_uchar(_c); + + auto result = swar_int(0); + for (auto i = 0u; i != swar_length; ++i) + { + result <<= char_bit_size; + result |= c; + } + return result; +} + +// Returns a swar_int filled with the complement of the specific char. +template +constexpr swar_int swar_fill_compl(CharT _c) +{ + auto c = uchar_t(~uchar_t(_c)); + + auto result = swar_int(0); + for (auto i = 0u; i != swar_length; ++i) + { + result <<= char_bit_size; + result |= c; + } + return result; +} + +constexpr void _swar_pack(swar_int&, int) {} +template +constexpr void _swar_pack(swar_int& result, int index, H h, T... t) +{ + if (std::size_t(index) == char_bit_size) + return; + + if (index >= 0) + result |= swar_int(make_uchar(h)) << index; + + _swar_pack(result, index + int(char_bit_size), t...); +} + +template +struct swar_pack_result +{ + swar_int value; + swar_int mask; + std::size_t count; + + constexpr CharT operator[](std::size_t idx) const + { + constexpr auto mask = (swar_int(1) << char_bit_size)-1; + return (value >> idx * char_bit_size)&mask; + } +}; + +// Returns a swar_int containing the specified characters. +// If more are provided than fit, will only take the first couple ones. +template +constexpr auto swar_pack(CharT... cs) +{ + using char_type = std::common_type_t; + swar_pack_result result{0, 0, 0}; + + _swar_pack(result.value, -SkipFirstNChars * int(char_bit_size), cs...); + + auto count = int(sizeof...(CharT)) - SkipFirstNChars; + if (count <= 0) + { + result.mask = 0; + result.count = 0; + } + else if (count >= int(swar_length)) + { + result.mask = swar_int(-1); + result.count = swar_length; + } + else + { + result.mask = swar_int(swar_int(1) << count * int(char_bit_size)) - 1; + result.count = std::size_t(count); + } + + return result; +} + +// Returns the index of the char that is different between lhs and rhs. +template +constexpr std::size_t swar_find_difference(swar_int lhs, swar_int rhs) +{ + if (lhs == rhs) + return swar_length; + + auto mask = lhs ^ rhs; + +#if defined(__GNUC__) + auto bit_idx = __builtin_ctzll(mask); +#elif defined(_MSC_VER) + unsigned long bit_idx; + if (!_BitScanForward64(&bit_idx, mask)) + bit_idx = 64; +#else +# error "unsupported compiler; please file an issue" +#endif + + return std::size_t(bit_idx) / char_bit_size; +} + +// Returns true if v has a char less than N. +template +constexpr bool swar_has_char_less(swar_int v) +{ + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + + constexpr auto offset = swar_fill(CharT(N)); + auto zero_or_msb = v - offset; + + constexpr auto msb_mask = swar_fill(CharT(1 << (char_bit_size - 1))); + auto not_msb = ~v & msb_mask; + + return zero_or_msb & not_msb; +} + +// Returns true if v has a zero char. +template +constexpr bool swar_has_zero(swar_int v) +{ + return swar_has_char_less(v); +} + +// Returns true if v contains the specified char. +template +constexpr bool swar_has_char(swar_int v) +{ + if constexpr (C == 0) + { + return swar_has_zero(v); + } + else + { + constexpr auto mask = swar_fill(C); + return swar_has_zero(v ^ mask); + } +} +} // namespace lexy::_detail + +namespace lexy::_detail +{ +struct _swar_base +{}; +template +constexpr auto is_swar_reader = std::is_base_of_v<_swar_base, Reader>; + +template +class swar_reader_base : _swar_base +{ +public: + swar_int peek_swar() const + { + auto ptr = static_cast(*this).position(); + + swar_int result; +#if LEXY_IS_LITTLE_ENDIAN + std::memcpy(&result, ptr, sizeof(swar_int)); +#else + using char_type = typename Derived::encoding::char_type; + auto dst = reinterpret_cast(&result); + auto length = sizeof(swar_int) / sizeof(char_type); + for (auto i = 0u; i != length; ++i) + { + std::memcpy(dst + i, ptr + length - i - 1, sizeof(char_type)); + } +#endif + return result; + } + + void bump_swar() + { + auto ptr = static_cast(*this).position(); + ptr += swar_length; + static_cast(*this).reset({ptr}); + } + void bump_swar(std::size_t char_count) + { + auto ptr = static_cast(*this).position(); + ptr += char_count; + static_cast(*this).reset({ptr}); + } +}; + +constexpr std::size_t round_size_for_swar(std::size_t size_in_bytes) +{ + // We round up to the next multiple. + if (auto remainder = size_in_bytes % sizeof(swar_int); remainder > 0) + size_in_bytes += sizeof(swar_int) - remainder; + // Then add one extra space of padding on top. + size_in_bytes += sizeof(swar_int); + return size_in_bytes; +} +} // namespace lexy::_detail + +#endif // LEXY_DETAIL_SWAR_HPP_INCLUDED + diff --git a/3rdparty/lexy/include/lexy/_detail/tuple.hpp b/3rdparty/lexy/include/lexy/_detail/tuple.hpp index 3aa681a61..b9c7ebf55 100644 --- a/3rdparty/lexy/include/lexy/_detail/tuple.hpp +++ b/3rdparty/lexy/include/lexy/_detail/tuple.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_TUPLE_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/_detail/type_name.hpp b/3rdparty/lexy/include/lexy/_detail/type_name.hpp index b257c6d57..8e7ffe250 100644 --- a/3rdparty/lexy/include/lexy/_detail/type_name.hpp +++ b/3rdparty/lexy/include/lexy/_detail/type_name.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DETAIL_TYPE_NAME_HPP_INCLUDED @@ -29,6 +29,7 @@ constexpr auto _full_type_name() auto function = string_view(__PRETTY_FUNCTION__); function.remove_prefix(prefix.length()); function.remove_suffix(suffix.length()); + function.try_remove_prefix("(anonymous namespace)::"); return function; #elif defined(__GNUC__) @@ -46,6 +47,7 @@ constexpr auto _full_type_name() auto function = string_view(__PRETTY_FUNCTION__); function.remove_prefix(prefix.length()); function.remove_suffix(suffix.length()); + function.try_remove_prefix("{anonymous}::"); return function; #elif defined(_MSC_VER) @@ -58,12 +60,8 @@ constexpr auto _full_type_name() auto function = string_view(__FUNCSIG__); function.remove_prefix(prefix.length()); function.remove_suffix(suffix.length()); - - if (auto s = string_view("struct "); function.starts_with(s)) - function.remove_prefix(s.length()); - else if (auto c = string_view("class "); function.starts_with(c)) - function.remove_prefix(c.length()); - + function.try_remove_prefix("struct ") || function.try_remove_prefix("class "); + function.try_remove_prefix("`anonymous-namespace'::"); return function; #else @@ -79,8 +77,8 @@ template constexpr string_view _type_name() { auto name = _full_type_name(); - LEXY_ASSERT(name.find('<') == string_view::npos || NsCount == 0, - "cannot strip namespaces from template instantiations"); + if (name.find('<') != string_view::npos && NsCount != 0) + return name; for (auto namespace_count = NsCount; namespace_count > 0; --namespace_count) { @@ -105,20 +103,25 @@ constexpr const char* type_name() return "unknown-type"; } -template -constexpr const void* type_id() +template +inline constexpr const char* _type_id_holder = type_name(); + +// Returns a unique address for each type. +// For implementation reasons, it also doubles as the pointer to the name. +template +constexpr const char* const* type_id() { - // As different types have different type names, the compiler can't merge them, - // and we necessarily have different addresses. - if constexpr (_detail::is_detected<_detect_name_f, T>) - return T::name(); - else if constexpr (_detail::is_detected<_detect_name_v, T>) - return T::name; + if constexpr (_detail::is_detected<_detect_name_v, T> // + && !_detail::is_detected<_detect_name_f, T>) + { + // We can use the address of the static constexpr directly. + return &T::name; + } else { - static_assert(LEXY_HAS_AUTOMATIC_TYPE_NAME, - "you need to manuall add a ::name() or ::name to your type"); - return _full_type_name().data(); + // We instantiate a variable template with a function unique by type. + // As the variable is inline, there should be a single address only. + return &_type_id_holder; } } } // namespace lexy::_detail diff --git a/3rdparty/lexy/include/lexy/_detail/unicode_database.hpp b/3rdparty/lexy/include/lexy/_detail/unicode_database.hpp index 002dce3d3..c98416f99 100644 --- a/3rdparty/lexy/include/lexy/_detail/unicode_database.hpp +++ b/3rdparty/lexy/include/lexy/_detail/unicode_database.hpp @@ -1,20 +1,20 @@ // AUTOGENERATED FILE --- DO NOT EDIT! // Generated by `support/generate-unicode-db.py`. -#define LEXY_UNICODE_DATABASE_VERSION "14.0.0" +#define LEXY_UNICODE_DATABASE_VERSION "15.0.0" namespace lexy::_unicode_db { -constexpr const char* block_starts = "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\021\025\026\027\030\031\032\033\034\035\036\037 !\"#$%&'(!)*+,-./0'\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0211\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0212\021\021\0213\021456789\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021:;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<\021=>?@ABCDEFGH\021IJKLMNOPQRSTUVWXYZ[\\]^_`a\021\021\021bcd]]]]]]]]]e\021\021\021\021f]]]]]]]]]]]]]]]\021\021g]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\021\021hi]]jk\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021l\021\021\021\021mn]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]o\021pq]]]]]]]]]r]]]]]]]]]]]]]]]]]]stuvwxyz{''|]]]]}~\177\200]]]]\201\202\203]]\204\205\206]\207\210\211\212''\213\214\215'\216\217]]]]\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\220\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\221\222\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\223\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\224]]]]]]]]]]]]\021\021\225]]]]]\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\226]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" - "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" - "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" - "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\227\230]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\231" - "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\231"; +constexpr const char* block_starts = "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\021\025\026\027\030\031\032\033\034\035\036\037 !\"#$%&'(!)*+,-./0'\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0211\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0212\021\021\0213\021456789\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021:;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<\021=>?@ABCDEFGH\021IJKLMNOPQRSTUVWXYZ[\\]^_`a\021\021\021bcdeeeeeeeeef\021\021\021\021geeeeeeeeeeeeeee\021\021heeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\021\021ijeekl\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021m\021\021\021\021noeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep\021qreeeeeeeeeseeeeeeeeeeeeeeeeeetuvwxyz{|''}eeee~\177\200\201e\202ee\203\204\205ee\206\207\210e\211\212\213\214''\215\216\217'\220\221eeee\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\222\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\223\224\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\225\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\226eeeeeeeeeeee\021\021\227eeeee\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\230\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\231eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\232\233eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\234" + "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\234"; constexpr const char* blocks = "\000\000\000\000\000\000\000\000\000\001\001\001\001\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\003\003\003\004\003\003\003\005\006\003\007\003\010\003\003\011\011\011\011\011\011\011\011\011\011\003\003\007\007\007\003\003\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\005\003\006\013\014\013\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\005\007\006\007\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\003\004\004\004\004\016\003\013\016\017\020\007\021\016\013\016\007\022\022\013\023\003\024\013\022\017\025\022\022\022\003\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\007\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\027\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\030\026\015\026\015\026\015\031\015\032\026\015\026\015\033\026\015\034\034\026\015\015\035\036\037\026\015\034 \015!\"\026\015\015\015!#\015$\026\015\026\015\026\015%\026\015%\015\015\026\015%\026\015&&\026\015\026\015'\026\015\015(\026\015\015\015(((()*\015)*\015)*\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015)*\015\026\015+,\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015-\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015\015.\026\015/0\015\015\026\015123\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015(\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015444444444555555544\013\013\013\013555555555555\013\013\013\013\013\013\013\013\013\013\013\013\013\01344444\013\013\013\013\013\013\0135\0135\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\0136666666666666666666666666666666666666666666666666666666666666666666667666666666666666666666666666666666666666666\026\015\026\0155\013\026\015889\015\015\015\003:8888\013\013;\024<<<8=8>>\015\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\0128\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015?\015\015\015\015\015\015\015\015\015\015\015\015@AB\027\027\027CD\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015EF\015\015GH\007\026\015I\026\015\015---" "JJJJJJJJJJJJJJJJ\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\01666666KK\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015L\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\0158MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM885\003\003\003\003\003\003\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\003\01088\016\016\00486666666666666666666666666666666NNNNNNNNNNNNNN\010N\003NN\003NN\003N88888888(((((((((((((((((((((((((((8888((((\003\00388888888888\021\021\021\021\021\021\007\007\007\003\003\004\003\003\016\016NNNNNNNNNNN\003\021\003\003\003((((((((((((((((((((((((((((((((5((((((((((NNNNNNNNNNNNN6NNNNNNN\011\011\011\011\011\011\011\011\011\011\003\003\003\003((N(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003(NNNNNNN\021\01666NNNN55NN\016666N((\011\011\011\011\011\011\011\011\011\011(((\016\016(\003\003\003\003\003\003\003\003\003\003\003\003\003\0038\021(N((((((((((((((((((((((((((((((NNNNNNNNNNNNNNNN6666666666688(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNN(88888888888888\011\011\011\011\011\011\011\011\011\011(((((((((((((((((((((((((((((((((66666666655\016\003\003\0035886\004\004" "((((((((((((((((((((((NN665NNNNNNNNN5NNN5NNNN688\003\003\003\003\003\003\003\003\003\003\003\003\003\003\0038(((((((((((((((((((((((((66688\0038(((((((((((88888((((((((((((((((((((((((\013((((((8\021\02188888866666666(((((((((((((((((((((((((((((((((((((((((56666666666NNNNNNNNNNNN66\021NNNNNNN666666NNNNNNNNNNNNNNNNNNNO((((((((((((((((((((((((((((((((((((((((((((((((((((((NO6(OOONNNNNNNNOOOO6OO(6666NNN((((((((((NN\003\003\011\011\011\011\011\011\011\011\011\011\0035(((((((((((((((NOO8((((((((88((88((((((((((((((((((((((8(((((((8(888((((886(OOONNNN88OO88OO6(88888888O8888((8(((NN88\011\011\011\011\011\011\011\011\011\011((\004\004\022\022\022\022\022\022\016\004(\003688NNO8((((((8888((88((((((((((((((((((((((8(((((((8((8((8((8868OOONN8888NN88NN6888N8888888((((8(8888888\011\011\011\011\011\011\011\011\011\011NN(((N\0038888888888NNO8(((((((((8(((8((((((((((((((((((((((8(((((((8((8(((((886(OOONNNNN8NNO8OO688(888888888888888((NN88\011\011\011\011\011\011\011\011\011\011\003\0048888888(NNN6668NOO8((((((((88((88((((((((((((((((((((((8(((((((8((8(((((886(ONONNNN88OO88OO688888886NO8888((8(((NN88\011\011\011\011\011\011\011\011\011\011\016(\022\022\022\022\022\0228888888888N(8((((((888(((8((((888((8(8((888((888(((888((((((((((((8888OONOO888OOO8OOO688(888888O88888888888888\011\011\011\011\011\011\011\011\011\011\022\022\022\016\016\016\016\016\016\004\01688888" - "NOOO6((((((((8(((8(((((((((((((((((((((((8((((((((((((((((886(NNNOOOO8NNN8NNN68888888NN8(((88(88((NN88\011\011\011\011\011\011\011\011\011\0118888888\003\022\022\022\022\022\022\022\016(NOO\003((((((((8(((8(((((((((((((((((((((((8((((((((((8(((((886(ONOOOOO8NOO8OON68888888OO888888((8((NN88\011\011\011\011\011\011\011\011\011\0118((8888888888888NNOO(((((((((8(((8(((((((((((((((((((((((((((((((((((((((((66(OOONNNN8OOO8OOO6(\0168888(((O\022\022\022\022\022\022\022(((NN88\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\016((((((8NOO8((((((((((((((((((888((((((((((((((((((((((((8(((((((((8(88(((((((88868888OOONNN8N8OOOOOOOO888888\011\011\011\011\011\011\011\011\011\01188OO\003888888888888((((((((((((((((((((((((((((((((((((((((((((((((N(PNNNNNNN8888\004((((((5666666N6\003\011\011\011\011\011\011\011\011\011\011\003\0038888888888888888888888888888888888888((8(8(((((8((((((((((((((((((((((((8(8((((((((((N(PNNNNNN6NN(88(((((85866666N88\011\011\011\011\011\011\011\011\011\01188((((88888888888888888888888888888888(\016\016\016\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\016\003\016\016\01666\016\016\016\016\016\016\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\022\0166\0166\0166\005\006\005\006QQ((((((((8((((((((((((((((((((((((((((((((((((8888NNNNNNNNNNNNNNONN666\00366(((((NNNNNNNNNNN8NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN8\016\016\016\016\016\016\016\0166\016\016\016\016\016\0168\016\016\003\003\003\003\003\016\016\016\016\003\0038888888888888888888888888888888888888" - "(((((((((((((((((((((((((((((((((((((((((((OONNNNONNNNN6O66OONN(\011\011\011\011\011\011\011\011\011\011\003\003\003\003\003\003((((((OONN((((NNN(OOO((OOOOOOO(((NNNN(((((((((((((NOONNOOOOOON(O\011\011\011\011\011\011\011\011\011\011OOON\016\016RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR8R88888R88\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0035\015\015\015(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8((((88(((((((8(8((((88(((((((((((((((((((((((((((((((((((((((((8((((88(((((((((((((((((((((((((((((((((8((((88(((((((8(8((((88(((((((((((((((8(((((((((((((((((((((((((((((((((((((((((((((((((((((((((8((((88(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88666\003\003\003\003\003\003\003\003\003SSSSSSSSS\022\022\022\022\022\022\022\022\022\022\022888((((((((((((((((\016\016\016\016\016\016\016\016\016\016888888\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\02788TTTTTT88" + "NOOON((((((((8(((8(((((((((((((((((((((((8((((((((((((((((886(NNNOOOO8NNN8NNN68888888NN8(((88(88((NN88\011\011\011\011\011\011\011\011\011\0118888888\003\022\022\022\022\022\022\022\016(NOO\003((((((((8(((8(((((((((((((((((((((((8((((((((((8(((((886(ONOOOOO8NOO8OON68888888OO888888((8((NN88\011\011\011\011\011\011\011\011\011\0118((O888888888888NNOO(((((((((8(((8(((((((((((((((((((((((((((((((((((((((((66(OOONNNN8OOO8OOO6(\0168888(((O\022\022\022\022\022\022\022(((NN88\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\016((((((8NOO8((((((((((((((((((888((((((((((((((((((((((((8(((((((((8(88(((((((88868888OOONNN8N8OOOOOOOO888888\011\011\011\011\011\011\011\011\011\01188OO\003888888888888((((((((((((((((((((((((((((((((((((((((((((((((N(PNNNNNNN8888\004((((((5666666N6\003\011\011\011\011\011\011\011\011\011\011\003\0038888888888888888888888888888888888888((8(8(((((8((((((((((((((((((((((((8(8((((((((((N(PNNNNNN6NN(88(((((85866666N68\011\011\011\011\011\011\011\011\011\01188((((88888888888888888888888888888888(\016\016\016\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\016\003\016\016\01666\016\016\016\016\016\016\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\022\0166\0166\0166\005\006\005\006QQ((((((((8((((((((((((((((((((((((((((((((((((8888NNNNNNNNNNNNNNONNNN6\00366(((((NNNNNNNNNNN8NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN8\016\016\016\016\016\016\016\0166\016\016\016\016\016\0168\016\016\003\003\003\003\003\016\016\016\016\003\0038888888888888888888888888888888888888" + "(((((((((((((((((((((((((((((((((((((((((((OONNNNONNNNN6O66OONN(\011\011\011\011\011\011\011\011\011\011\003\003\003\003\003\003((((((OONN((((NNN(OOO((OOOOOOO(((NNNN(((((((((((((NOONNOOOOOON(O\011\011\011\011\011\011\011\011\011\011OOON\016\016RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR8R88888R88\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0034\015\015\015(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8((((88(((((((8(8((((88(((((((((((((((((((((((((((((((((((((((((8((((88(((((((((((((((((((((((((((((((((8((((88(((((((8(8((((88(((((((((((((((8(((((((((((((((((((((((((((((((((((((((((((((((((((((((((8((((88(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88666\003\003\003\003\003\003\003\003\003SSSSSSSSS\022\022\022\022\022\022\022\022\022\022\022888((((((((((((((((\016\016\016\016\016\016\016\016\016\016888888\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\02788TTTTTT88" "\010((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\016\003(((((((((((((((((\002((((((((((((((((((((((((((\005\006888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003\003\003UUU((((((((8888888((((((((((((((((((NN6Q888888888(((((((((((((((((((NNQ\003\003888888888((((((((((((((((((NN888888888888(((((((((((((8(((8NN888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((66ONNNNNNNOOOOOOOONOO66666666666\003\003\0035\003\003\003\004(688\011\011\011\011\011\011\011\011\011\011888888\022\022\022\022\022\022\022\022\022\022888888\003\003\003\003\003\003\010\003\003\003\003666\0216\011\011\011\011\011\011\011\011\011\011888888(((((((((((((((((((((((((((((((((((5(((((((((((((((((((((((((((((((((((((((((((((((((((((8888888(((((VV((((((((((((((((((((((((((((((((((N(88888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888888" "(((((((((((((((((((((((((((((((8NNNOOOONNOOO8888OONOOOOOO6668888\016888\003\003\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((((88(((((88888888888((((((((((((((((((((((((((((((((((((((((((((8888((((((((((((((((((((((((((888888\011\011\011\011\011\011\011\011\011\011S888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016(((((((((((((((((((((((NNOON88\003\003(((((((((((((((((((((((((((((((((((((((((((((((((((((ONONNNNNNN86ONOONNNNNNNNOOOOOONN66666666886\011\011\011\011\011\011\011\011\011\011888888\011\011\011\011\011\011\011\011\011\011888888\003\003\003\003\003\003\0035\003\003\003\003\003\0038866666666666666KNN66666666666NNN8888888888888888888888888888888888888888888888888NNNNO(((((((((((((((((((((((((((((((((((((((((((((((6ONNNNNONOOOOONOQ((((((((888\011\011\011\011\011\011\011\011\011\011\003\003\003\003\003\003\003\016\016\016\016\016\016\016\016\016\016666666666\016\016\016\016\016\016\016\016\016\003\0038NNO((((((((((((((((((((((((((((((ONNNNOONNQ6NN((\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((((((((((((((((((6ONNOOONONNNQQ88888888\003\003\003\003((((((((((((((((((((((((((((((((((((OOOOOOOONNNNNNNNOON6888\003\003\003\003\003\011\011\011\011\011\011\011\011\011\011888(((\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((((555555\003\003WXYZZ[\\]^8888888___________________________________________88___\003\003\003\003\003\003\003\00388888888666\0036666666666666Q6666666((((6((((((6((Q66(88888" "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015444444444444444444444444444444444444444444444444444444444444444\015\015\015\015\015\015\015\015\015\015\015\015\0154\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0154444444444444444444444444444444444444666666666666666666666666666666666666666NNNNNNNNNNNNNN66666666666\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015`\015\015a\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\01588bbbbbb88\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\01588bbbbbb88\015\015\015\015\015\015\015\0158b8b8b8b\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\015\015\015\015\015\015\015\015\01588\015\015\015\015\015\015\015\015cccccccc\015\015\015\015\015\015\015\015cccccccc\015\015\015\015\015\015\015\015cccccccc\015\015\015\015\0158\015\015bbdde\013f\013\013\013\015\015\0158\015\015gggge\013\013\013\015\015\015\01588\015\015bbhh8\013\013\013\015\015\015\015\015\015\015\015bbiiI\013\013\01388\015\015\0158\015\015jjkke\013\0138\002\002\002\002\002\002\002\002\002\002\002\021ll\021\021\010\010\010\010\010\010\003\003\020\025\005\020\020\025\005\020\003\003\003\003\003\003\003\003mn\021\021\021\021\021\002\003\003\003\003\003\003\003\003\003\020\025\003\003\003\003\014\014\003\003\003\007\005\006\003\003\003\003\003\003\003\003\003\003\003\007\003\014\003\003\003\003\003\003\003\003\003\003\002\021\021\021\021\0218\021\021\021\021\021\021\021\021\021\021\022488\022\022\022\022\022\022\007\007\007\005\0064\022\022\022\022\022\022\022\022\022\022\007\007\007\005\00684444444444444888\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\0048888888888888886666666666666KKKK6KKK666666666666888888888888888" @@ -23,32 +23,33 @@ constexpr const char* blocks = "\000\000\000\000\000\000\000\000\000\001\001\001 "\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\005\006\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\016\016\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\026\015yz{\015\015\026\015\026\015\026\015|}~\177\015\026\015\015\026\015\015\015\015\015\01544\200\200\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\016\016\016\016\016\016\026\015\026\015666\026\01588888\003\003\003\003\022\003\003\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158\01588888\01588((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888885\003888888888888886(((((((((((((((((((((((888888888(((((((8(((((((8(((((((8(((((((8(((((((8(((((((8(((((((8(((((((8NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" "\003\003\020\025\020\025\003\003\003\020\025\003\020\025\003\003\003\003\003\003\003\003\003\010\003\003\010\003\020\025\003\003\020\025\005\006\005\006\005\006\005\006\003\003\003\003\003\201\003\003\003\003\003\003\003\003\003\003\010\010\003\003\003\003\010\003\005\003\003\003\003\003\003\003\003\003\003\003\003\003\016\016\003\003\003\005\006\005\006\005\006\005\006\0108888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\0168888\002\003\003\003\0165(U\005\006\005\006\005\006\005\006\005\006\016\016\005\006\005\006\005\006\005\006\010\005\006\006\016UUUUUUUUU6666QQ\01055555\016\016UUU5(\003\016\0168((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8866\013\01355(\010((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003555(88888(((((((((((((((((((((((((((((((((((((((((((8((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8\016\016\022\022\022\022\016\016\016\016\016\016\016\016\016\016((((((((((((((((((((((((((((((((\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888((((((((((((((((" "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\022\022\022\022\022\022\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016(((((((((((((((((((((5(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888((((((((((((((((((((((((((((((((((((((((555555\003\003" - "((((((((((((5\003\003\003((((((((((((((((\011\011\011\011\011\011\011\011\011\011((88888888888888888888\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015(6KKK\003NNNNNNNN66\0035\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\01544NN((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((UUUUUUUUUU66\003\003\003\003\003\00388888888\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013555555555\013\013\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\0154\015\015\015\015\015\015\015\015\026\015\026\015\202\026\015\026\015\026\015\026\015\026\0155\013\013\026\015\203\015(\026\015\026\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\204\205\206\207\204\015\210\211\212\213\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\214\215\216\026\015\026\01588888\026\0158\0158\015\026\015\026\015888888888888888888888888555\026\015(44\015(((((((N(((6((((N(((((((((((((((((((((((OONNO\016\016\016\0166888\022\022\022\022\022\022\016\016\004\016888888((((((((((((((((((((((((((((((((((((((((((((((((((((\003\003\003\00388888888OO((((((((((((((((((((((((((((((((((((((((((((((((((OOOOOOOOOOOOOOOO6N88888888\003\003\011\011\011\011\011\011\011\011\011\011888888666666666666666666((((((\003\003\003(\003((N\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((NNNNN666\003\003(((((((((((((((((((((((NNNNNNNNNNNOQ88888888888\003(((((((((((((((((((((((((((((888NNNO(((((((((((((((((((((((((((((((((((((((((((((((6OONNNNOONNOOQ\003\003\003\003\003\003\003\003\003\003\003\003\00385\011\011\011\011\011\011\011\011\011\0118888\003\003(((((N5(((((((((\011\011\011\011\011\011\011\011\011\011(((((8" - "(((((((((((((((((((((((((((((((((((((((((NNNNNNOONNOONN888888888(((N((((((((NO88\011\011\011\011\011\011\011\011\011\01188\003\003\003\003((((((((((((((((5((((((\016\016\016(ONO((((((((((((((((((((((((((((((((((((((((((((((((((N(NNN((NN(((((N6(6(888888888888888888888888((5\003\003(((((((((((ONNOO\003\003(55O68888888888((((((88((((((88((((((888888888(((((((8(((((((8\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0134444\015\015\015\015\015\015\015\015\0155\013\0138888\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217(((((((((((((((((((((((((((((((((((OONOONOO\003Q688\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888(((((((((((((((((((((((8888(((((((((((((((((((((((((((((((((((((((((((((((((8888\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220" + "((((((((((((5\003\003\003((((((((((((((((\011\011\011\011\011\011\011\011\011\011((88888888888888888888\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015(6KKK\003NNNNNNNN66\0035\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\01544NN((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((UUUUUUUUUU66\003\003\003\003\003\00388888888\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013555555555\013\013\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\0154\015\015\015\015\015\015\015\015\026\015\026\015\202\026\015\026\015\026\015\026\015\026\0155\013\013\026\015\203\015(\026\015\026\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\204\205\206\207\204\015\210\211\212\213\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\214\215\216\026\015\026\01588888\026\0158\0158\015\026\015\026\015888888888888888888888888444\026\015(44\015(((((((N(((6((((N(((((((((((((((((((((((OONNO\016\016\016\0166888\022\022\022\022\022\022\016\016\004\016888888((((((((((((((((((((((((((((((((((((((((((((((((((((\003\003\003\00388888888OO((((((((((((((((((((((((((((((((((((((((((((((((((OOOOOOOOOOOOOOOO6N88888888\003\003\011\011\011\011\011\011\011\011\011\011888888666666666666666666((((((\003\003\003(\003((N\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((NNNNN666\003\003(((((((((((((((((((((((NNNNNNNNNNNOQ88888888888\003(((((((((((((((((((((((((((((888NNNO(((((((((((((((((((((((((((((((((((((((((((((((6OONNNNOONNOOQ\003\003\003\003\003\003\003\003\003\003\003\003\00385\011\011\011\011\011\011\011\011\011\0118888\003\003(((((N5(((((((((\011\011\011\011\011\011\011\011\011\011(((((8" + "(((((((((((((((((((((((((((((((((((((((((NNNNNNOONNOONN888888888(((N((((((((NO88\011\011\011\011\011\011\011\011\011\01188\003\003\003\003((((((((((((((((5((((((\016\016\016(ONO((((((((((((((((((((((((((((((((((((((((((((((((((N(NNN((NN(((((N6(6(888888888888888888888888((5\003\003(((((((((((ONNOO\003\003(55O68888888888((((((88((((((88((((((888888888(((((((8(((((((8\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0134444\015\015\015\015\015\015\015\015\0154\013\0138888\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217(((((((((((((((((((((((((((((((((((OONOONOO\003Q688\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888(((((((((((((((((((((((8888(((((((((((((((((((((((((((((((((((((((((((((((((8888\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220" "\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888888888\015\015\015\015\015\015\015888888888888\015\015\015\015\01588888(N((((((((((\007(((((((((((((8(((((8(8((8((8((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\0138888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\222\222\222\222\222\222((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((" "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\006\005\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888\01688888888888888888888888888888888((((((((((\222\222\004\016\016\0166666666666666666\003\003\003\003\003\003\003\005\006\0038888886666666666666666\003\010\010\014\014\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\003\003\005\006\003\003\003\003\014\014\014\003\003\0038\003\003\003\003\010\005\006\005\006\005\006\003\003\003\007\010\007\007\0078\003\004\003\0038888\222(\222(\2228\222(\222(\222(\222(\222((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88\0218\003\003\003\004\003\003\003\005\006\003\007\003\010\003\003\011\011\011\011\011\011\011\011\011\011\003\003\007\007\007\003\003\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\005\003\006\013\014\013\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\005\007\006\007\005\006\003\005\006\003\003((((((((((5(((((((((((((((((((((((((((((((((((((((((((((\223\223(((((((((((((((((((((((((((((((888((((((88((((((88((((((88(((888\004\004\007\013\016\004\0048\016\007\007\007\007\016\0168888888888\021\021\021\016\01688((((((((((((8((((((((((((((((((((((((((8(((((((((((((((((((8((8(((((((((((((((88((((((((((((((8888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888" "\003\003\0038888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888\016\016\016\016\016\016\016\016\016UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016888\01688888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01668888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((888(((((((((((((((((((((((((((((((((((((((((((((((((8888888888888886\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\0228888((((((((((((((((((((((((((((((((\022\022\022\022888888888((((((((((((((((((((U((((((((U88888((((((((((((((((((((((((((((((((((((((NNNNN88888((((((((((((((((((((((((((((((8\003((((((((((((((((((((((((((((((((((((8888((((((((\003UUUUU888888888888888888888888888888888888888888\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88\011\011\011\011\011\011\011\011\011\011888888\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\2248888\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158888" "((((((((((((((((((((((((((((((((((((((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888\003\225\225\225\225\225\225\225\225\225\225\2258\225\225\225\225\225\225\225\225\225\225\225\225\225\225\2258\225\225\225\225\225\225\2258\225\2258\015\015\015\015\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158\015\015\015\015\015\015\0158\015\0158888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888((((((((((((((((((((((8888888888((((((((88888888888888888888888845544484444444444444444444444444444444444444444448444444444888888888888888888888888888888888888888888888888888888888888888888888((((((88(8((((((((((((((((((((((((((((((((((((((((((((8((888(88(((((((((((((((((((((((8\003\022\022\022\022\022\022\022\022(((((((((((((((((((((((\016\016\022\022\022\022\022\022\022(((((((((((((((((((((((((((((((88888888\022\022\022\022\022\022\022\022\022888888888888888888888888888888888888888888888888(((((((((((((((((((8((88888\022\022\022\022\022((((((((((((((((((((((\022\022\022\022\022\022888\003((((((((((((((((((((((((((88888\0038888888888888888888888888888888888888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888\022\022((\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022" "(NNN8NN88888NNNN((((8(((8(((((((((((((((((((((((((((((8866688886\022\022\022\022\022\022\022\022\0228888888\003\003\003\003\003\003\003\003\0038888888(((((((((((((((((((((((((((((\022\022\003(((((((((((((((((((((((((((((\022\022\02288888888888888888888888888888888((((((((\016((((((((((((((((((((((((((((668888\022\022\022\022\022\003\003\003\003\003\003\003888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((888\003\003\003\003\003\003\003((((((((((((((((((((((88\022\022\022\022\022\022\022\022(((((((((((((((((((88888\022\022\022\022\022\022\022\022((((((((((((((((((8888888\003\003\003\003888888888888\022\022\022\022\022\022\02288888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888888888888888888888888888888888888888888888888888===================================================8888888888888\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158888888\022\022\022\022\022\022((((((((((((((((((((((((((((((((((((NNNN88888888\011\011\011\011\011\011\011\011\011\011888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" - "888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\0228((((((((((((((((((((((((((((((((((((((((((8NN\01088((888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((\022\022\022\022\022\022\022\022\022\022(88888888((((((((((((((((((((((66666666666\022\022\022\022\003\003\003\003\0038888888888888888888888((((((((((((((((((6666\003\003\003\00388888888888888888888888888888888888888(((((((((((((((((((((\022\022\022\022\022\022\02288888888888888888888(((((((((((((((((((((((888888888ONO(((((((((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNNNNN6\003\003\003\003\003\003\0038888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\011\011\011\011\011\011\011\011\011\0116((NN(888888888666O(((((((((((((((((((((((((((((((((((((((((((((OOONNNNOO66\003\003\021\003\003\003\003N8888888888\02188(((((((((((((((((((((((((8888888\011\011\011\011\011\011\011\011\011\011888888NNN((((((((((((((((((((((((((((((((((((NNNNNONNNNNN668\011\011\011\011\011\011\011\011\011\011\003\003\003\003(OO(88888888(((((((((((((((((((((((((((((((((((6\003\003(888888888NNO((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNNOQ((((\003\003\003\0036666\003ON\011\011\011\011\011\011\011\011\011\011(\003(\003\003\0038\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288888888888" - "((((((((((((((((((8(((((((((((((((((((((((((OOONNNOONQ6N\003\003\003\003\003\003N88888888888888888888888888888888888888888888888888888888888888888(((((((8(8((((8(((((((((((((((8((((((((((\003888888(((((((((((((((((((((((((((((((((((((((((((((((NOOONNNNNN6688888\011\011\011\011\011\011\011\011\011\011888888NNOO8((((((((88((88((((((((((((((((((((((8(((((((8((8(((((866(OONOOOO88OO88OOQ88(888888O88888(((((OO886666666888666668888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNOO6NNO6((((\003\003\003\003\003\011\011\011\011\011\011\011\011\011\011\003\0038\0036(((888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNONOOOONNO66((\003(88888888\011\011\011\011\011\011\011\011\011\0118888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((OOONNNN88OOOONNO66\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003((((NN8888888888888888888888888888888888" + "888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\0228((((((((((((((((((((((((((((((((((((((((((8NN\01088((888888888888888888888888888888888888888888888888888888888888888888888888888666(((((((((((((((((((((((((((((\022\022\022\022\022\022\022\022\022\022(88888888((((((((((((((((((((((66666666666\022\022\022\022\003\003\003\003\0038888888888888888888888((((((((((((((((((6666\003\003\003\00388888888888888888888888888888888888888(((((((((((((((((((((\022\022\022\022\022\022\02288888888888888888888(((((((((((((((((((((((888888888ONO(((((((((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNNNNN6\003\003\003\003\003\003\0038888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\011\011\011\011\011\011\011\011\011\0116((NN(8888888886NNO(((((((((((((((((((((((((((((((((((((((((((((OOONNNNOO66\003\003\021\003\003\003\003N8888888888\02188(((((((((((((((((((((((((8888888\011\011\011\011\011\011\011\011\011\011888888NNN((((((((((((((((((((((((((((((((((((NNNNNONNNNNN668\011\011\011\011\011\011\011\011\011\011\003\003\003\003(OO(88888888(((((((((((((((((((((((((((((((((((6\003\003(888888888NNO((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNNOQ((((\003\003\003\0036666\003ON\011\011\011\011\011\011\011\011\011\011(\003(\003\003\0038\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288888888888" + "((((((((((((((((((8(((((((((((((((((((((((((OOONNNOONQ6N\003\003\003\003\003\003N((N88888888888888888888888888888888888888888888888888888888888888(((((((8(8((((8(((((((((((((((8((((((((((\003888888(((((((((((((((((((((((((((((((((((((((((((((((NOOONNNNNN6688888\011\011\011\011\011\011\011\011\011\011888888NNOO8((((((((88((88((((((((((((((((((((((8(((((((8((8(((((866(OONOOOO88OO88OOQ88(888888O88888(((((OO886666666888666668888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNOO6NNO6((((\003\003\003\003\003\011\011\011\011\011\011\011\011\011\011\003\0038\0036(((888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNONOOOONNO66((\003(88888888\011\011\011\011\011\011\011\011\011\0118888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((OOONNNN88OOOONNO66\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003((((NN8888888888888888888888888888888888" "((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNOONO6N\003\003\003(88888888888\011\011\011\011\011\011\011\011\011\011888888\003\003\003\003\003\003\003\003\003\003\003\003\0038888888888888888888(((((((((((((((((((((((((((((((((((((((((((NONOONNNNNNQ6(\003888888\011\011\011\011\011\011\011\011\011\011888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((88NNNOONNNNONNNN68888\011\011\011\011\011\011\011\011\011\011\022\022\003\003\003\016(((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNNO66\0038888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022888888888888((((((((88(88((((((((8((8((((((((((((((((((((((((OOOOOO8OO88NNQ6(O(O6\003\003\003888888888\011\011\011\011\011\011\011\011\011\0118888888888888888888888888888888888888888888888888888888888888888888888((((((((88(((((((((((((((((((((((((((((((((((((((OOONNNN88NNOOOO6(\003(O888888888888888888888888888" - "(NNNNNNNNNN((((((((((((((((((((((((((((((((((((((((66NNNNO(NNNN\003\003\003\003\003\003\003\003688888888(NNNNNNOONNN((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNNNNO66\003\003\003(\003\003\003\003\0038888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((8(((((((((((((((((((((((((((((((((((((ONNNNNNN8NNNNNNO6(\003\003\003\003\0038888888888\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888\003\003((((((((((((((((((((((((((((((88NNNNNNNNNNNNNNNNNNNNNN8ONNNNNNNONNONN8888888888888888888888888888888888888888888888888888888888888888888888888(((((((8((8((((((((((((((((((((((((((((((((((((((NNNNNN888N8NN8NNN6N66(N88888888\011\011\011\011\011\011\011\011\011\011888888((((((8((8((((((((((((((((((((((((((((((((OOOOO8NN8OONO6(8888888\011\011\011\011\011\011\011\011\011\01188888888888888888888888888888888888888888888888888888888888888888888888888888888888888" - "88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((NNOO\003\003888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\004\004\004\004\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888\003((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU8\003\003\003\003\00388888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((" - "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003\0038888888888888(((((((((((((((((((((((((((((((((((((((((((((((8\021\021\021\021\021\021\021\021\0218888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" - "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888(((((((((((((((((((((((((((((((8\011\011\011\011\011\011\011\011\011\0118888\003\003(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((8866666\0038888888888((((((((((((((((((((((((((((((((((((((((((((((((6666666\003\003\003\003\003\016\016\016\0165555\003\0168888888888\011\011\011\011\011\011\011\011\011\0118\022\022\022\022\022\022\0228(((((((((((((((((((((88888(((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\003\003\003\00388888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888N(OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO8888888NNNN5555555555555888888888888888888888888888888888888888888888888888888888888888855\0035688888888888OO88888888888888" - "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888(((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888885555855555558558" - "(((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888(((88888888888888888((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888(((((((((((((888(((((((((8888888((((((((((88\0166N\003\021\021\021\0218888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888866666666666666666666666666666666666666666666668866666666666666666666666888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888888888888888888888888888888888888888888888888888" - "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016QQ666\016\016\016QQQQQQ\021\021\021\021\021\021\021\02166666666\016\0166666666\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0166666\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016666\0168888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" - "\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0278\027\02788\02788\027\02788\027\027\027\0278\027\027\027\027\027\027\027\027\015\015\015\0158\0158\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\0278\027\027\027\02788\027\027\027\027\027\027\027\0278\027\027\027\027\027\027\0278\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\0278\027\027\027\0278\027\027\027\027\0278\027888\027\027\027\027\027\027\0278\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\01588\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\01588\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011" - "6666666666666666666666666666666666666666666666666666666\016\016\016\01666666666666666666666666666666666666666666666666666\016\016\016\016\016\016\016\0166\016\016\016\016\016\016\016\016\016\016\016\016\016\0166\016\016\003\003\003\003\00388888888888888866666866666666666666688888888888888888888888888888888888888888888888888888888888888888888888888888888\015\015\015\015\015\015\015\015\015\015(\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888NNNNNNN8NNNNNNNNNNNNNNNNN88NNNNNNN8NN8NNNNN888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((8886666666555555588\011\011\011\011\011\011\011\011\011\0118888(\01688888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" - "888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888((((((((((((((((((((((((((((((688888888888888888((((((((((((((((((((((((((((((((((((((((((((6666\011\011\011\011\011\011\011\011\011\01188888\00488888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((8((((8((8(((((((((((((((8(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88\022\022\022\022\022\022\022\022\022666666688888888888888888888888888888888888888888\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015666N66658888\011\011\011\011\011\011\011\011\011\0118888\003\0038888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" - "88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\022\022\022\004\022\022\022\0228888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888((((8(((((((((((((((((((((((((((8((8(88(8((((((((((8((((8(8(888888(8888(8(8(8(((8((8(88(8(8(8(8(8((8(88((((8(((((((8((((8((((8(8((((((((((8(((((((((((((((((88888(((8(((((8(((((((((((((((((8888888888888888888888888888888888888888888888888888\007\00788888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888" - "\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888888888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\0168888888\016\01688888888888888\016\016\016\016\016\0168888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\013\013\013\013\013\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\016\016\016\016\016888" - "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888\016\016\016\016\016\016\016\016\016\016\016\0168888\016888888888888888\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016888888888888888888888888888888888888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016888\016\016\016\016\016888\016\016\016\016\016\016\016888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\016\016\01688888\016\016\016\016\016\0168888888888\016\016\016\016\016\016\016\016\016\016888888\016\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888888888888888888888888888\011\011\011\011\011\011\011\011\011\011888888" - "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((" - "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888888888888888888888888888((((((((((((((((((((((((((((((8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\021888888888888888888888888888888\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\02188888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" - "6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666668888888888888888\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\22188"; + "(NNNNNNNNNN((((((((((((((((((((((((((((((((((((((((66NNNNO(NNNN\003\003\003\003\003\003\003\003688888888(NNNNNNOONNN((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNNNNO66\003\003\003(\003\003\003\003\0038888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888\003\003\003\003\003\003\003\003\003\003888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((8(((((((((((((((((((((((((((((((((((((ONNNNNNN8NNNNNNO6(\003\003\003\003\0038888888888\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888\003\003((((((((((((((((((((((((((((((88NNNNNNNNNNNNNNNNNNNNNN8ONNNNNNNONNONN8888888888888888888888888888888888888888888888888888888888888888888888888(((((((8((8((((((((((((((((((((((((((((((((((((((NNNNNN888N8NN8NNN6N66(N88888888\011\011\011\011\011\011\011\011\011\011888888((((((8((8((((((((((((((((((((((((((((((((OOOOO8NN8OONO6(8888888\011\011\011\011\011\011\011\011\011\01188888888888888888888888888888888888888888888888888888888888888888888888888888888888888" + "88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((NNOO\003\0038888888NN(O(((((((((((((8((((((((((((((((((((((((((((((((((OONNNNN888OONQ6\003\003\003\003\003\003\003\003\003\003\003\003\003\011\011\011\011\011\011\011\011\011\01188888888888888888888888888888888888888888888888888888888888888888888888888888888888888(888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\004\004\004\004\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888\003((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU8\003\003\003\003\00388888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((" + "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003\0038888888888888((((((((((((((((((((((((((((((((((((((((((((((((\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0216((((((66666666666666688888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" + "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888(((((((((((((((((((((((((((((((8\011\011\011\011\011\011\011\011\011\0118888\003\003(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((8866666\0038888888888((((((((((((((((((((((((((((((((((((((((((((((((6666666\003\003\003\003\003\016\016\016\0165555\003\0168888888888\011\011\011\011\011\011\011\011\011\0118\022\022\022\022\022\022\0228(((((((((((((((((((((88888(((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\003\003\003\00388888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" + "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888N(OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO8888888NNNN5555555555555888888888888888888888888888888888888888888888888888888888888888855\0035688888888888OO88888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888(((((((((8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" + "8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888885555855555558558(((((((((((((((((((((((((((((((((((888888888888888(88888888888888888888888888888(((88(88888888888888((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888(((((((((((((888(((((((((8888888((((((((((88\0166N\003\021\021\021\02188888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" + "66666666666666666666666666666666666666666666668866666666666666666666666888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888888888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016QQ666\016\016\016QQQQQQ\021\021\021\021\021\021\021\02166666666\016\0166666666\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0166666\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016666\01688888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888" + "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0278\027\02788\02788\027\02788\027\027\027\0278\027\027\027\027\027\027\027\027\015\015\015\0158\0158\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\0278\027\027\027\02788\027\027\027\027\027\027\027\0278\027\027\027\027\027\027\0278\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\0278\027\027\027\0278\027\027\027\027\0278\027888\027\027\027\027\027\027\0278\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\01588\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015" + "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\01588\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\0116666666666666666666666666666666666666666666666666666666\016\016\016\01666666666666666666666666666666666666666666666666666\016\016\016\016\016\016\016\0166\016\016\016\016\016\016\016\016\016\016\016\016\016\0166\016\016\003\003\003\003\00388888888888888866666866666666666666688888888888888888888888888888888888888888888888888888888888888888888888888888888\015\015\015\015\015\015\015\015\015\015(\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015888888\015\015\015\015\015\015888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888NNNNNNN8NNNNNNNNNNNNNNNNN88NNNNNNN8NN8NNNNN8888844444444444444444444444444444444444444444444444444444444444444888888888888888888888888888888888N8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" + "(((((((((((((((((((((((((((((((((((((((((((((8886666666555555588\011\011\011\011\011\011\011\011\011\0118888(\01688888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888((((((((((((((((((((((((((((((688888888888888888((((((((((((((((((((((((((((((((((((((((((((6666\011\011\011\011\011\011\011\011\011\01188888\0048888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((56666\011\011\011\011\011\011\011\011\011\01188888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((8((((8((8(((((((((((((((8" + "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88\022\022\022\022\022\022\022\022\022666666688888888888888888888888888888888888888888\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015666N66658888\011\011\011\011\011\011\011\011\011\0118888\003\003888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\022\022\022\004\022\022\022\0228888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" + "((((8(((((((((((((((((((((((((((8((8(88(8((((((((((8((((8(8(888888(8888(8(8(8(((8((8(88(8(8(8(8(8((8(88((((8(((((((8((((8((((8(8((((((((((8(((((((((((((((((88888(((8(((((8(((((((((((((((((8888888888888888888888888888888888888888888888888888\007\00788888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888888888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\0168888888\016\01688888888888888\016\016\016\016\016\0168888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" + "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\013\013\013\013\013\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888\016\016\016\016\016\016\016\016\016\016\016\0168888\016888888888888888\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016888888888888888888888888888888888888888888888888888888888888888888888888888888" + "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\0168888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\0168888888\016\016\016\016\016\016\016\016\0168888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888888888888888888888888888\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((" + "((((((((((((((((((((((((((((((88((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888888888888888888888888888((((((((((((((((((((((((((((((8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" + "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888888888888888888888888888888888888888\021888888888888888888888888888888\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888886666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666668888888888888888" + "\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\22188"; constexpr std::size_t property_index(char32_t code_point) { diff --git a/3rdparty/lexy/include/lexy/action/base.hpp b/3rdparty/lexy/include/lexy/action/base.hpp index 528213bc8..84cc15f2d 100644 --- a/3rdparty/lexy/include/lexy/action/base.hpp +++ b/3rdparty/lexy/include/lexy/action/base.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_ACTION_BASE_HPP_INCLUDED @@ -82,6 +82,23 @@ namespace _detail vars(nullptr), // cur_depth(0), max_depth(static_cast(max_depth)), enable_whitespace_skipping(true) {} + + template + constexpr parse_context_control_block(Handler&& handler, + parse_context_control_block* cb) + : parse_handler(LEXY_MOV(handler)), parse_state(cb->parse_state), // + vars(cb->vars), cur_depth(cb->cur_depth), max_depth(cb->max_depth), + enable_whitespace_skipping(cb->enable_whitespace_skipping) + {} + + template + constexpr void copy_vars_from(parse_context_control_block* cb) + { + vars = cb->vars; + cur_depth = cb->cur_depth; + max_depth = cb->max_depth; + enable_whitespace_skipping = cb->enable_whitespace_skipping; + } }; } // namespace _detail @@ -99,16 +116,19 @@ template > struct _pc { + using handler_type = Handler; + using state_type = State; + using production = Production; using whitespace_production = WhitespaceProduction; using value_type = _production_value_type; - typename Handler::template event_handler handler; + typename Handler::event_handler handler; _detail::parse_context_control_block* control_block; _detail::lazy_init value; constexpr explicit _pc(_detail::parse_context_control_block* cb) - : control_block(cb) + : handler(Production{}), control_block(cb) {} template @@ -180,6 +200,7 @@ constexpr void* no_parse_state = nullptr; template constexpr auto _do_action(_pc& context, Reader& reader) { + context.on(parse_events::grammar_start{}, reader.position()); context.on(parse_events::production_start{}, reader.position()); // We parse whitespace, theen the rule, then finish. @@ -189,14 +210,21 @@ constexpr auto _do_action(_pc& context, Reader& read auto rule_result = parser::parse(context, reader); if (rule_result) + { context.on(parse_events::production_finish{}, reader.position()); + context.on(parse_events::grammar_finish{}, reader); + } else + { context.on(parse_events::production_cancel{}, reader.position()); + context.on(parse_events::grammar_cancel{}, reader); + } return rule_result; } -template +template typename Result, typename Handler, + typename State, typename Reader> constexpr auto do_action(Handler&& handler, State* state, Reader& reader) { static_assert(!std::is_reference_v, "need to move handler in"); @@ -209,12 +237,13 @@ constexpr auto do_action(Handler&& handler, State* state, Reader& reader) using value_type = typename decltype(context)::value_type; if constexpr (std::is_void_v) - return LEXY_MOV(control_block.parse_handler).get_result_void(rule_result); + return LEXY_MOV(control_block.parse_handler).template get_result>(rule_result); else if (context.value) return LEXY_MOV(control_block.parse_handler) - .template get_result(rule_result, LEXY_MOV(*context.value)); + .template get_result>(rule_result, LEXY_MOV(*context.value)); else - return LEXY_MOV(control_block.parse_handler).template get_result(rule_result); + return LEXY_MOV(control_block.parse_handler) + .template get_result>(rule_result); } } // namespace lexy diff --git a/3rdparty/lexy/include/lexy/action/match.hpp b/3rdparty/lexy/include/lexy/action/match.hpp index 402a417f7..dade7042c 100644 --- a/3rdparty/lexy/include/lexy/action/match.hpp +++ b/3rdparty/lexy/include/lexy/action/match.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_ACTION_MATCH_HPP_INCLUDED @@ -8,23 +8,24 @@ namespace lexy { -class match_handler +class _mh { public: - constexpr match_handler() : _failed(false) {} + constexpr _mh() : _failed(false) {} - template class event_handler { public: + constexpr event_handler(production_info) {} + template - constexpr void on(match_handler& handler, parse_events::error, Error&&) + constexpr void on(_mh& handler, parse_events::error, Error&&) { handler._failed = true; } template - constexpr int on(match_handler&, Event, const Args&...) + constexpr int on(_mh&, Event, const Args&...) { return 0; // operation_chain_start needs to return something } @@ -33,7 +34,8 @@ class match_handler template using value_callback = _detail::void_value_callback; - constexpr bool get_result_void(bool rule_parse_result) && + template + constexpr bool get_result(bool rule_parse_result) && { return rule_parse_result && !_failed; } @@ -47,10 +49,13 @@ struct match_action { State* _state = nullptr; - using handler = match_handler; + using handler = _mh; using state = State; using input = Input; + template + using result_type = bool; + constexpr match_action() = default; template constexpr explicit match_action(U& state) : _state(&state) @@ -60,7 +65,7 @@ struct match_action constexpr auto operator()(Production, const Input& input) const { auto reader = input.reader(); - return lexy::do_action(handler(), _state, reader); + return lexy::do_action(handler(), _state, reader); } }; diff --git a/3rdparty/lexy/include/lexy/action/parse.hpp b/3rdparty/lexy/include/lexy/action/parse.hpp index c7b6c1f10..ec4241590 100644 --- a/3rdparty/lexy/include/lexy/action/parse.hpp +++ b/3rdparty/lexy/include/lexy/action/parse.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_ACTION_PARSE_HPP_INCLUDED @@ -89,28 +89,28 @@ class parse_result _impl_t _impl; lexy::_detail::lazy_init _value; - template - friend class parse_handler; + template + friend class _ph; }; } // namespace lexy namespace lexy { -template -class parse_handler +template +class _ph { - using iterator = typename lexy::input_reader::iterator; + using iterator = typename Reader::iterator; public: - constexpr explicit parse_handler(const Input& input, const ErrorCallback& callback) - : _validate(input, callback) + template + constexpr explicit _ph(const _detail::any_holder& input, + _detail::any_holder& sink) + : _validate(input, sink) {} - template - using event_handler = - typename validate_handler::template event_handler; + using event_handler = typename _vh::event_handler; - constexpr operator validate_handler&() + constexpr operator _vh&() { return _validate; } @@ -118,28 +118,22 @@ class parse_handler template using value_callback = production_value_callback; - constexpr auto get_result_void(bool rule_parse_result) && - { - return parse_result( - LEXY_MOV(_validate).get_result_void(rule_parse_result)); - } - - template + template constexpr auto get_result(bool rule_parse_result, T&& result) && { - return parse_result(LEXY_MOV(_validate).get_result_void( - rule_parse_result), - LEXY_MOV(result)); + using validate_result = lexy::validate_result; + return Result(LEXY_MOV(_validate).template get_result(rule_parse_result), + LEXY_MOV(result)); } - template + template constexpr auto get_result(bool rule_parse_result) && { - return parse_result( - LEXY_MOV(_validate).get_result_void(rule_parse_result)); + using validate_result = lexy::validate_result; + return Result(LEXY_MOV(_validate).template get_result(rule_parse_result)); } private: - validate_handler _validate; + _vh _validate; }; template @@ -148,10 +142,13 @@ struct parse_action const ErrorCallback* _callback; State* _state = nullptr; - using handler = parse_handler; + using handler = _ph>; using state = State; using input = Input; + template + using result_type = parse_result; + constexpr explicit parse_action(const ErrorCallback& callback) : _callback(&callback) {} template constexpr explicit parse_action(U& state, const ErrorCallback& callback) @@ -161,8 +158,11 @@ struct parse_action template constexpr auto operator()(Production, const Input& input) const { - auto reader = input.reader(); - return lexy::do_action(handler(input, *_callback), _state, reader); + _detail::any_holder input_holder(&input); + _detail::any_holder sink(_get_error_sink(*_callback)); + auto reader = input.reader(); + return lexy::do_action(handler(input_holder, sink), _state, + reader); } }; diff --git a/3rdparty/lexy/include/lexy/action/parse_as_tree.hpp b/3rdparty/lexy/include/lexy/action/parse_as_tree.hpp index e3295f330..b2f54d2c0 100644 --- a/3rdparty/lexy/include/lexy/action/parse_as_tree.hpp +++ b/3rdparty/lexy/include/lexy/action/parse_as_tree.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_ACTION_PARSE_AS_TREE_HPP_INCLUDED @@ -11,54 +11,70 @@ namespace lexy { -template -class parse_tree_handler +template +class _pth { public: - explicit parse_tree_handler(Tree& tree, const Input& input, const ErrorCallback& cb) - : _tree(&tree), _depth(0), _validate(input, cb) + template + explicit _pth(Tree& tree, const _detail::any_holder& input, + _detail::any_holder& sink) + : _tree(&tree), _depth(0), _validate(input, sink) {} - template class event_handler { - using iterator = typename lexy::input_reader::iterator; + using iterator = typename Reader::iterator; public: - void on(parse_tree_handler& handler, parse_events::production_start ev, iterator pos) + event_handler(production_info info) : _validate(info) {} + + void on(_pth& handler, parse_events::grammar_start, iterator) { - if (handler._depth++ == 0) - handler._builder.emplace(LEXY_MOV(*handler._tree), Production{}); - else - _marker = handler._builder->start_production(Production{}); + LEXY_PRECONDITION(handler._depth == 0); - _validate.on(handler._validate, ev, pos); + handler._builder.emplace(LEXY_MOV(*handler._tree), _validate.get_info()); } + void on(_pth& handler, parse_events::grammar_finish, Reader& reader) + { + LEXY_PRECONDITION(handler._depth == 0); - void on(parse_tree_handler& handler, parse_events::production_finish, iterator pos) + auto begin = reader.position(); + lexy::try_match_token(dsl::any, reader); + auto end = reader.position(); + + *handler._tree = LEXY_MOV(*handler._builder).finish({begin, end}); + } + void on(_pth& handler, parse_events::grammar_cancel, Reader&) { - if (--handler._depth == 0) - { - auto reader = handler._validate.input().reader(); - reader.set_position(pos); - lexy::try_match_token(dsl::any, reader); - auto end = reader.position(); + LEXY_PRECONDITION(handler._depth == 0); - *handler._tree = LEXY_MOV(*handler._builder).finish({pos, end}); - } - else + handler._tree->clear(); + } + + void on(_pth& handler, parse_events::production_start ev, iterator pos) + { + if (handler._depth++ > 0) + _marker = handler._builder->start_production(_validate.get_info()); + + _validate.on(handler._validate, ev, pos); + } + + void on(_pth& handler, parse_events::production_finish ev, iterator pos) + { + if (--handler._depth > 0) { + if (handler._builder->current_child_count() == 0) + handler._builder->token(lexy::position_token_kind, _validate.production_begin(), + _validate.production_begin()); handler._builder->finish_production(LEXY_MOV(_marker)); } + + _validate.on(handler._validate, ev, pos); } - void on(parse_tree_handler& handler, parse_events::production_cancel, iterator pos) + void on(_pth& handler, parse_events::production_cancel ev, iterator pos) { - if (--handler._depth == 0) - { - handler._tree->clear(); - } - else + if (--handler._depth > 0) { // Cancelling the production removes all nodes from the tree. // To ensure that the parse tree remains lossless, we add everything consumed by it @@ -66,61 +82,61 @@ class parse_tree_handler handler._builder->cancel_production(LEXY_MOV(_marker)); handler._builder->token(lexy::error_token_kind, _validate.production_begin(), pos); } + + _validate.on(handler._validate, ev, pos); } - auto on(parse_tree_handler& handler, lexy::parse_events::operation_chain_start, iterator) + auto on(_pth& handler, lexy::parse_events::operation_chain_start, iterator) { // As we don't know the production yet (or whether it is actually an operation), // we create a container node to decide later. return handler._builder->start_container(); } template - void on(parse_tree_handler& handler, lexy::parse_events::operation_chain_op, Operation op, - iterator) + void on(_pth& handler, lexy::parse_events::operation_chain_op, Operation op, iterator) { // We set the production of the current container. // This will do a "left rotation" on the parse tree, making a new container the parent. handler._builder->set_container_production(op); } template - void on(parse_tree_handler& handler, lexy::parse_events::operation_chain_finish, - Marker&& marker, iterator) + void on(_pth& handler, lexy::parse_events::operation_chain_finish, Marker&& marker, + iterator) { handler._builder->finish_container(LEXY_MOV(marker)); } template - void on(parse_tree_handler& handler, parse_events::token, TokenKind kind, iterator begin, - iterator end) + void on(_pth& handler, parse_events::token, TokenKind kind, iterator begin, iterator end) { handler._builder->token(kind, begin, end); } template - void on(parse_tree_handler& handler, parse_events::error ev, Error&& error) + void on(_pth& handler, parse_events::error ev, Error&& error) { _validate.on(handler._validate, ev, LEXY_FWD(error)); } template - auto on(parse_tree_handler& handler, Event ev, Args&&... args) + auto on(_pth& handler, Event ev, Args&&... args) { return _validate.on(handler._validate, ev, LEXY_FWD(args)...); } private: - typename Tree::builder::marker _marker; - typename validate_handler::template event_handler - _validate; + typename Tree::builder::marker _marker; + typename _vh::event_handler _validate; }; template using value_callback = _detail::void_value_callback; - constexpr auto get_result_void(bool rule_parse_result) && + template + constexpr auto get_result(bool rule_parse_result) && { LEXY_PRECONDITION(_depth == 0); - return LEXY_MOV(_validate).get_result_void(rule_parse_result); + return LEXY_MOV(_validate).template get_result(rule_parse_result); } private: @@ -128,7 +144,7 @@ class parse_tree_handler Tree* _tree; int _depth; - validate_handler _validate; + _vh _validate; }; template ; + using handler = _pth>; using state = State; using input = Input; + template + using result_type = validate_result; + constexpr explicit parse_as_tree_action(tree_type& tree, const ErrorCallback& callback) : _tree(&tree), _callback(&callback) {} @@ -157,8 +176,11 @@ struct parse_as_tree_action template constexpr auto operator()(Production, const Input& input) const { - auto reader = input.reader(); - return lexy::do_action(handler(*_tree, input, *_callback), _state, reader); + _detail::any_holder input_holder(&input); + _detail::any_holder sink(_get_error_sink(*_callback)); + auto reader = input.reader(); + return lexy::do_action(handler(*_tree, input_holder, sink), _state, + reader); } }; diff --git a/3rdparty/lexy/include/lexy/action/scan.hpp b/3rdparty/lexy/include/lexy/action/scan.hpp index 2c246535f..09dc65f4d 100644 --- a/3rdparty/lexy/include/lexy/action/scan.hpp +++ b/3rdparty/lexy/include/lexy/action/scan.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_ACTION_SCAN_HPP_INCLUDED @@ -33,13 +33,13 @@ class scanner : public _detail::scanner, lexy::input_reader>; - using _handler = lexy::validate_handler; + using _handler = lexy::_vh>; using _production = _scp; public: constexpr explicit scanner(const Input& input, State* state, const ErrorCallback& callback) - : _impl(input.reader()), - _cb(_handler(input, callback), state, max_recursion_depth<_production>()), _context(&_cb) + : _impl(input.reader()), _input(&input), _sink(_get_error_sink(callback)), + _cb(_handler(_input, _sink), state, max_recursion_depth<_production>()), _context(&_cb) { _context.on(parse_events::production_start{}, this->position()); } @@ -58,7 +58,8 @@ class scanner : public _detail::scannerposition()); - return LEXY_MOV(_cb.parse_handler).get_result_void(parse_result); + return LEXY_MOV(_cb.parse_handler) + .template get_result>(parse_result); } private: @@ -67,8 +68,10 @@ class scanner : public _detail::scanner _cb; - _pc<_handler, State, _production> _context; + _detail::any_holder _input; + _detail::any_holder<_error_sink_t> _sink; + _detail::parse_context_control_block<_handler, State> _cb; + _pc<_handler, State, _production> _context; friend _impl; }; diff --git a/3rdparty/lexy/include/lexy/action/trace.hpp b/3rdparty/lexy/include/lexy/action/trace.hpp index 1b968e30d..1a38fac64 100644 --- a/3rdparty/lexy/include/lexy/action/trace.hpp +++ b/3rdparty/lexy/include/lexy/action/trace.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_ACTION_TRACE_HPP_INCLUDED @@ -303,37 +303,41 @@ class trace_writer namespace lexy { template -class trace_handler +class _th { public: - explicit trace_handler(OutputIt out, const Input& input, - visualization_options opts = {}) noexcept + explicit _th(OutputIt out, const Input& input, visualization_options opts = {}) noexcept : _writer(out, opts), _input(&input), _anchor(input) { LEXY_PRECONDITION(opts.max_tree_depth <= visualization_options::max_tree_depth_limit); } - template class event_handler { using iterator = typename lexy::input_reader::iterator; public: - void on(trace_handler& handler, parse_events::production_start, iterator pos) + constexpr event_handler(production_info info) : _info(info) {} + + void on(_th&, parse_events::grammar_start, iterator) {} + void on(_th&, parse_events::grammar_finish, lexy::input_reader&) {} + void on(_th&, parse_events::grammar_cancel, lexy::input_reader&) {} + + void on(_th& handler, parse_events::production_start, iterator pos) { auto loc = handler.get_location(pos); - handler._writer.write_production_start(loc, lexy::production_name()); + handler._writer.write_production_start(loc, _info.name); // All events for the production are after the initial event. _previous_anchor.emplace(handler._anchor); handler._anchor = loc.anchor(); } - void on(trace_handler& handler, parse_events::production_finish, iterator pos) + void on(_th& handler, parse_events::production_finish, iterator pos) { auto loc = handler.get_location(pos); handler._writer.write_finish(loc); } - void on(trace_handler& handler, parse_events::production_cancel, iterator pos) + void on(_th& handler, parse_events::production_cancel, iterator pos) { auto loc = handler.get_location(pos); handler._writer.write_cancel(loc); @@ -342,67 +346,68 @@ class trace_handler handler._anchor = *_previous_anchor; } - int on(trace_handler& handler, parse_events::operation_chain_start, iterator pos) + int on(_th& handler, parse_events::operation_chain_start, iterator pos) { auto loc = handler.get_location(pos); handler._writer.write_production_start(loc, "operation chain"); return 0; // need to return something } template - void on(trace_handler& handler, parse_events::operation_chain_op, Operation, iterator pos) + void on(_th& handler, parse_events::operation_chain_op, Operation, iterator pos) { auto loc = handler.get_location(pos); handler._writer.write_operation(loc, lexy::production_name()); } - void on(trace_handler& handler, parse_events::operation_chain_finish, int, iterator pos) + void on(_th& handler, parse_events::operation_chain_finish, int, iterator pos) { auto loc = handler.get_location(pos); handler._writer.write_finish(loc); } template - void on(trace_handler& handler, parse_events::token, TK kind, iterator begin, iterator end) + void on(_th& handler, parse_events::token, TK kind, iterator begin, iterator end) { auto loc = handler.get_location(begin); handler._writer.write_token(loc, token_kind(kind), lexeme_for(begin, end)); } - void on(trace_handler& handler, parse_events::backtracked, iterator begin, iterator end) + void on(_th& handler, parse_events::backtracked, iterator begin, iterator end) { auto loc = handler.get_location(begin); handler._writer.write_backtrack(loc, lexeme_for(begin, end)); } template - void on(trace_handler& handler, parse_events::error, const Error& error) + void on(_th& handler, parse_events::error, const Error& error) { auto loc = handler.get_location(error.position()); handler._writer.write_error(loc, error); } - void on(trace_handler& handler, parse_events::recovery_start, iterator pos) + void on(_th& handler, parse_events::recovery_start, iterator pos) { auto loc = handler.get_location(pos); handler._writer.write_recovery_start(loc); } - void on(trace_handler& handler, parse_events::recovery_finish, iterator pos) + void on(_th& handler, parse_events::recovery_finish, iterator pos) { auto loc = handler.get_location(pos); handler._writer.write_finish(loc); } - void on(trace_handler& handler, parse_events::recovery_cancel, iterator pos) + void on(_th& handler, parse_events::recovery_cancel, iterator pos) { auto loc = handler.get_location(pos); handler._writer.write_cancel(loc); } - void on(trace_handler& handler, parse_events::debug, iterator pos, const char* str) + void on(_th& handler, parse_events::debug, iterator pos, const char* str) { auto loc = handler.get_location(pos); handler._writer.write_debug(loc, str); } private: + production_info _info; // The beginning of the previous production. // If the current production gets canceled, it needs to be restored. _detail::lazy_init> _previous_anchor; @@ -411,7 +416,8 @@ class trace_handler template using value_callback = _detail::void_value_callback; - constexpr OutputIt get_result_void(bool) && + template + constexpr OutputIt get_result(bool) && { return LEXY_MOV(_writer).finish(); } @@ -435,10 +441,13 @@ struct trace_action visualization_options _opts; State* _state = nullptr; - using handler = trace_handler; + using handler = _th; using state = State; using input = Input; + template + using result_type = OutputIt; + constexpr explicit trace_action(OutputIt out, visualization_options opts = {}) : _out(out), _opts(opts) {} @@ -451,7 +460,8 @@ struct trace_action constexpr auto operator()(Production, const Input& input) const { auto reader = input.reader(); - return lexy::do_action(handler(_out, input, _opts), _state, reader); + return lexy::do_action(handler(_out, input, _opts), _state, + reader); } }; diff --git a/3rdparty/lexy/include/lexy/action/validate.hpp b/3rdparty/lexy/include/lexy/action/validate.hpp index 47decbde7..ac6b48f0e 100644 --- a/3rdparty/lexy/include/lexy/action/validate.hpp +++ b/3rdparty/lexy/include/lexy/action/validate.hpp @@ -1,9 +1,10 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_ACTION_VALIDATE_HPP_INCLUDED #define LEXY_ACTION_VALIDATE_HPP_INCLUDED +#include #include #include #include @@ -111,43 +112,121 @@ class validate_result _status_fatal, } _status; - template - friend class validate_handler; + template + friend class _vh; }; } // namespace lexy namespace lexy { -template -class validate_handler +template +struct _validate_callbacks +{ + _detail::any_ref sink; + _detail::any_cref input; + + void (*generic)(_detail::any_ref sink, production_info info, _detail::any_cref input, + typename Reader::iterator begin, const error& error); + void (*literal)(_detail::any_ref sink, production_info info, _detail::any_cref input, + typename Reader::iterator begin, const error& error); + void (*keyword)(_detail::any_ref sink, production_info info, _detail::any_cref input, + typename Reader::iterator begin, const error& error); + void (*char_class)(_detail::any_ref sink, production_info info, _detail::any_cref input, + typename Reader::iterator begin, + const error& error); + + template + constexpr _validate_callbacks(const _detail::any_holder& input, + _detail::any_holder& sink) + : sink(&sink), input(&input), + generic([](_detail::any_ref sink, production_info info, _detail::any_cref input, + typename Reader::iterator begin, const error& error) { + lexy::error_context err_ctx(info, *input->template get(), begin); + sink->template get()(err_ctx, LEXY_FWD(error)); + }), + literal([](_detail::any_ref sink, production_info info, _detail::any_cref input, + typename Reader::iterator begin, const error& error) { + lexy::error_context err_ctx(info, *input->template get(), begin); + sink->template get()(err_ctx, LEXY_FWD(error)); + }), + keyword([](_detail::any_ref sink, production_info info, _detail::any_cref input, + typename Reader::iterator begin, const error& error) { + lexy::error_context err_ctx(info, *input->template get(), begin); + sink->template get()(err_ctx, LEXY_FWD(error)); + }), + char_class([](_detail::any_ref sink, production_info info, _detail::any_cref input, + typename Reader::iterator begin, + const error& error) { + lexy::error_context err_ctx(info, *input->template get(), begin); + sink->template get()(err_ctx, LEXY_FWD(error)); + }) + {} +}; + +template +class _vh { public: - constexpr explicit validate_handler(const Input& input, const ErrorCallback& callback) - : _sink(_get_error_sink(callback)), _input(&input) + template + constexpr explicit _vh(const _detail::any_holder& input, + _detail::any_holder& sink) + : _cb(input, sink) {} - template class event_handler { - using iterator = typename lexy::input_reader::iterator; + using iterator = typename Reader::iterator; public: - constexpr event_handler() = default; + constexpr event_handler(production_info info) : _begin(), _info(info) {} - constexpr void on(validate_handler&, parse_events::production_start, iterator pos) + constexpr void on(_vh& handler, parse_events::production_start, iterator pos) { _begin = pos; + + _prev = handler._top; + handler._top = this; + } + constexpr void on(_vh& handler, parse_events::production_finish, iterator) + { + handler._top = _prev; + } + constexpr void on(_vh& handler, parse_events::production_cancel, iterator) + { + handler._top = _prev; } - template - constexpr void on(validate_handler& handler, parse_events::error, Error&& error) + template + constexpr void on(_vh& handler, parse_events::error, const error& error) { - lexy::error_context err_ctx(Production{}, *handler._input, _begin); - handler._sink(err_ctx, LEXY_FWD(error)); + handler._cb.generic(handler._cb.sink, get_info(), handler._cb.input, _begin, error); + } + template + constexpr void on(_vh& handler, parse_events::error, const error& error) + { + handler._cb.generic(handler._cb.sink, get_info(), handler._cb.input, _begin, error); + } + template + constexpr void on(_vh& handler, parse_events::error, + const error& error) + { + handler._cb.literal(handler._cb.sink, get_info(), handler._cb.input, _begin, error); + } + template + constexpr void on(_vh& handler, parse_events::error, + const error& error) + { + handler._cb.keyword(handler._cb.sink, get_info(), handler._cb.input, _begin, error); + } + template + constexpr void on(_vh& handler, parse_events::error, + const error& error) + { + handler._cb.char_class(handler._cb.sink, get_info(), handler._cb.input, _begin, error); } template - constexpr auto on(validate_handler&, Event, const Args&...) + constexpr auto on(_vh&, Event, const Args&...) { return 0; // operation_chain_start must return something } @@ -157,26 +236,33 @@ class validate_handler return _begin; } + constexpr production_info get_info() const + { + auto cur = this; + while (cur->_info.is_transparent && cur->_prev != nullptr) + cur = cur->_prev; + return cur->_info; + } + private: - iterator _begin = {}; + iterator _begin; + production_info _info; + event_handler* _prev = nullptr; }; template using value_callback = _detail::void_value_callback; - constexpr auto get_result_void(bool rule_parse_result) && - { - return validate_result(rule_parse_result, LEXY_MOV(_sink).finish()); - } - - const Input& input() const + template + constexpr auto get_result(bool rule_parse_result) && { - return *_input; + using sink_t = _error_sink_t; + return Result(rule_parse_result, LEXY_MOV(_cb.sink->template get()).finish()); } private: - _error_sink_t _sink; - const Input* _input; + _validate_callbacks _cb; + event_handler* _top = nullptr; }; template @@ -185,10 +271,13 @@ struct validate_action const ErrorCallback* _callback; State* _state = nullptr; - using handler = validate_handler; + using handler = _vh>; using state = State; using input = Input; + template + using result_type = validate_result; + constexpr explicit validate_action(const ErrorCallback& callback) : _callback(&callback) {} template constexpr explicit validate_action(U& state, const ErrorCallback& callback) @@ -198,8 +287,11 @@ struct validate_action template constexpr auto operator()(Production, const Input& input) const { - auto reader = input.reader(); - return lexy::do_action(handler(input, *_callback), _state, reader); + _detail::any_holder input_holder(&input); + _detail::any_holder sink(_get_error_sink(*_callback)); + auto reader = input.reader(); + return lexy::do_action(handler(input_holder, sink), _state, + reader); } }; diff --git a/3rdparty/lexy/include/lexy/callback.hpp b/3rdparty/lexy/include/lexy/callback.hpp index 2c6d4f78a..fa65f3ebb 100644 --- a/3rdparty/lexy/include/lexy/callback.hpp +++ b/3rdparty/lexy/include/lexy/callback.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/callback/adapter.hpp b/3rdparty/lexy/include/lexy/callback/adapter.hpp index fa1f28b1b..b212fd2ab 100644 --- a/3rdparty/lexy/include/lexy/callback/adapter.hpp +++ b/3rdparty/lexy/include/lexy/callback/adapter.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_ADAPTER_HPP_INCLUDED @@ -16,6 +16,33 @@ struct _callback : _overloaded constexpr explicit _callback(Fns... fns) : _overloaded(LEXY_MOV(fns)...) {} }; +template +struct _callback_with_state : _overloaded +{ + using return_type = ReturnType; + + template + struct _with_state + { + const _callback_with_state& _cb; + State& _state; + + template + constexpr return_type operator()(Args&&... args) const&& + { + return _cb(_state, LEXY_FWD(args)...); + } + }; + + constexpr explicit _callback_with_state(Fns... fns) : _overloaded(LEXY_MOV(fns)...) {} + + template + constexpr auto operator[](State& state) const + { + return _with_state{*this, state}; + } +}; + /// Creates a callback. template constexpr auto callback(Fns&&... fns) @@ -26,14 +53,28 @@ constexpr auto callback(Fns&&... fns) else return _callback...>(LEXY_FWD(fns)...); } - -/// Creates a callback. template constexpr auto callback(Fns&&... fns) { return _callback...>(LEXY_FWD(fns)...); } +/// Creates a callback that also receives the parse state. +template +constexpr auto callback_with_state(Fns&&... fns) +{ + if constexpr ((lexy::is_callback> && ...)) + return _callback_with_state::return_type...>, + std::decay_t...>(LEXY_FWD(fns)...); + else + return _callback_with_state...>(LEXY_FWD(fns)...); +} +template +constexpr auto callback_with_state(Fns&&... fns) +{ + return _callback_with_state...>(LEXY_FWD(fns)...); +} + template struct _cb_from_sink { diff --git a/3rdparty/lexy/include/lexy/callback/aggregate.hpp b/3rdparty/lexy/include/lexy/callback/aggregate.hpp index ee947c4ba..97420f146 100644 --- a/3rdparty/lexy/include/lexy/callback/aggregate.hpp +++ b/3rdparty/lexy/include/lexy/callback/aggregate.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_AGGREGATE_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/callback/base.hpp b/3rdparty/lexy/include/lexy/callback/base.hpp index 5b60a8550..915afdd7d 100644 --- a/3rdparty/lexy/include/lexy/callback/base.hpp +++ b/3rdparty/lexy/include/lexy/callback/base.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_BASE_HPP_INCLUDED @@ -27,6 +27,13 @@ template constexpr bool is_callback_state = _detail::is_detected<_detect_callback_state, T, std::decay_t>; +template +using _detect_callback_with_state_for + = decltype(LEXY_DECLVAL(const T)[LEXY_DECLVAL(State&)](LEXY_DECLVAL(Args)...)); +template +constexpr bool is_callback_with_state_for + = _detail::is_detected<_detect_callback_with_state_for, std::decay_t, State, Args...>; + /// Returns the type of the `.sink()` function. template using sink_callback = decltype(LEXY_DECLVAL(Sink).sink(LEXY_DECLVAL(Args)...)); @@ -82,4 +89,3 @@ constexpr auto _make_overloaded(Op&&... op) } // namespace lexy #endif // LEXY_CALLBACK_BASE_HPP_INCLUDED - diff --git a/3rdparty/lexy/include/lexy/callback/bind.hpp b/3rdparty/lexy/include/lexy/callback/bind.hpp index 696711985..a73851ad4 100644 --- a/3rdparty/lexy/include/lexy/callback/bind.hpp +++ b/3rdparty/lexy/include/lexy/callback/bind.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_BIND_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/callback/bit_cast.hpp b/3rdparty/lexy/include/lexy/callback/bit_cast.hpp index b465e2737..9401c00f5 100644 --- a/3rdparty/lexy/include/lexy/callback/bit_cast.hpp +++ b/3rdparty/lexy/include/lexy/callback/bit_cast.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_BIT_CAST_HPP_INCLUDED @@ -16,7 +16,7 @@ #ifndef LEXY_HAS_BITCAST # if defined(__has_include) -# if __has_include() +# if __has_include() && __cplusplus >= 202002L # include # ifdef __cpp_lib_bit_cast # define LEXY_HAS_BITCAST 1 diff --git a/3rdparty/lexy/include/lexy/callback/composition.hpp b/3rdparty/lexy/include/lexy/callback/composition.hpp index 45142b5b4..0936c541b 100644 --- a/3rdparty/lexy/include/lexy/callback/composition.hpp +++ b/3rdparty/lexy/include/lexy/callback/composition.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_COMPOSITION_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/callback/constant.hpp b/3rdparty/lexy/include/lexy/callback/constant.hpp index 7a9a5fe2f..4ffbd61ca 100644 --- a/3rdparty/lexy/include/lexy/callback/constant.hpp +++ b/3rdparty/lexy/include/lexy/callback/constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_CONSTANT_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/callback/container.hpp b/3rdparty/lexy/include/lexy/callback/container.hpp index 5f2d35744..a96c64bab 100644 --- a/3rdparty/lexy/include/lexy/callback/container.hpp +++ b/3rdparty/lexy/include/lexy/callback/container.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_CONTAINER_HPP_INCLUDED @@ -16,7 +16,7 @@ template constexpr auto _has_reserve = _detail::is_detected<_detect_reserve, Container>; template -using _detect_append = decltype(LEXY_DECLVAL(Container&).append(LEXY_DECLVAL(Container &&))); +using _detect_append = decltype(LEXY_DECLVAL(Container&).append(LEXY_DECLVAL(Container&&))); template constexpr auto _has_append = _detail::is_detected<_detect_append, Container>; } // namespace lexy @@ -32,18 +32,19 @@ struct _list_sink using return_type = Container; template - auto operator()(U&& obj) -> decltype(LEXY_DECLVAL(C&).push_back(LEXY_FWD(obj))) + constexpr auto operator()(U&& obj) -> decltype(LEXY_DECLVAL(C&).push_back(LEXY_FWD(obj))) { return _result.push_back(LEXY_FWD(obj)); } template - auto operator()(Args&&... args) -> decltype(LEXY_DECLVAL(C&).emplace_back(LEXY_FWD(args)...)) + constexpr auto operator()(Args&&... args) + -> decltype(LEXY_DECLVAL(C&).emplace_back(LEXY_FWD(args)...)) { return _result.emplace_back(LEXY_FWD(args)...); } - Container&& finish() && + constexpr Container&& finish() && { return LEXY_MOV(_result); } @@ -171,18 +172,19 @@ struct _collection_sink using return_type = Container; template - auto operator()(U&& obj) -> decltype(LEXY_DECLVAL(C&).insert(LEXY_FWD(obj))) + constexpr auto operator()(U&& obj) -> decltype(LEXY_DECLVAL(C&).insert(LEXY_FWD(obj))) { return _result.insert(LEXY_FWD(obj)); } template - auto operator()(Args&&... args) -> decltype(LEXY_DECLVAL(C&).emplace(LEXY_FWD(args)...)) + constexpr auto operator()(Args&&... args) + -> decltype(LEXY_DECLVAL(C&).emplace(LEXY_FWD(args)...)) { return _result.emplace(LEXY_FWD(args)...); } - Container&& finish() && + constexpr Container&& finish() && { return LEXY_MOV(_result); } @@ -355,7 +357,7 @@ struct _concat using return_type = Container; - void operator()(Container&& container) + constexpr void operator()(Container&& container) { if (_result.empty()) { @@ -389,7 +391,7 @@ struct _concat } } - Container&& finish() && + constexpr Container&& finish() && { return LEXY_MOV(_result); } diff --git a/3rdparty/lexy/include/lexy/callback/fold.hpp b/3rdparty/lexy/include/lexy/callback/fold.hpp index f54d51978..b58e80dc7 100644 --- a/3rdparty/lexy/include/lexy/callback/fold.hpp +++ b/3rdparty/lexy/include/lexy/callback/fold.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_FOLD_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/callback/forward.hpp b/3rdparty/lexy/include/lexy/callback/forward.hpp index 3570781f3..2655af45b 100644 --- a/3rdparty/lexy/include/lexy/callback/forward.hpp +++ b/3rdparty/lexy/include/lexy/callback/forward.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_FORWARD_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/callback/integer.hpp b/3rdparty/lexy/include/lexy/callback/integer.hpp index 84883af20..ab7500210 100644 --- a/3rdparty/lexy/include/lexy/callback/integer.hpp +++ b/3rdparty/lexy/include/lexy/callback/integer.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_INTEGER_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/callback/noop.hpp b/3rdparty/lexy/include/lexy/callback/noop.hpp index 909ae21e8..a36e0dc30 100644 --- a/3rdparty/lexy/include/lexy/callback/noop.hpp +++ b/3rdparty/lexy/include/lexy/callback/noop.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_NOOP_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/callback/object.hpp b/3rdparty/lexy/include/lexy/callback/object.hpp index 5ebce6240..eb87e841d 100644 --- a/3rdparty/lexy/include/lexy/callback/object.hpp +++ b/3rdparty/lexy/include/lexy/callback/object.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_OBJECT_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/callback/string.hpp b/3rdparty/lexy/include/lexy/callback/string.hpp index 243117dbe..984f1e1ba 100644 --- a/3rdparty/lexy/include/lexy/callback/string.hpp +++ b/3rdparty/lexy/include/lexy/callback/string.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CALLBACK_STRING_HPP_INCLUDED @@ -149,39 +149,39 @@ struct _as_string using return_type = String; template - void operator()(CharT c) + constexpr void operator()(CharT c) { _result.push_back(c); } - void operator()(String&& str) + constexpr void operator()(String&& str) { _result.append(LEXY_MOV(str)); } template - auto operator()(Iterator begin, Iterator end) + constexpr auto operator()(Iterator begin, Iterator end) -> decltype(void(LEXY_DECLVAL(Str).append(begin, end))) { _result.append(begin, end); } template - void operator()(lexeme lex) + constexpr void operator()(lexeme lex) { static_assert(lexy::char_type_compatible_with_reader, "cannot convert lexeme to this string type"); _result.append(lex.begin(), lex.end()); } - void operator()(code_point cp) + constexpr void operator()(code_point cp) { typename Encoding::char_type buffer[4] = {}; auto size = _detail::encode_code_point(cp.value(), buffer, 4); _result.append(buffer, buffer + size); } - String&& finish() && + constexpr String&& finish() && { return _case_folding(LEXY_MOV(_result)); } diff --git a/3rdparty/lexy/include/lexy/code_point.hpp b/3rdparty/lexy/include/lexy/code_point.hpp index 072a0f307..a67b3bdbe 100644 --- a/3rdparty/lexy/include/lexy/code_point.hpp +++ b/3rdparty/lexy/include/lexy/code_point.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_CODE_POINT_HPP_INCLUDED @@ -8,10 +8,6 @@ #include #include -#ifndef LEXY_HAS_UNICODE_DATABASE -# define LEXY_HAS_UNICODE_DATABASE 0 -#endif - #if LEXY_HAS_UNICODE_DATABASE # define LEXY_UNICODE_CONSTEXPR constexpr #else @@ -89,7 +85,7 @@ class code_point LEXY_UNICODE_CATEGORY(Lo, other_letter), LEXY_UNICODE_CATEGORY(Mn, nonspacing_mark), - LEXY_UNICODE_CATEGORY(Mc, spaing_mark), + LEXY_UNICODE_CATEGORY(Mc, spacing_mark), LEXY_UNICODE_CATEGORY(Me, enclosing_mark), LEXY_UNICODE_CATEGORY(Nd, decimal_number), diff --git a/3rdparty/lexy/include/lexy/dsl.hpp b/3rdparty/lexy/include/lexy/dsl.hpp index c6a9efb5d..b41eeceb1 100644 --- a/3rdparty/lexy/include/lexy/dsl.hpp +++ b/3rdparty/lexy/include/lexy/dsl.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_HPP_INCLUDED @@ -60,5 +60,9 @@ #include #include +#if LEXY_EXPERIMENTAL +# include +#endif + #endif // LEXY_DSL_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/any.hpp b/3rdparty/lexy/include/lexy/dsl/any.hpp index 5913c94dc..2b30e39ef 100644 --- a/3rdparty/lexy/include/lexy/dsl/any.hpp +++ b/3rdparty/lexy/include/lexy/dsl/any.hpp @@ -1,9 +1,10 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_ANY_HPP_INCLUDED #define LEXY_DSL_ANY_HPP_INCLUDED +#include #include #include @@ -14,15 +15,24 @@ struct _any : token_base<_any, unconditional_branch_base> template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr std::true_type try_parse(Reader reader) { - while (reader.peek() != Reader::encoding::eof()) + using encoding = typename Reader::encoding; + if constexpr (lexy::_detail::is_swar_reader) + { + while (!lexy::_detail::swar_has_char( + reader.peek_swar())) + reader.bump_swar(); + } + + while (reader.peek() != encoding::eof()) reader.bump(); - end = reader.position(); + + end = reader.current(); return {}; } }; diff --git a/3rdparty/lexy/include/lexy/dsl/ascii.hpp b/3rdparty/lexy/include/lexy/dsl/ascii.hpp index 08cf64a05..7bc237034 100644 --- a/3rdparty/lexy/include/lexy/dsl/ascii.hpp +++ b/3rdparty/lexy/include/lexy/dsl/ascii.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_ASCII_HPP_INCLUDED @@ -8,6 +8,8 @@ #include #include +// SWAR tricks inspired by https://garbagecollected.org/2017/01/31/four-column-ascii/. + namespace lexyd::ascii { //=== control ===// @@ -25,6 +27,17 @@ struct _control : char_class_base<_control> result.insert(0x7F); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + using char_type = typename Encoding::char_type; + constexpr auto mask = lexy::_detail::swar_fill_compl(char_type(0b11111)); + constexpr auto expected = lexy::_detail::swar_fill(char_type(0b00'00000)); + + // We're only checking for 0x00-0x1F, and allow a false negative for 0x7F. + return (c & mask) == expected; + } }; inline constexpr auto control = _control{}; @@ -112,6 +125,22 @@ struct _lower : char_class_base<_lower> result.insert('a', 'z'); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + using char_type = typename Encoding::char_type; + + // All interesting characters are in column 4. + constexpr auto mask = lexy::_detail::swar_fill_compl(char_type(0b11111)); + constexpr auto expected = lexy::_detail::swar_fill(char_type(0b11'00000)); + + // But we need to eliminate ~ at the beginning and {|}~\x7F at the end. + constexpr auto offset_low = lexy::_detail::swar_fill(char_type(1)); + constexpr auto offset_high = lexy::_detail::swar_fill(char_type(5)); + + return ((c - offset_low) & mask) == expected && ((c + offset_high) & mask) == expected; + } }; inline constexpr auto lower = _lower{}; @@ -128,6 +157,22 @@ struct _upper : char_class_base<_upper> result.insert('A', 'Z'); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + using char_type = typename Encoding::char_type; + + // All interesting characters are in column 3. + constexpr auto mask = lexy::_detail::swar_fill_compl(char_type(0b11111)); + constexpr auto expected = lexy::_detail::swar_fill(char_type(0b10'00000)); + + // But we need to eliminate @ at the beginning and [\]^_ at the end. + constexpr auto offset_low = lexy::_detail::swar_fill(char_type(1)); + constexpr auto offset_high = lexy::_detail::swar_fill(char_type(5)); + + return ((c - offset_low) & mask) == expected && ((c + offset_high) & mask) == expected; + } }; inline constexpr auto upper = _upper{}; @@ -145,6 +190,13 @@ struct _alpha : char_class_base<_alpha> result.insert('A', 'Z'); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + // We're assuming lower characters are more common, so do the efficient check only for them. + return _lower::template char_class_match_swar(c); + } }; inline constexpr auto alpha = _alpha{}; @@ -163,6 +215,13 @@ struct _alphau : char_class_base<_alphau> result.insert('_'); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + // We're assuming alpha characters are more common, so do the efficient check only for them. + return _alpha::template char_class_match_swar(c); + } }; inline constexpr auto alpha_underscore = _alphau{}; @@ -180,6 +239,21 @@ struct _digit : char_class_base<_digit> result.insert('0', '9'); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + using char_type = typename Encoding::char_type; + + // All interesting characters are in the second half of column 1. + constexpr auto mask = lexy::_detail::swar_fill_compl(char_type(0b01111)); + constexpr auto expected = lexy::_detail::swar_fill(char_type(0b01'10000)); + + // But we need to eliminate :;<=>? at the end. + constexpr auto offset_high = lexy::_detail::swar_fill(char_type(6)); + + return (c & mask) == expected && ((c + offset_high) & mask) == expected; + } }; inline constexpr auto digit = _digit{}; @@ -197,6 +271,13 @@ struct _alnum : char_class_base<_alnum> result.insert(_digit::char_class_ascii()); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + // We're assuming alpha characters are more common, so do the efficient check only for them. + return _alpha::template char_class_match_swar(c); + } }; inline constexpr auto alnum = _alnum{}; inline constexpr auto alpha_digit = _alnum{}; @@ -215,6 +296,14 @@ struct _word : char_class_base<_word> result.insert(_digit::char_class_ascii()); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + // We're assuming alphau characters are more common, so do the efficient check only for + // them. + return _alphau::template char_class_match_swar(c); + } }; inline constexpr auto word = _word{}; inline constexpr auto alpha_digit_underscore = _word{}; @@ -281,6 +370,35 @@ struct _graph : char_class_base<_graph> result.insert(0x21, 0x7E); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + using char_type = typename Encoding::char_type; + + // First check that we have only ASCII, but shifted by one, so we also exclude 0x7F. + constexpr auto ascii_mask = lexy::_detail::swar_fill_compl(char_type(0b11'11111)); + constexpr auto ascii_offset = lexy::_detail::swar_fill(char_type(1)); + constexpr auto ascii_expected = lexy::_detail::swar_fill(char_type(0)); + if (((c + ascii_offset) & ascii_mask) != ascii_expected) + return false; + + // The above check also included 0xFF for single byte encodings where it overflowed, + // so do a separate check in those cases. + if constexpr (sizeof(char_type) == 1) + { + if ((c & ascii_mask) != ascii_expected) + return false; + } + + // Then we must not have a character in column 0, or space. + // If we subtract one we turn 0x21-0x01 into column 0 and 0x00 to a value definitely not in + // column 0, so need to check both. + constexpr auto mask = lexy::_detail::swar_fill_compl(char_type(0b11111)); + constexpr auto offset_low = lexy::_detail::swar_fill(char_type(1)); + return !lexy::_detail::swar_has_zero(c & mask) + && !lexy::_detail::swar_has_zero((c - offset_low) & mask); + } }; inline constexpr auto graph = _graph{}; @@ -297,6 +415,31 @@ struct _print : char_class_base<_print> result.insert(0x20, 0x7E); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + using char_type = typename Encoding::char_type; + + // First check that we have only ASCII, but shifted by one, so we also exclude 0x7F. + constexpr auto ascii_mask = lexy::_detail::swar_fill_compl(char_type(0b11'11111)); + constexpr auto ascii_offset = lexy::_detail::swar_fill(char_type(1)); + constexpr auto ascii_expected = lexy::_detail::swar_fill(char_type(0)); + if (((c + ascii_offset) & ascii_mask) != ascii_expected) + return false; + + // The above check also included 0xFF for single byte encodings where it overflowed, + // so do a separate check in those cases. + if constexpr (sizeof(char_type) == 1) + { + if ((c & ascii_mask) != ascii_expected) + return false; + } + + // Then we must not have a character in column 0. + constexpr auto mask = lexy::_detail::swar_fill_compl(char_type(0b11111)); + return !lexy::_detail::swar_has_zero(c & mask); + } }; inline constexpr auto print = _print{}; @@ -313,6 +456,17 @@ struct _char : char_class_base<_char> result.insert(0x00, 0x7F); return result; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + using char_type = typename Encoding::char_type; + + constexpr auto mask = lexy::_detail::swar_fill_compl(char_type(0b11'11111)); + constexpr auto expected = lexy::_detail::swar_fill(char_type(0)); + + return (c & mask) == expected; + } }; inline constexpr auto character = _char{}; } // namespace lexyd::ascii diff --git a/3rdparty/lexy/include/lexy/dsl/base.hpp b/3rdparty/lexy/include/lexy/dsl/base.hpp index 51daf12e6..ea965b0b6 100644 --- a/3rdparty/lexy/include/lexy/dsl/base.hpp +++ b/3rdparty/lexy/include/lexy/dsl/base.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_BASE_HPP_INCLUDED @@ -12,6 +12,19 @@ //=== parse_events ===// namespace lexy::parse_events { +/// Parsing started. +/// Arguments: position +struct grammar_start +{}; +/// Parsing finished succesfully. +/// Arguments: the reader at the final parse position. +struct grammar_finish +{}; +/// Parsing finished unsuccesfully. +/// Arguments: the reader at the final parse position. +struct grammar_cancel +{}; + /// Start of the current production. /// Arguments: position struct production_start @@ -102,6 +115,13 @@ using parser_for = typename Rule::template p; template using branch_parser_for = typename BranchRule::template bp; +template +struct _pb : lexy::branch_parser_for, Reader> +{}; +// We create a new type here to shorten its name. +template +using production_branch_parser = _pb; + /// A branch parser that takes a branch unconditionally and forwards to the regular parser. template struct unconditional_branch_parser @@ -230,7 +250,7 @@ LEXY_FORCE_INLINE constexpr auto try_match_token(TokenRule, Reader& reader) if constexpr (std::is_same_v) { parser.try_parse(reader); - reader.set_position(parser.end); + reader.reset(parser.end); return std::true_type{}; } else if constexpr (std::is_same_v) @@ -243,7 +263,7 @@ LEXY_FORCE_INLINE constexpr auto try_match_token(TokenRule, Reader& reader) if (!parser.try_parse(reader)) return false; - reader.set_position(parser.end); + reader.reset(parser.end); return true; } } diff --git a/3rdparty/lexy/include/lexy/dsl/bits.hpp b/3rdparty/lexy/include/lexy/dsl/bits.hpp index 409764a70..ab6d484c0 100644 --- a/3rdparty/lexy/include/lexy/dsl/bits.hpp +++ b/3rdparty/lexy/include/lexy/dsl/bits.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_BITS_HPP_INCLUDED @@ -104,13 +104,13 @@ struct _bits : token_base<_bits> template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { - static_assert(std::is_same_v); + static_assert(lexy::is_byte_encoding); auto byte = reader.peek(); if (byte == Reader::encoding::eof() @@ -118,14 +118,14 @@ struct _bits : token_base<_bits> return false; reader.bump(); - end = reader.position(); + end = reader.current(); return true; } template constexpr void report_error(Context& context, const Reader&) { - auto err = lexy::error(end, "bits"); + auto err = lexy::error(end.position(), "bits"); context.on(_ev::error{}, err); } }; diff --git a/3rdparty/lexy/include/lexy/dsl/bom.hpp b/3rdparty/lexy/include/lexy/dsl/bom.hpp index 12146b4f7..a3010cc37 100644 --- a/3rdparty/lexy/include/lexy/dsl/bom.hpp +++ b/3rdparty/lexy/include/lexy/dsl/bom.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_BOM_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/brackets.hpp b/3rdparty/lexy/include/lexy/dsl/brackets.hpp index 616e5f35b..7e461c929 100644 --- a/3rdparty/lexy/include/lexy/dsl/brackets.hpp +++ b/3rdparty/lexy/include/lexy/dsl/brackets.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_BRACKETS_HPP_INCLUDED @@ -100,7 +100,8 @@ struct _brackets template constexpr auto brackets(Open, Close) { - static_assert(lexy::is_branch_rule && lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Open, "brackets()"); + LEXY_REQUIRE_BRANCH_RULE(Close, "brackets()"); return _brackets{}; } diff --git a/3rdparty/lexy/include/lexy/dsl/branch.hpp b/3rdparty/lexy/include/lexy/dsl/branch.hpp index 07a3c6338..1bbd7fd18 100644 --- a/3rdparty/lexy/include/lexy/dsl/branch.hpp +++ b/3rdparty/lexy/include/lexy/dsl/branch.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_BRANCH_HPP_INCLUDED @@ -31,19 +31,19 @@ struct _br : _copy_base template constexpr auto operator>>(Condition, Then) { - static_assert(lexy::is_branch_rule, "condition must be a branch"); + LEXY_REQUIRE_BRANCH_RULE(Condition, "Left-hand-side of >>"); return _br{}; } template constexpr auto operator>>(Condition, _seq) { - static_assert(lexy::is_branch_rule, "condition must be a branch"); + LEXY_REQUIRE_BRANCH_RULE(Condition, "Left-hand-side of >>"); return _br{}; } template constexpr auto operator>>(Condition, _br) { - static_assert(lexy::is_branch_rule, "condition must be a branch"); + LEXY_REQUIRE_BRANCH_RULE(Condition, "Left-hand-side of >>"); return _br{}; } diff --git a/3rdparty/lexy/include/lexy/dsl/byte.hpp b/3rdparty/lexy/include/lexy/dsl/byte.hpp index bc9d0907e..32df8597b 100644 --- a/3rdparty/lexy/include/lexy/dsl/byte.hpp +++ b/3rdparty/lexy/include/lexy/dsl/byte.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_BYTE_HPP_INCLUDED @@ -7,58 +7,140 @@ #include #include #include +#include #include //=== byte ===// namespace lexyd { -template -struct _b : token_base<_b> +template +struct _b : token_base<_b> { static_assert(N > 0); + static constexpr bool _match(lexy::byte_encoding::int_type cur) + { + if (cur == lexy::byte_encoding::eof()) + return false; + + if constexpr (!std::is_void_v) + { + constexpr auto predicate = Predicate{}; + return predicate(static_cast(cur)); + } + else + { + return true; + } + } + template > struct tp; + template struct tp> { - typename Reader::iterator end; + static_assert(lexy::is_byte_encoding); + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { - static_assert(std::is_same_v); - // Bump N times. - auto result = ((reader.peek() == Reader::encoding::eof() ? ((void)Idx, false) - : (reader.bump(), true)) - && ...); - end = reader.position(); + auto result + = ((_match(reader.peek()) ? (reader.bump(), true) : ((void)Idx, false)) && ...); + end = reader.current(); return result; } template constexpr void report_error(Context& context, const Reader&) { - auto err = lexy::error(end, "byte"); + constexpr auto name + = std::is_void_v ? "byte" : lexy::_detail::type_name(); + auto err = lexy::error(end.position(), name); context.on(_ev::error{}, err); } }; + + //=== dsl ===// + template + constexpr auto if_() const + { + static_assert(std::is_void_v); + return _b{}; + } + + template + constexpr auto range() const + { + struct predicate + { + static LEXY_CONSTEVAL auto name() + { + return "byte.range"; + } + + constexpr bool operator()(unsigned char byte) const + { + return Low <= byte && byte <= High; + } + }; + + return if_(); + } + + template + constexpr auto set() const + { + struct predicate + { + static LEXY_CONSTEVAL auto name() + { + return "byte.set"; + } + + constexpr bool operator()(unsigned char byte) const + { + return ((byte == Bytes) || ...); + } + }; + + return if_(); + } + + constexpr auto ascii() const + { + struct predicate + { + static LEXY_CONSTEVAL auto name() + { + return "byte.ASCII"; + } + + constexpr bool operator()(unsigned char byte) const + { + return byte <= 0x7F; + } + }; + + return if_(); + } }; /// Matches an arbitrary byte. -constexpr auto byte = _b<1>{}; +constexpr auto byte = _b<1, void>{}; /// Matches N arbitrary bytes. template -constexpr auto bytes = _b{}; +constexpr auto bytes = _b{}; } // namespace lexyd namespace lexy { template -constexpr auto token_kind_of> = lexy::any_token_kind; +constexpr auto token_kind_of> = lexy::any_token_kind; } // namespace lexy //=== padding bytes ===// @@ -84,13 +166,14 @@ struct _pb : branch_base template struct bp { - typename Reader::iterator end; + static_assert(lexy::is_byte_encoding); + typename Reader::marker end; constexpr auto try_parse(const void*, const Reader& reader) { - lexy::token_parser_for<_b, Reader> parser(reader); - auto result = parser.try_parse(reader); - end = parser.end; + lexy::token_parser_for<_b, Reader> parser(reader); + auto result = parser.try_parse(reader); + end = parser.end; return result; } @@ -102,10 +185,10 @@ struct _pb : branch_base LEXY_PARSER_FUNC auto finish(Context& context, Reader& reader, Args&&... args) { auto begin = reader.position(); - context.on(_ev::token{}, lexy::any_token_kind, begin, end); - reader.set_position(end); + context.on(_ev::token{}, lexy::any_token_kind, begin, end.position()); + reader.reset(end); - _validate(context, reader, begin, end); + _validate(context, reader, begin, end.position()); return lexy::whitespace_parser::parse(context, reader, LEXY_FWD(args)...); } @@ -117,8 +200,9 @@ struct _pb : branch_base template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { + static_assert(lexy::is_byte_encoding); auto begin = reader.position(); - if (!_b::token_parse(context, reader)) + if (!_b::token_parse(context, reader)) return false; auto end = reader.position(); @@ -167,6 +251,7 @@ auto _bint() return 0; } } + template using bint = decltype(_bint()); } // namespace lexy::_detail @@ -187,10 +272,11 @@ namespace lexyd template struct _bint : branch_base { - using _rule = lexy::_detail::type_or>; + using _rule = lexy::_detail::type_or>; template > struct _pc; + template struct _pc> { @@ -238,7 +324,8 @@ struct _bint : branch_base template struct bp { - typename Reader::iterator end; + static_assert(lexy::is_byte_encoding); + typename Reader::marker end; constexpr auto try_parse(const void*, const Reader& reader) { @@ -256,10 +343,11 @@ struct _bint : branch_base LEXY_PARSER_FUNC auto finish(Context& context, Reader& reader, Args&&... args) { auto begin = reader.position(); - context.on(_ev::token{}, _rule{}, begin, end); - reader.set_position(end); + context.on(_ev::token{}, _rule{}, begin, end.position()); + reader.reset(end); - return _pc::parse(context, reader, begin, end, LEXY_FWD(args)...); + return _pc::parse(context, reader, begin, end.position(), + LEXY_FWD(args)...); } }; @@ -269,6 +357,7 @@ struct _bint : branch_base template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { + static_assert(lexy::is_byte_encoding); auto begin = reader.position(); if (!_rule::token_parse(context, reader)) return false; @@ -308,4 +397,3 @@ inline constexpr auto big_bint64 = _bint<8, lexy::_detail::bint_big>{}; } // namespace lexyd #endif // LEXY_DSL_BYTE_HPP_INCLUDED - diff --git a/3rdparty/lexy/include/lexy/dsl/capture.hpp b/3rdparty/lexy/include/lexy/dsl/capture.hpp index 86a92dc97..92d4d8de2 100644 --- a/3rdparty/lexy/include/lexy/dsl/capture.hpp +++ b/3rdparty/lexy/include/lexy/dsl/capture.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_CAPTURE_HPP_INCLUDED @@ -16,7 +16,7 @@ struct _cap : _copy_base template struct bp { - typename Reader::iterator end; + typename Reader::marker end; constexpr auto try_parse(const void*, const Reader& reader) { @@ -35,12 +35,12 @@ struct _cap : _copy_base { auto begin = reader.position(); - context.on(_ev::token{}, Token{}, begin, end); - reader.set_position(end); + context.on(_ev::token{}, Token{}, begin, end.position()); + reader.reset(end); using continuation = lexy::whitespace_parser; return continuation::parse(context, reader, LEXY_FWD(args)..., - lexy::lexeme(begin, end)); + lexy::lexeme(begin, end.position())); } }; diff --git a/3rdparty/lexy/include/lexy/dsl/case_folding.hpp b/3rdparty/lexy/include/lexy/dsl/case_folding.hpp index 740d232dd..c4beb55dd 100644 --- a/3rdparty/lexy/include/lexy/dsl/case_folding.hpp +++ b/3rdparty/lexy/include/lexy/dsl/case_folding.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_CASE_FOLDING_HPP_INCLUDED @@ -28,6 +28,12 @@ struct _cfl : token_base<_cfl>, _lit_base using lit_case_folding = _cfl_folding; + template + static constexpr auto lit_first_char() -> typename Encoding::char_type + { + return Literal::template lit_first_char(); + } + template static LEXY_CONSTEVAL std::size_t lit_insert(Trie& trie, std::size_t pos, std::size_t char_class) @@ -39,10 +45,10 @@ struct _cfl : token_base<_cfl>, _lit_base struct tp { lexy::token_parser_for> impl; - typename Reader::iterator end; + typename Reader::marker end; constexpr explicit tp(const Reader& reader) - : impl(CaseFolding{reader}), end(reader.position()) + : impl(CaseFolding{reader}), end(reader.current()) {} constexpr bool try_parse(Reader _reader) @@ -78,6 +84,7 @@ struct _acfr // ascii case folding reader using encoding = typename Reader::encoding; using iterator = typename Reader::iterator; + using marker = typename Reader::marker; constexpr auto peek() const -> typename encoding::int_type { @@ -98,9 +105,13 @@ struct _acfr // ascii case folding reader return _impl.position(); } - constexpr void set_position(iterator new_pos) + constexpr marker current() const noexcept { - _impl.set_position(new_pos); + return _impl.current(); + } + constexpr void reset(marker m) noexcept + { + _impl.reset(m); } }; } // namespace lexy @@ -140,6 +151,7 @@ struct _sucfr32 // simple unicode case folding reader, UTF-32 using encoding = typename Reader::encoding; using iterator = typename Reader::iterator; + using marker = typename Reader::marker; constexpr auto peek() const -> typename encoding::int_type { @@ -157,9 +169,13 @@ struct _sucfr32 // simple unicode case folding reader, UTF-32 return _impl.position(); } - constexpr void set_position(iterator new_pos) + constexpr marker current() const noexcept { - _impl.set_position(new_pos); + return _impl.current(); + } + constexpr void reset(marker m) noexcept + { + _impl.reset(m); } }; @@ -168,22 +184,23 @@ struct _sucfrm // simple unicode case folding reader, UTF-8 and UTF-16 { using encoding = typename Reader::encoding; using iterator = typename Reader::iterator; + using marker = typename Reader::marker; Reader _impl; - typename Reader::iterator _cur_pos; + typename Reader::marker _cur_pos; typename encoding::char_type _buffer[4]; unsigned char _buffer_size; unsigned char _buffer_cur; constexpr explicit _sucfrm(Reader impl) - : _impl(impl), _cur_pos(_impl.position()), _buffer{}, _buffer_size(0), _buffer_cur(0) + : _impl(impl), _cur_pos(_impl.current()), _buffer{}, _buffer_size(0), _buffer_cur(0) { _fill(); } constexpr void _fill() { - _cur_pos = _impl.position(); + _cur_pos = _impl.current(); // We need to read the next code point at this point. auto result = lexy::_detail::parse_code_point(_impl); @@ -194,13 +211,13 @@ struct _sucfrm // simple unicode case folding reader, UTF-8 and UTF-16 _buffer_size = static_cast( lexy::_detail::encode_code_point(folded.value(), _buffer, 4)); _buffer_cur = 0; - _impl.set_position(result.end); + _impl.reset(result.end); } else { // Fill the buffer with the partial code point. _buffer_cur = _buffer_size = 0; - while (_impl.position() != result.end) + while (_impl.position() != result.end.position()) { _buffer[_buffer_size] = static_cast(_impl.peek()); ++_buffer_size; @@ -227,22 +244,23 @@ struct _sucfrm // simple unicode case folding reader, UTF-8 and UTF-16 constexpr iterator position() const { - // We only report the position at a code point boundary. + return current().position(); + } + + constexpr marker current() const noexcept + { + // We only report a marker at a code point boundary. // This has two consequences: // 1. If we don't match a rule, the error token does not include any common start code - // units. - // That's actually nice, and makes it unnecessary to handle that situation in the error - // reporting. The only relevant difference is in the error token. + // units. That's actually nice, and makes it unnecessary to handle that situation in the + // error reporting. The only relevant difference is in the error token. // 2. If the user wants to match partial code unit sequences, the behavior can become buggy. // However, that's not really something we should worry about. return _cur_pos; } - - constexpr void set_position(iterator new_pos) + constexpr void reset(marker m) noexcept { - // It's a code point boundary, so reset. - _impl.set_position(new_pos); - _fill(); + _impl.reset(m); } }; diff --git a/3rdparty/lexy/include/lexy/dsl/char_class.hpp b/3rdparty/lexy/include/lexy/dsl/char_class.hpp index 9b2e6c17b..cc09ab31e 100644 --- a/3rdparty/lexy/include/lexy/dsl/char_class.hpp +++ b/3rdparty/lexy/include/lexy/dsl/char_class.hpp @@ -1,10 +1,11 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_CHAR_CLASS_HPP_INCLUDED #define LEXY_DSL_CHAR_CLASS_HPP_INCLUDED #include +#include #include #include @@ -172,7 +173,7 @@ struct char_class_base : token_base, _char_class_base // static const char* char_class_name(); // static ascii_set char_class_ascii(); - static constexpr auto char_class_unicode() + static constexpr bool char_class_unicode() { return true; } @@ -191,21 +192,31 @@ struct char_class_base : token_base, _char_class_base context.on(_ev::error{}, err); } + /// Returns true if c contains only characters from the char class. + /// If it returns false, it may still be valid, it just couldn't be detected. + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int) + { + return std::false_type{}; + } + //=== provided functions ===// template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { + static_assert(lexy::is_char_encoding); + using matcher = lexy::_detail::ascii_set_matcher<_cas>; if (matcher::template match(reader.peek())) { reader.bump(); - end = reader.position(); + end = reader.current(); return true; } @@ -214,38 +225,37 @@ struct char_class_base : token_base, _char_class_base { return false; } - else if constexpr (std::is_same_v // - || std::is_same_v) + else if constexpr (lexy::is_unicode_encoding) { - static_assert(!Derived::char_class_unicode(), - "cannot use this character class with default/byte_encoding"); + static_assert(Derived::char_class_unicode(), + "cannot use this character class with Unicode encoding"); - if (reader.peek() == Reader::encoding::eof()) + // Parse one code point. + auto result = lexy::_detail::parse_code_point(reader); + if (result.error != lexy::_detail::cp_error::success) return false; - auto cp = static_cast(reader.peek()); - reader.bump(); - - if (!Derived::char_class_match_cp(cp)) + if (!Derived::char_class_match_cp(result.cp)) return false; - end = reader.position(); + end = result.end; return true; } else { - static_assert(Derived::char_class_unicode(), - "cannot use this character class with Unicode encoding"); + static_assert(!Derived::char_class_unicode(), + "cannot use this character class with non-Unicode char encodings"); - // Parse one code point. - auto result = lexy::_detail::parse_code_point(reader); - if (result.error != lexy::_detail::cp_error::success) + if (reader.peek() == Reader::encoding::eof()) return false; - if (!Derived::char_class_match_cp(result.cp)) + auto cp = static_cast(reader.peek()); + reader.bump(); + + if (!Derived::char_class_match_cp(cp)) return false; - end = result.end; + end = reader.current(); return true; } } @@ -352,8 +362,8 @@ constexpr auto _make_char_class(C c) return c; } template || std::is_same_v>> + typename = std::enable_if_t + || std::is_same_v>> constexpr auto _make_char_class(_lit) { if constexpr (std::is_same_v) @@ -402,8 +412,8 @@ struct _calt : char_class_base<_calt> static constexpr auto char_class_match_cp(char32_t cp) { - if constexpr ((std::is_same_v && ...)) + if constexpr ((std::is_same_v + && ...)) return std::false_type{}; else return (Cs::char_class_match_cp(cp) || ...); @@ -563,8 +573,8 @@ struct _cand : char_class_base<_cand> static constexpr auto char_class_match_cp(char32_t cp) { - if constexpr ((std::is_same_v && ...)) + if constexpr ((std::is_same_v + && ...)) return std::false_type{}; else return (Cs::char_class_match_cp(cp) && ...); diff --git a/3rdparty/lexy/include/lexy/dsl/choice.hpp b/3rdparty/lexy/include/lexy/dsl/choice.hpp index 70e094db6..ed0fbd952 100644 --- a/3rdparty/lexy/include/lexy/dsl/choice.hpp +++ b/3rdparty/lexy/include/lexy/dsl/choice.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_CHOICE_HPP_INCLUDED @@ -134,20 +134,20 @@ struct _chc template constexpr auto operator|(R, S) { - static_assert(lexy::is_branch_rule, "choice requires a branch condition"); - static_assert(lexy::is_branch_rule, "choice requires a branch condition"); + LEXY_REQUIRE_BRANCH_RULE(R, "choice"); + LEXY_REQUIRE_BRANCH_RULE(S, "choice"); return _chc{}; } template constexpr auto operator|(_chc, S) { - static_assert(lexy::is_branch_rule, "choice requires a branch condition"); + LEXY_REQUIRE_BRANCH_RULE(S, "choice"); return _chc{}; } template constexpr auto operator|(R, _chc) { - static_assert(lexy::is_branch_rule, "choice requires a branch condition"); + LEXY_REQUIRE_BRANCH_RULE(R, "choice"); return _chc{}; } template diff --git a/3rdparty/lexy/include/lexy/dsl/code_point.hpp b/3rdparty/lexy/include/lexy/dsl/code_point.hpp index cb068e5c2..bc09666aa 100644 --- a/3rdparty/lexy/include/lexy/dsl/code_point.hpp +++ b/3rdparty/lexy/include/lexy/dsl/code_point.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_CODE_POINT_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/combination.hpp b/3rdparty/lexy/include/lexy/dsl/combination.hpp index 025083601..73db67b97 100644 --- a/3rdparty/lexy/include/lexy/dsl/combination.hpp +++ b/3rdparty/lexy/include/lexy/dsl/combination.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_COMBINATION_HPP_INCLUDED @@ -130,7 +130,7 @@ struct _comb : rule_base template constexpr auto combination(R...) { - static_assert((lexy::is_branch_rule && ...), "combination() requires a branch rule"); + LEXY_REQUIRE_BRANCH_RULE(R..., "combination()"); static_assert((!lexy::is_unconditional_branch_rule && ...), "combination() does not support unconditional branches"); return _comb{}; @@ -141,7 +141,7 @@ constexpr auto combination(R...) template constexpr auto partial_combination(R...) { - static_assert((lexy::is_branch_rule && ...), "partial_combination() requires a branch rule"); + LEXY_REQUIRE_BRANCH_RULE(R..., "partial_combination()"); static_assert((!lexy::is_unconditional_branch_rule && ...), "partial_combination() does not support unconditional branches"); // If the choice no longer matches, we just break. diff --git a/3rdparty/lexy/include/lexy/dsl/context_counter.hpp b/3rdparty/lexy/include/lexy/dsl/context_counter.hpp index 1deae2343..411ca1ad9 100644 --- a/3rdparty/lexy/include/lexy/dsl/context_counter.hpp +++ b/3rdparty/lexy/include/lexy/dsl/context_counter.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_CONTEXT_COUNTER_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/context_flag.hpp b/3rdparty/lexy/include/lexy/dsl/context_flag.hpp index dcd442f42..6782c5322 100644 --- a/3rdparty/lexy/include/lexy/dsl/context_flag.hpp +++ b/3rdparty/lexy/include/lexy/dsl/context_flag.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_CONTEXT_FLAG_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/context_identifier.hpp b/3rdparty/lexy/include/lexy/dsl/context_identifier.hpp index 282a28e38..6850904f9 100644 --- a/3rdparty/lexy/include/lexy/dsl/context_identifier.hpp +++ b/3rdparty/lexy/include/lexy/dsl/context_identifier.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_CONTEXT_IDENTIFIER_HPP_INCLUDED @@ -73,7 +73,7 @@ struct _ctx_irem : branch_base template struct bp { - typename Reader::iterator end; + typename Reader::marker end; template constexpr bool try_parse(const ControlBlock* cb, const Reader& reader) @@ -85,7 +85,7 @@ struct _ctx_irem : branch_base end = parser.end; // The two lexemes need to be equal. - auto lexeme = lexy::lexeme(reader.position(), end); + auto lexeme = lexy::lexeme(reader.position(), end.position()); return lexy::_detail::equal_lexemes(_ctx_id::get(cb), lexeme); } @@ -97,8 +97,9 @@ struct _ctx_irem : branch_base LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { // Finish parsing the token. - context.on(_ev::token{}, lexy::identifier_token_kind, reader.position(), end); - reader.set_position(end); + context.on(_ev::token{}, lexy::identifier_token_kind, reader.position(), + end.position()); + reader.reset(end); return lexy::whitespace_parser::parse(context, reader, LEXY_FWD(args)...); } diff --git a/3rdparty/lexy/include/lexy/dsl/delimited.hpp b/3rdparty/lexy/include/lexy/dsl/delimited.hpp index d878c9b38..0b6684d94 100644 --- a/3rdparty/lexy/include/lexy/dsl/delimited.hpp +++ b/3rdparty/lexy/include/lexy/dsl/delimited.hpp @@ -1,9 +1,10 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_DELIMITED_HPP_INCLUDED #define LEXY_DSL_DELIMITED_HPP_INCLUDED +#include #include #include #include @@ -48,39 +49,68 @@ struct _del_chars begin = recover_end; } + template + constexpr void parse_swar(Reader& reader, Close, Escs...) + { + using encoding = typename Reader::encoding; + + // If we have a SWAR reader and the Close and Escape chars are literal rules, + // we can munch as much content as possible in a fast loop. + // We also need to efficiently check for the CharClass for it to make sense. + if constexpr (lexy::_detail::is_swar_reader // + && (lexy::is_literal_rule && ... && Escs::esc_is_literal) + && !std::is_same_v< + decltype(CharClass::template char_class_match_swar({})), + std::false_type>) + { + using char_type = typename encoding::char_type; + using lexy::_detail::swar_has_char; + + while (true) + { + auto cur = reader.peek_swar(); + + // If we have an EOF or the initial character of the closing delimiter, we exit as + // we have no more content. + if (swar_has_char(cur) + || swar_has_char()>(cur)) + break; + + // The same is true if we have the escape character. + if constexpr (sizeof...(Escs) > 0) + { + if ((swar_has_char()>(cur) + || ...)) + break; + } + + // We definitely don't have the end of the delimited content in the current SWAR, + // check if they all follow the char class. + if (!CharClass::template char_class_match_swar(cur)) + // They don't or we need to look closer, exit the loop. + break; + + reader.bump_swar(); + } + } + } + + // Precondition: the next code unit definitely belongs to the content, not the delimiter. template - constexpr void parse(Context& context, Reader& reader, Sink& sink) + constexpr void parse_one(Context& context, Reader& reader, Sink& sink) { + using encoding = typename Reader::encoding; + // First try to match the ASCII characters. using matcher = lexy::_detail::ascii_set_matcher<_cas>; - if (matcher::template match(reader.peek())) + if (matcher::template match(reader.peek())) { reader.bump(); } else if constexpr (!std::is_same_v) { - // Try to match any code point in default_encoding or byte_encoding. - if constexpr (std::is_same_v // - || std::is_same_v) - { - static_assert(!CharClass::char_class_unicode(), - "cannot use this character class with default/byte_encoding"); - LEXY_ASSERT(reader.peek() != Reader::encoding::eof(), - "EOF should be checked before calling this"); - - auto recover_begin = reader.position(); - auto cp = static_cast(reader.peek()); - reader.bump(); - - if (!CharClass::char_class_match_cp(cp)) - { - finish(context, sink, recover_begin); - _recover(context, recover_begin, reader.position()); - } - } - // Otherwise, try to match Unicode characters. - else + if constexpr (lexy::is_unicode_encoding) { static_assert(CharClass::char_class_unicode(), "cannot use this character class with Unicode encoding"); @@ -89,13 +119,35 @@ struct _del_chars if (result.error == lexy::_detail::cp_error::success && CharClass::char_class_match_cp(result.cp)) { - reader.set_position(result.end); + reader.reset(result.end); } else { finish(context, sink, reader.position()); - _recover(context, reader.position(), result.end); - reader.set_position(result.end); + + auto recover_begin = reader.position(); + if (recover_begin == result.end.position()) + reader.bump(); + else + reader.reset(result.end); + _recover(context, recover_begin, reader.position()); + } + } + else + { + static_assert(!CharClass::char_class_unicode(), + "cannot use this character class with non-Unicode char encoding"); + LEXY_ASSERT(reader.peek() != encoding::eof(), + "EOF should be checked before calling this"); + + auto recover_begin = reader.position(); + auto cp = static_cast(reader.peek()); + reader.bump(); + + if (!CharClass::char_class_match_cp(cp)) + { + finish(context, sink, recover_begin); + _recover(context, recover_begin, reader.position()); } } } @@ -103,7 +155,7 @@ struct _del_chars else { // We can just discard the invalid ASCII character. - LEXY_ASSERT(reader.peek() != Reader::encoding::eof(), + LEXY_ASSERT(reader.peek() != encoding::eof(), "EOF should be checked before calling this"); auto recover_begin = reader.position(); reader.bump(); @@ -155,12 +207,20 @@ struct _del : rule_base _del_limit, Limit>; template - static constexpr bool _loop(CloseParser& close, Context& context, Reader& reader, Sink& sink) + LEXY_PARSER_FUNC static bool _loop(CloseParser& close, Context& context, Reader& reader, + Sink& sink) { auto del_begin = reader.position(); _del_chars cur_chars(reader); - while (!close.try_parse(context.control_block, reader)) + while (true) { + // Parse as many content chars as possible. + // If it returns, we need to look closer at the next char. + cur_chars.parse_swar(reader, Close{}, Escapes{}...); + + // Check for closing delimiter. + if (close.try_parse(context.control_block, reader)) + break; close.cancel(context); // Check for missing delimiter. @@ -176,12 +236,12 @@ struct _del : rule_base } // Check for escape sequences. - if ((Escapes::_try_parse(context, reader, sink, cur_chars) || ...)) + if ((Escapes::esc_try_parse(context, reader, sink, cur_chars) || ...)) // We had an escape sequence, so do nothing in this iteration. continue; - // Parse the next character. - cur_chars.parse(context, reader, sink); + // It is actually a content char, consume it. + cur_chars.parse_one(context, reader, sink); } // Finish the currently active character sequence. @@ -195,6 +255,7 @@ struct _del : rule_base template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { + static_assert(lexy::is_char_encoding); auto sink = context.value_callback().sink(); // Parse characters until we have the closing delimiter. @@ -223,7 +284,7 @@ struct _escape_base template struct _delim_dsl { - /// Add literal tokens that will limit the delimited to detect a missing terminator. + /// Add char classes that will limit the delimited to detect a missing terminator. template constexpr auto limit(LimitCharClass) const { @@ -231,7 +292,7 @@ struct _delim_dsl return _delim_dsl{}; } - /// Add literal tokens that will limit the delimited and specify the error. + /// Add char classes that will limit the delimited and specify the error. template constexpr auto limit(LimitCharClass) const { @@ -267,7 +328,8 @@ struct _delim_dsl template constexpr auto delimited(Open, Close) { - static_assert(lexy::is_branch_rule && lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Open, "delimited()"); + LEXY_REQUIRE_BRANCH_RULE(Close, "delimited()"); return _delim_dsl{}; } @@ -275,7 +337,7 @@ constexpr auto delimited(Open, Close) template constexpr auto delimited(Delim) { - static_assert(lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Delim, "delimited()"); return _delim_dsl{}; } @@ -305,9 +367,16 @@ namespace lexyd template struct _escape : _escape_base { + static constexpr bool esc_is_literal = lexy::is_literal_rule; + template + static constexpr auto esc_first_char() -> typename Encoding::char_type + { + return Escape::template lit_first_char(); + } + template - static constexpr bool _try_parse(Context& context, Reader& reader, Sink& sink, - _del_chars& cur_chars) + static constexpr bool esc_try_parse(Context& context, Reader& reader, Sink& sink, + _del_chars& cur_chars) { auto begin = reader.position(); @@ -359,7 +428,7 @@ struct _escape : _escape_base template constexpr auto rule(Branch) const { - static_assert(lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Branch, "escape()"); return _escape{}; } @@ -367,7 +436,7 @@ struct _escape : _escape_base template constexpr auto capture(Branch branch) const { - static_assert(lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Branch, "escape()"); return this->rule(lexy::dsl::capture(branch)); } diff --git a/3rdparty/lexy/include/lexy/dsl/digit.hpp b/3rdparty/lexy/include/lexy/dsl/digit.hpp index 0909b0d32..a3f1dc61d 100644 --- a/3rdparty/lexy/include/lexy/dsl/digit.hpp +++ b/3rdparty/lexy/include/lexy/dsl/digit.hpp @@ -1,15 +1,18 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_DIGIT_HPP_INCLUDED #define LEXY_DSL_DIGIT_HPP_INCLUDED +#include #include #include #include #include //=== bases ===// +// SWAR matching code adapted from: +// https://lemire.me/blog/2018/09/30/quickly-identifying-a-sequence-of-digits-in-a-string-of-characters/ namespace lexyd { template @@ -37,6 +40,16 @@ struct _d<2> : char_class_base<_d<2>> { return static_cast(c) - '0'; } + + template + static constexpr bool swar_matches(lexy::_detail::swar_int c) + { + constexpr auto mask = lexy::_detail::swar_fill_compl(CharT(0xF)); + constexpr auto expected = lexy::_detail::swar_fill(CharT(0x30)); + constexpr auto offset = lexy::_detail::swar_fill(CharT(0x0E)); + + return (c & mask) == expected && ((c + offset) & mask) == expected; + } }; using binary = _d<2>; @@ -62,6 +75,16 @@ struct _d<8> : char_class_base<_d<8>> { return static_cast(c) - '0'; } + + template + static constexpr bool swar_matches(lexy::_detail::swar_int c) + { + constexpr auto mask = lexy::_detail::swar_fill_compl(CharT(0xF)); + constexpr auto expected = lexy::_detail::swar_fill(CharT(0x30)); + constexpr auto offset = lexy::_detail::swar_fill(CharT(0x08)); + + return (c & mask) == expected && ((c + offset) & mask) == expected; + } }; using octal = _d<8>; @@ -87,15 +110,24 @@ struct _d<10> : char_class_base<_d<10>> { return static_cast(c) - '0'; } + + template + static constexpr bool swar_matches(lexy::_detail::swar_int c) + { + constexpr auto mask = lexy::_detail::swar_fill_compl(CharT(0xF)); + constexpr auto expected = lexy::_detail::swar_fill(CharT(0x30)); + constexpr auto offset = lexy::_detail::swar_fill(CharT(0x06)); + + return (c & mask) == expected && ((c + offset) & mask) == expected; + } }; using decimal = _d<10>; -template <> -struct _d<16> : char_class_base<_d<16>> +struct hex_lower : char_class_base { static LEXY_CONSTEVAL auto char_class_name() { - return "digit.hex"; + return "digit.hex-lower"; } static LEXY_CONSTEVAL auto char_class_ascii() @@ -103,7 +135,6 @@ struct _d<16> : char_class_base<_d<16>> lexy::_detail::ascii_set result; result.insert('0', '9'); result.insert('a', 'f'); - result.insert('A', 'F'); return result; } @@ -114,28 +145,32 @@ struct _d<16> : char_class_base<_d<16>> { if (c >= 'a') return static_cast(c) - 'a' + 10; - else if (c >= 'A') - return static_cast(c) - 'A' + 10; else if (c <= '9') return static_cast(c) - '0'; else return unsigned(-1); } + + template + static constexpr bool swar_matches(lexy::_detail::swar_int c) + { + // False negative for hex digits, but that's okay. + return _d<10>::swar_matches(c); + } }; -using hex = _d<16>; -struct hex_lower : char_class_base +struct hex_upper : char_class_base { static LEXY_CONSTEVAL auto char_class_name() { - return "digit.hex-lower"; + return "digit.hex-upper"; } static LEXY_CONSTEVAL auto char_class_ascii() { lexy::_detail::ascii_set result; result.insert('0', '9'); - result.insert('a', 'f'); + result.insert('A', 'F'); return result; } @@ -144,26 +179,35 @@ struct hex_lower : char_class_base template static constexpr unsigned digit_value(CharT c) { - if (c >= 'a') - return static_cast(c) - 'a' + 10; + if (c >= 'A') + return static_cast(c) - 'A' + 10; else if (c <= '9') return static_cast(c) - '0'; else return unsigned(-1); } + + template + static constexpr bool swar_matches(lexy::_detail::swar_int c) + { + // False negative for hex digits, but that's okay. + return _d<10>::swar_matches(c); + } }; -struct hex_upper : char_class_base +template <> +struct _d<16> : char_class_base<_d<16>> { static LEXY_CONSTEVAL auto char_class_name() { - return "digit.hex-upper"; + return "digit.hex"; } static LEXY_CONSTEVAL auto char_class_ascii() { lexy::_detail::ascii_set result; result.insert('0', '9'); + result.insert('a', 'f'); result.insert('A', 'F'); return result; } @@ -173,14 +217,24 @@ struct hex_upper : char_class_base template static constexpr unsigned digit_value(CharT c) { - if (c >= 'A') + if (c >= 'a') + return static_cast(c) - 'a' + 10; + else if (c >= 'A') return static_cast(c) - 'A' + 10; else if (c <= '9') return static_cast(c) - '0'; else return unsigned(-1); } + + template + static constexpr bool swar_matches(lexy::_detail::swar_int c) + { + // False negative for hex digits, but that's okay. + return _d<10>::swar_matches(c); + } }; +using hex = _d<16>; } // namespace lexyd //=== digit ===// @@ -236,67 +290,98 @@ struct forbidden_leading_zero namespace lexyd { +template +constexpr bool _match_digits(Reader& reader) +{ + // Need at least one digit. + // Checking for a single digit is also cheaper than doing a SWAR comparison, + // so we do that manually in either case. + if (!lexy::try_match_token(digit, reader)) + return false; + + // Now we consume as many digits as possible. + // First using SWAR... + if constexpr (lexy::_detail::is_swar_reader) + { + using char_type = typename Reader::encoding::char_type; + while (Base::template swar_matches(reader.peek_swar())) + reader.bump_swar(); + } + + // ... then manually to get any trailing digits. + while (lexy::try_match_token(digit, reader)) + { + } + + return true; +} +template +constexpr bool _match_digits_sep(Reader& reader) +{ + // Need at least one digit. + if (!lexy::try_match_token(digit, reader)) + return false; + + // Might have following digits. + while (true) + { + if (lexy::try_match_token(Sep{}, reader)) + { + // Need a digit after a separator. + if (!lexy::try_match_token(digit, reader)) + return false; + } + else + { + // Attempt to consume as many digits as possible. + if constexpr (lexy::_detail::is_swar_reader) + { + using char_type = typename Reader::encoding::char_type; + while (Base::template swar_matches(reader.peek_swar())) + reader.bump_swar(); + } + + if (!lexy::try_match_token(digit, reader)) + // If we're not having a digit, we're done. + break; + } + } + + return true; +} + template struct _digits_st : token_base<_digits_st> { template struct tp { - typename Reader::iterator end; - bool forbidden_leading_zero; + typename Reader::marker end; + bool forbidden_leading_zero; constexpr explicit tp(const Reader& reader) - : end(reader.position()), forbidden_leading_zero(false) + : end(reader.current()), forbidden_leading_zero(false) {} constexpr bool try_parse(Reader reader) { - // Check for a zero that is followed by a digit or separator. - if (reader.peek() == lexy::_detail::transcode_int('0')) + using char_type = typename Reader::encoding::char_type; + auto begin = reader.current(); + auto result = _match_digits_sep(reader); + end = reader.current(); + + if (result && lexy::_detail::next(begin.position()) != end.position() + && *begin.position() == lexy::_detail::transcode_char('0')) { + reader.reset(begin); reader.bump(); - end = reader.position(); + end = reader.current(); - if (lexy::try_match_token(digit, reader) - || lexy::try_match_token(Sep{}, reader)) - { - forbidden_leading_zero = true; - return false; - } - - // Just zero. - return true; - } - // Need at least one digit. - else if (!lexy::try_match_token(digit, reader)) - { - end = reader.position(); - forbidden_leading_zero = false; + forbidden_leading_zero = true; return false; } - // Might have following digits. - while (true) - { - if (lexy::try_match_token(Sep{}, reader)) - { - // Need a digit after a separator. - if (!lexy::try_match_token(digit, reader)) - { - end = reader.position(); - forbidden_leading_zero = false; - return false; - } - } - else if (!lexy::try_match_token(digit, reader)) - { - // If we're not having a digit, we're done. - break; - } - } - - end = reader.position(); - return true; + return result; } template @@ -304,14 +389,14 @@ struct _digits_st : token_base<_digits_st> { if (forbidden_leading_zero) { - auto err - = lexy::error(reader.position(), end); + auto err = lexy::error(reader.position(), + end.position()); context.on(_ev::error{}, err); } else { - auto err - = lexy::error(end, Base::char_class_name()); + auto err = lexy::error(end.position(), + Base::char_class_name()); context.on(_ev::error{}, err); } } @@ -324,46 +409,22 @@ struct _digits_s : token_base<_digits_s> template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { - // Need at least one digit. - if (!lexy::try_match_token(digit, reader)) - { - end = reader.position(); - return false; - } - - // Might have following digits. - while (true) - { - if (lexy::try_match_token(Sep{}, reader)) - { - // Need a digit after a separator. - if (!lexy::try_match_token(digit, reader)) - { - end = reader.position(); - return false; - } - } - else if (!lexy::try_match_token(digit, reader)) - { - // If we're not having a digit, we're done. - break; - } - } - - end = reader.position(); - return true; + auto result = _match_digits_sep(reader); + end = reader.current(); + return result; } template constexpr void report_error(Context& context, const Reader&) { - auto err = lexy::error(end, Base::char_class_name()); + auto err = lexy::error(end.position(), + Base::char_class_name()); context.on(_ev::error{}, err); } }; @@ -380,44 +441,32 @@ struct _digits_t : token_base<_digits_t> template struct tp { - typename Reader::iterator end; - bool forbidden_leading_zero; + typename Reader::marker end; + bool forbidden_leading_zero; constexpr explicit tp(const Reader& reader) - : end(reader.position()), forbidden_leading_zero(false) + : end(reader.current()), forbidden_leading_zero(false) {} constexpr bool try_parse(Reader reader) { - // Check for a zero that is followed by a digit. - if (reader.peek() == lexy::_detail::transcode_int('0')) + using char_type = typename Reader::encoding::char_type; + auto begin = reader.current(); + auto result = _match_digits(reader); + end = reader.current(); + + if (result && lexy::_detail::next(begin.position()) != end.position() + && *begin.position() == lexy::_detail::transcode_char('0')) { + reader.reset(begin); reader.bump(); - end = reader.position(); - - if (lexy::try_match_token(digit, reader)) - { - forbidden_leading_zero = true; - return false; - } - - // Just zero. - return true; - } + end = reader.current(); - // Need at least one digit. - if (!lexy::try_match_token(digit, reader)) - { - forbidden_leading_zero = false; + forbidden_leading_zero = true; return false; } - // Might have more than one digit afterwards. - while (lexy::try_match_token(digit, reader)) - {} - - end = reader.position(); - return true; + return result; } template @@ -426,7 +475,7 @@ struct _digits_t : token_base<_digits_t> if (forbidden_leading_zero) { auto err = lexy::error(reader.position(), - this->end); + end.position()); context.on(_ev::error{}, err); } else @@ -452,22 +501,15 @@ struct _digits : token_base<_digits> template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { - // Need at least one digit. - if (!lexy::try_match_token(digit, reader)) - return false; - - // Might have more than one digit afterwards. - while (lexy::try_match_token(digit, reader)) - {} - - end = reader.position(); - return true; + auto result = _match_digits(reader); + end = reader.current(); + return result; } template @@ -523,16 +565,16 @@ struct _ndigits_s : token_base<_ndigits_s> template struct tp> { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { // Match the Base one time. if (!lexy::try_match_token(digit, reader)) { - end = reader.position(); + end = reader.current(); return false; } @@ -540,14 +582,15 @@ struct _ndigits_s : token_base<_ndigits_s> auto success = (((void)Idx, lexy::try_match_token(Sep{}, reader), lexy::try_match_token(digit, reader)) && ...); - end = reader.position(); + end = reader.current(); return success; } template constexpr void report_error(Context& context, const Reader&) { - auto err = lexy::error(end, Base::char_class_name()); + auto err = lexy::error(end.position(), + Base::char_class_name()); context.on(_ev::error{}, err); } }; @@ -563,22 +606,23 @@ struct _ndigits : token_base<_ndigits> template struct tp> { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { // Match the Base N times. auto success = (((void)Idx, lexy::try_match_token(digit, reader)) && ...); - end = reader.position(); + end = reader.current(); return success; } template constexpr void report_error(Context& context, const Reader&) { - auto err = lexy::error(end, Base::char_class_name()); + auto err = lexy::error(end.position(), + Base::char_class_name()); context.on(_ev::error{}, err); } }; diff --git a/3rdparty/lexy/include/lexy/dsl/eof.hpp b/3rdparty/lexy/include/lexy/dsl/eof.hpp index 594e23497..beb96cb97 100644 --- a/3rdparty/lexy/include/lexy/dsl/eof.hpp +++ b/3rdparty/lexy/include/lexy/dsl/eof.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_EOF_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/error.hpp b/3rdparty/lexy/include/lexy/dsl/error.hpp index cdbc0b166..7be4fac65 100644 --- a/3rdparty/lexy/include/lexy/dsl/error.hpp +++ b/3rdparty/lexy/include/lexy/dsl/error.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_ERROR_HPP_INCLUDED @@ -10,6 +10,14 @@ namespace lexyd { +template +struct _err_production +{ + static constexpr auto name = ""; + static constexpr auto max_recursion_depth = 0; + static constexpr auto rule = Rule{}; +}; + template struct _err : unconditional_branch_base { @@ -23,9 +31,17 @@ struct _err : unconditional_branch_base auto end = reader.position(); if constexpr (!std::is_same_v) { - lexy::token_parser_for parser(reader); - parser.try_parse(reader); - end = parser.end; + auto backtrack = reader.current(); + + // We match a dummy production that only consists of the rule. + lexy::do_action< + _err_production, + lexy::match_action::template result_type>(lexy::_mh(), + context.control_block + ->parse_state, + reader); + end = reader.position(); + reader.reset(LEXY_MOV(backtrack)); } auto err = lexy::error(begin, end); @@ -102,7 +118,7 @@ struct _must_dsl template constexpr auto must(Branch) { - static_assert(lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Branch, "must()"); static_assert(!lexy::is_unconditional_branch_rule); return _must_dsl{}; } diff --git a/3rdparty/lexy/include/lexy/dsl/expression.hpp b/3rdparty/lexy/include/lexy/dsl/expression.hpp index bdbdc8b47..a7d4f94fa 100644 --- a/3rdparty/lexy/include/lexy/dsl/expression.hpp +++ b/3rdparty/lexy/include/lexy/dsl/expression.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_EXPRESSION_HPP_INCLUDED @@ -197,7 +197,7 @@ struct operation_list (void)((cur_idx <= op.idx && op.idx < cur_idx + op_of::op_literals::size ? (result = Continuation::parse(context, reader, - parsed_operator{op.pos, + parsed_operator{op.cur, op.idx - cur_idx}, LEXY_FWD(args)...), true) @@ -226,19 +226,21 @@ struct _operation_list_of { constexpr auto bp = get_binding_power(0, CurLevel); - auto tail = get(typename Operation::operand{}); - if constexpr (std::is_base_of_v == Pre - && (bp.is_prefix() || bp.lhs >= MinBindingPower)) + auto tail = get(typename Operation::operand{}); + constexpr auto is_prefix = std::is_base_of_v; + if constexpr (is_prefix == Pre + && ((is_prefix && bp.rhs >= MinBindingPower) + || (!is_prefix && bp.lhs >= MinBindingPower))) return tail + Operation{}; else return tail; } }; -// prefix operations: don't care about binding power -template -using pre_operation_list_of - = decltype(_operation_list_of::template get<1>(typename Expr::operation{})); +// prefix operations +template +using pre_operation_list_of = decltype(_operation_list_of::template get<1>( + typename Expr::operation{})); // infix and postfix operations template @@ -249,6 +251,7 @@ using post_operation_list_of = decltype(_operation_list_of struct _expr : rule_base { struct _state @@ -306,7 +309,7 @@ struct _expr : rule_base if (op.idx >= op_rule::op_literals::size) { // The list ends at this point. - reader.set_position(op.pos); + reader.reset(op.cur); break; } @@ -378,10 +381,11 @@ struct _expr : rule_base if (op.idx < op_rule::op_literals::size) { using tag = typename Context::production::operator_chain_error; - auto err = lexy::error(op.pos, reader.position()); + auto err + = lexy::error(op.cur.position(), reader.position()); context.on(_ev::error{}, err); } - reader.set_position(op.pos); + reader.reset(op.cur); } } else if constexpr (binding_power.is_postfix()) @@ -413,11 +417,11 @@ struct _expr : rule_base if (state.cur_nesting_level++ >= production::max_operator_nesting) { using tag = typename production::operator_nesting_error; - auto err = lexy::error(op.pos, reader.position()); + auto err = lexy::error(op.cur.position(), reader.position()); context.on(_ev::error{}, err); // We do not recover, to prevent stack overflow. - reader.set_position(op.pos); + reader.reset(op.cur); return false; } @@ -434,7 +438,7 @@ struct _expr : rule_base { // Operators can't be grouped. using tag = typename production::operator_group_error; - auto err = lexy::error(op.pos, reader.position()); + auto err = lexy::error(op.cur.position(), reader.position()); context.on(_ev::error{}, err); // Trivially recover, but don't update group: // let the first one stick. @@ -446,12 +450,12 @@ struct _expr : rule_base } }; - template + template static constexpr bool _parse_lhs(Context& context, Reader& reader, _state& state) { using namespace lexy::_detail; - using op_list = pre_operation_list_of; + using op_list = pre_operation_list_of; using atom_parser = lexy::parser_for; @@ -467,11 +471,11 @@ struct _expr : rule_base if (op.idx >= op_list::ops::size) { // We don't have a prefix operator, so it must be an atom. - reader.set_position(op.pos); + reader.reset(op.cur); return atom_parser::parse(context, reader); } - auto start_event = context.on(_ev::operation_chain_start{}, op.pos); + auto start_event = context.on(_ev::operation_chain_start{}, op.cur.position()); auto result = op_list::template apply<_continuation>(context, reader, op, state); context.on(_ev::operation_chain_finish{}, LEXY_MOV(start_event), reader.position()); return result; @@ -487,12 +491,12 @@ struct _expr : rule_base if constexpr (op_list::size == 0) { // We don't have any post operators, so we only parse the left-hand-side. - return _parse_lhs(context, reader, state); + return _parse_lhs(context, reader, state); } else { auto start_event = context.on(_ev::operation_chain_start{}, reader.position()); - if (!_parse_lhs(context, reader, state)) + if (!_parse_lhs(context, reader, state)) { context.on(_ev::operation_chain_finish{}, LEXY_MOV(start_event), reader.position()); return false; @@ -504,7 +508,7 @@ struct _expr : rule_base auto op = parse_operator(reader); if (op.idx >= op_list::ops::size) { - reader.set_position(op.pos); + reader.reset(op.cur); break; } @@ -528,8 +532,19 @@ struct _expr : rule_base { static_assert(std::is_same_v); + using production = typename Context::production; + constexpr auto binding_power = lexy::_detail::binding_power_of( + lexy::_detail::type_or{}); + // The MinBindingPower is determined by the root operation. + // The initial operand is always on the left, so we use the left binding power. + // However, for a prefix operator it is zero, but then it's a right operand so we use + // that. + constexpr auto min_binding_power + = binding_power.is_prefix() ? binding_power.rhs : binding_power.lhs; + _state state; - _parse<0>(context, reader, state); + _parse(context, reader, state); + // Regardless of parse errors, we can recover if we already had a value at some point. return !!context.value; } @@ -572,7 +587,13 @@ struct expression_production using operator_chain_error = lexy::operator_chain_error; using operator_group_error = lexy::operator_group_error; - static constexpr auto rule = lexyd::_expr{}; + static constexpr auto rule = lexyd::_expr{}; +}; + +template +struct subexpression_production : Expr +{ + static constexpr auto rule = lexyd::_expr{}; }; } // namespace lexy diff --git a/3rdparty/lexy/include/lexy/dsl/flags.hpp b/3rdparty/lexy/include/lexy/dsl/flags.hpp index fe8d9e63e..51cd1f948 100644 --- a/3rdparty/lexy/include/lexy/dsl/flags.hpp +++ b/3rdparty/lexy/include/lexy/dsl/flags.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_FLAGS_HPP_INCLUDED @@ -126,9 +126,15 @@ struct _flag : rule_base template constexpr auto flag(Rule) { - static_assert(lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Rule, "flag()"); return _flag{}; } + +template +constexpr auto flag(Rule rule) +{ + return flag(rule); +} } // namespace lexyd #endif // LEXY_DSL_FLAGS_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/follow.hpp b/3rdparty/lexy/include/lexy/dsl/follow.hpp index 90586fd07..9d47e09dd 100644 --- a/3rdparty/lexy/include/lexy/dsl/follow.hpp +++ b/3rdparty/lexy/include/lexy/dsl/follow.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_FOLLOW_HPP_INCLUDED @@ -30,6 +30,12 @@ struct _nf : token_base<_nf>, _lit_base using lit_case_folding = typename Literal::lit_case_folding; + template + static constexpr auto lit_first_char() -> typename Encoding::char_type + { + return Literal::template lit_first_char(); + } + template static LEXY_CONSTEVAL std::size_t lit_insert(Trie& trie, std::size_t pos, std::size_t char_class) @@ -43,11 +49,11 @@ struct _nf : token_base<_nf>, _lit_base struct tp { lexy::token_parser_for impl; - typename Reader::iterator end; + typename Reader::marker end; bool literal_success; constexpr explicit tp(const Reader& reader) - : impl(reader), end(reader.position()), literal_success(false) + : impl(reader), end(reader.current()), literal_success(false) {} constexpr bool try_parse(Reader reader) @@ -61,7 +67,7 @@ struct _nf : token_base<_nf>, _lit_base literal_success = true; // To match, we must not match the char class now. - reader.set_position(end); + reader.reset(end); if constexpr (std::is_void_v) { return !lexy::try_match_token(CharClass{}, reader); @@ -82,7 +88,8 @@ struct _nf : token_base<_nf>, _lit_base } else { - auto err = lexy::error(end, end); + auto err + = lexy::error(end.position(), end.position()); context.on(_ev::error{}, err); } } diff --git a/3rdparty/lexy/include/lexy/dsl/identifier.hpp b/3rdparty/lexy/include/lexy/dsl/identifier.hpp index 604c2083b..1b780f00f 100644 --- a/3rdparty/lexy/include/lexy/dsl/identifier.hpp +++ b/3rdparty/lexy/include/lexy/dsl/identifier.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_IDENTIFIER_HPP_INCLUDED @@ -36,21 +36,34 @@ struct _idp : token_base<_idp> template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { + static_assert(lexy::is_char_encoding); + // Need to match Leading character. if (!lexy::try_match_token(Leading{}, reader)) return false; // Match zero or more trailing characters. - while (lexy::try_match_token(Trailing{}, reader)) - {} + while (true) + { + if constexpr (lexy::_detail::is_swar_reader) + { + // If we have a swar reader, consume as much as possible at once. + while (Trailing{}.template char_class_match_swar( + reader.peek_swar())) + reader.bump_swar(); + } + + if (!lexy::try_match_token(Trailing{}, reader)) + break; + } - end = reader.position(); + end = reader.current(); return true; } @@ -159,7 +172,7 @@ struct _id : branch_base template struct bp { - typename Reader::iterator end; + typename Reader::marker end; constexpr bool try_parse(const void*, const Reader& reader) { @@ -170,7 +183,8 @@ struct _id : branch_base end = parser.end; // We only succeed if it's not a reserved identifier. - [[maybe_unused]] auto input = lexy::partial_input(reader, reader.position(), end); + [[maybe_unused]] auto input + = lexy::partial_input(reader, reader.position(), end.position()); return !(ReservedPredicate::is_reserved(input) || ...); } @@ -183,12 +197,12 @@ struct _id : branch_base { auto begin = reader.position(); - context.on(_ev::token{}, lexy::identifier_token_kind, begin, end); - reader.set_position(end); + context.on(_ev::token{}, lexy::identifier_token_kind, begin, end.position()); + reader.reset(end); using continuation = lexy::whitespace_parser; return continuation::parse(context, reader, LEXY_FWD(args)..., - lexy::lexeme(begin, end)); + lexy::lexeme(begin, end.position())); } }; @@ -310,6 +324,14 @@ struct _kw : token_base<_kw>, _lit_base using lit_case_folding = void; + template + static constexpr auto lit_first_char() -> typename Encoding::char_type + { + typename Encoding::char_type result = 0; + (void)((result = lexy::_detail::transcode_char(C), true) || ...); + return result; + } + template static LEXY_CONSTEVAL std::size_t lit_insert(Trie& trie, std::size_t pos, std::size_t char_class) @@ -322,16 +344,16 @@ struct _kw : token_base<_kw>, _lit_base template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { // Need to match the literal. - if (!lexy::try_match_token(_lit{}, reader)) + if (!lexy::_detail::match_literal<0, CharT, C...>(reader)) return false; - end = reader.position(); + end = reader.current(); // To qualify as a keyword, and not just the prefix of an identifier, // we must not have a trailing identifier character. diff --git a/3rdparty/lexy/include/lexy/dsl/if.hpp b/3rdparty/lexy/include/lexy/dsl/if.hpp index 1caba5d18..ff4f1d4a0 100644 --- a/3rdparty/lexy/include/lexy/dsl/if.hpp +++ b/3rdparty/lexy/include/lexy/dsl/if.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_IF_HPP_INCLUDED @@ -36,7 +36,7 @@ struct _if : rule_base template constexpr auto if_(Branch) { - static_assert(lexy::is_branch_rule, "if_() requires a branch condition"); + LEXY_REQUIRE_BRANCH_RULE(Branch, "if()"); if constexpr (lexy::is_unconditional_branch_rule) // Branch is always taken, so don't wrap in if_(). return Branch{}; diff --git a/3rdparty/lexy/include/lexy/dsl/integer.hpp b/3rdparty/lexy/include/lexy/dsl/integer.hpp index 88cde44f7..95475fa0c 100644 --- a/3rdparty/lexy/include/lexy/dsl/integer.hpp +++ b/3rdparty/lexy/include/lexy/dsl/integer.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_INTEGER_HPP_INCLUDED @@ -234,19 +234,26 @@ constexpr bool _ndigits_can_overflow() return N >= max_digit_count; } -// Parses T in the Base while checking for overflow. +// Parses T in the Base without checking for overflow. template struct _unbounded_integer_parser { - using traits = lexy::integer_traits; - using result_type = typename traits::type; - using base = Base; + using traits = lexy::integer_traits; + using base = Base; static constexpr auto radix = Base::digit_radix; + struct result_type + { + typename traits::type value; + std::false_type overflow; + }; + template - static constexpr bool parse(result_type& result, Iterator cur, Iterator end) + static constexpr result_type parse(Iterator cur, Iterator end) { + typename traits::type value(0); + // Just parse digits until we've run out of digits. while (cur != end) { @@ -255,94 +262,83 @@ struct _unbounded_integer_parser // Skip digit separator. continue; - traits::template add_digit_unchecked(result, digit); + traits::template add_digit_unchecked(value, digit); } - return true; + return {value, {}}; } }; -// Parses T in the Base without checking for overflow. +// Parses T in the Base while checking for overflow. template struct _bounded_integer_parser { - using traits = lexy::integer_traits; - using result_type = typename traits::type; - using base = Base; + using traits = lexy::integer_traits; + using base = Base; - static constexpr auto radix = Base::digit_radix; + static constexpr auto radix = Base::digit_radix; + static constexpr auto max_digit_count = traits::template max_digit_count; + static_assert(max_digit_count > 1, "integer must be able to store all possible digit values"); - template - static constexpr unsigned find_digit(Iterator& cur, Iterator end) + struct result_type { - if constexpr (AssumeOnlyDigits) - { - if (cur == end) - return unsigned(-1); - else - return Base::digit_value(*cur++); - } - else - { - auto digit = 0u; - do - { - if (cur == end) - return unsigned(-1); - - digit = Base::digit_value(*cur++); - } while (digit >= Base::digit_radix); - return digit; - } - } + typename traits::type value; + bool overflow; + }; template - static constexpr bool parse(result_type& result, Iterator cur, Iterator end) + static constexpr result_type parse(Iterator cur, Iterator end) { - constexpr auto max_digit_count = traits::template max_digit_count; - static_assert(max_digit_count > 1, - "integer must be able to store all possible digit values"); - - // Skip leading zeroes. + // Find the first non-zero digit. + // Note that we always need a loop, even if leading zeros are not allowed: + // error recovery might get them anyway. + auto first_digit = 0u; while (true) { if (cur == end) - return true; // We only had zeroes. - - const auto digit = Base::digit_value(*cur++); - if (digit == 0 || digit >= radix) - continue; // Zero or digit separator. + return {typename traits::type(0), false}; - // First non-zero digit, so we can assign it instead of adding. - result = result_type(digit); - break; + first_digit = Base::digit_value(*cur++); + if (first_digit != 0 && first_digit < radix) + break; } - // At this point, we've parsed exactly one non-zero digit. - - // Handle max_digit_count - 1 digits without checking for overflow. - // We cannot overflow, as the maximal value has one digit more. - for (std::size_t digit_count = 1; digit_count < max_digit_count - 1; ++digit_count) - { - auto digit = find_digit(cur, end); - if (digit == unsigned(-1)) - return true; - traits::template add_digit_unchecked(result, digit); - } + // At this point, we've parsed exactly one non-zero digit, so we can assign. + auto value = typename traits::type(first_digit); - // Handle the final digit, if there is any, while checking for overflow. + // Handle at most the number of remaining digits. + // Due to the fixed loop count, it is most likely unrolled. + for (std::size_t digit_count = 1; digit_count < max_digit_count; ++digit_count) { - auto digit = find_digit(cur, end); - if (digit == unsigned(-1)) - return true; + // Find the next digit. + auto digit = 0u; + do + { + if (cur == end) + return {value, false}; - if (!traits::template add_digit_checked(result, digit)) - return false; + digit = Base::digit_value(*cur++); + // If we can assume it's a digit, we don't need the comparison. + } while (AssumeOnlyDigits ? false : digit >= Base::digit_radix); + + // We need to handle the last loop iteration special. + // (The compiler will not generate a branch here.) + if (digit_count == max_digit_count - 1) + { + // The last digit might overflow, so check for it. + if (!traits::template add_digit_checked(value, digit)) + return {value, true}; + } + else + { + // Add the digit without checking as it can't overflow. + traits::template add_digit_unchecked(value, digit); + } } // If we've reached this point, we've parsed the maximal number of digits allowed. - // Now we can only fail if there are still digits left. - return cur == end; + // Now we can only overflow if there are still digits left. + return {value, cur != end}; } }; template @@ -360,7 +356,7 @@ struct _integer_parser_digits> template struct _integer_parser_digits> { - using type = _integer_parser; + using type = _integer_parser; }; template struct _integer_parser_digits> @@ -401,8 +397,8 @@ struct _int : _copy_base typename Reader::iterator begin, typename Reader::iterator end, Args&&... args) { - auto result = typename IntParser::result_type(0); - if (!IntParser::parse(result, begin, end)) + auto [value, overflow] = IntParser::parse(begin, end); + if (overflow) { // Raise error but recover. using tag = lexy::_detail::type_or; @@ -411,14 +407,14 @@ struct _int : _copy_base // Need to skip whitespace now as well. return lexy::whitespace_parser::parse(context, reader, - LEXY_FWD(args)..., result); + LEXY_FWD(args)..., value); } }; template struct bp { - typename Reader::iterator end; + typename Reader::marker end; constexpr auto try_parse(const void*, const Reader& reader) { @@ -436,10 +432,11 @@ struct _int : _copy_base LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { auto begin = reader.position(); - context.on(_ev::token{}, Token{}, begin, end); - reader.set_position(end); + context.on(_ev::token{}, Token{}, begin, end.position()); + reader.reset(end); - return _pc::parse(context, reader, begin, end, LEXY_FWD(args)...); + return _pc::parse(context, reader, begin, end.position(), + LEXY_FWD(args)...); } }; @@ -452,17 +449,19 @@ struct _int : _copy_base auto begin = reader.position(); if (lexy::token_parser_for parser(reader); parser.try_parse(reader)) { - context.on(_ev::token{}, typename Token::token_type{}, begin, parser.end); - reader.set_position(parser.end); + context.on(_ev::token{}, typename Token::token_type{}, begin, + parser.end.position()); + reader.reset(parser.end); } else { parser.report_error(context, reader); - reader.set_position(parser.end); + reader.reset(parser.end); // To recover we try and skip additional digits. while (lexy::try_match_token(digit, reader)) - {} + { + } auto recovery_end = reader.position(); if (begin == recovery_end) @@ -489,7 +488,7 @@ struct _int : _copy_base template struct _int_dsl : _int<_digits>, - _integer_parser, true>, void> + _integer_parser_for>>, void> { template constexpr auto operator()(Digits) const diff --git a/3rdparty/lexy/include/lexy/dsl/list.hpp b/3rdparty/lexy/include/lexy/dsl/list.hpp index 5b9f0c4ce..25b85076d 100644 --- a/3rdparty/lexy/include/lexy/dsl/list.hpp +++ b/3rdparty/lexy/include/lexy/dsl/list.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_LIST_HPP_INCLUDED @@ -15,7 +15,7 @@ template struct _lst : _copy_base { template - static constexpr bool _loop(Context& context, Reader& reader, Sink& sink) + LEXY_PARSER_FUNC static bool _loop(Context& context, Reader& reader, Sink& sink) { while (true) { @@ -132,8 +132,7 @@ struct _lst : _copy_base template constexpr auto list(Item) { - static_assert(lexy::is_branch_rule, - "list() without a separator requires a branch condition"); + LEXY_REQUIRE_BRANCH_RULE(Item, "list() without a separator"); return _lst{}; } @@ -148,8 +147,7 @@ constexpr auto list(Item, _sep) template constexpr auto list(Item, _tsep) { - static_assert(lexy::is_branch_rule, - "list() without a trailing separator requires a branch condition"); + LEXY_REQUIRE_BRANCH_RULE(Item, "list() with a trailing separator"); return _lst>{}; } @@ -185,8 +183,8 @@ struct _lstt : rule_base }; template - static constexpr bool _loop(_state initial_state, TermParser& term, Context& context, - Reader& reader, Sink& sink) + LEXY_PARSER_FUNC static bool _loop(_state initial_state, TermParser& term, Context& context, + Reader& reader, Sink& sink) { auto state = initial_state; diff --git a/3rdparty/lexy/include/lexy/dsl/literal.hpp b/3rdparty/lexy/include/lexy/dsl/literal.hpp index 4e332999a..adc2cf599 100644 --- a/3rdparty/lexy/include/lexy/dsl/literal.hpp +++ b/3rdparty/lexy/include/lexy/dsl/literal.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_LITERAL_HPP_INCLUDED @@ -8,9 +8,58 @@ #include #include #include +#include #include #include +//=== lit_matcher ===// +namespace lexy::_detail +{ +template +constexpr auto match_literal(Reader& reader) +{ + static_assert(lexy::is_char_encoding); + using char_type = typename Reader::encoding::char_type; + if constexpr (CurCharIndex >= sizeof...(Cs)) + { + (void)reader; + return std::true_type{}; + } + // We only use SWAR if the reader supports it and we have enough to fill at least one. + else if constexpr (is_swar_reader && sizeof...(Cs) >= swar_length) + { + // Try and pack as many characters into a swar as possible, starting at the current + // index. + constexpr auto pack = swar_pack(transcode_char(Cs)...); + + // Do a single swar comparison. + if ((reader.peek_swar() & pack.mask) == pack.value) + { + reader.bump_swar(pack.count); + + // Recurse with the incremented index. + return bool(match_literal(reader)); + } + else + { + auto partial = swar_find_difference(reader.peek_swar() & pack.mask, pack.value); + reader.bump_swar(partial); + return false; + } + } + else + { + static_assert(CurCharIndex == 0); + + // Compare each code unit, bump on success, cancel on failure. + return ((reader.peek() == transcode_int(Cs) + ? (reader.bump(), true) + : false) + && ...); + } +} +} // namespace lexy::_detail + //=== lit_trie ===// namespace lexy::_detail { @@ -136,11 +185,11 @@ struct _merge_case_folding typename H::lit_case_folding, CurrentCaseFolding>, T...> { - static_assert( - std::is_same_v // - || std::is_void_v || std::is_void_v, - "cannot mix literals with different case foldings in a literal_set"); + static_assert(std::is_same_v // + || std::is_void_v + || std::is_void_v, + "cannot mix literals with different case foldings in a literal_set"); }; template @@ -224,7 +273,7 @@ struct lit_trie_matcher if constexpr (sizeof...(Idx) > 0) { - auto cur_pos = reader.position(); + auto cur = reader.current(); auto cur_char = reader.peek(); auto next_value = Trie.node_no_match; @@ -235,7 +284,7 @@ struct lit_trie_matcher return next_value; // We haven't found a longer match, return our match. - reader.set_position(cur_pos); + reader.reset(cur); } // But first, we might need to check that we don't match that nodes char class. @@ -257,6 +306,7 @@ struct lit_trie_matcher template LEXY_FORCE_INLINE static constexpr std::size_t try_match(Reader& _reader) { + static_assert(lexy::is_char_encoding); if constexpr (std::is_same_v, Reader>) { return _impl<>::try_match(_reader); @@ -265,7 +315,7 @@ struct lit_trie_matcher { CaseFolding reader{_reader}; auto result = _impl<>::try_match(reader); - _reader.set_position(reader.position()); + _reader.reset(reader.current()); return result; } } @@ -285,6 +335,14 @@ struct _lit static constexpr auto lit_char_classes = lexy::_detail::char_class_list{}; using lit_case_folding = void; + template + static constexpr auto lit_first_char() -> typename Encoding::char_type + { + typename Encoding::char_type result = 0; + (void)((result = lexy::_detail::transcode_char(C), true) || ...); + return result; + } + template static LEXY_CONSTEVAL std::size_t lit_insert(Trie& trie, std::size_t pos, std::size_t) { @@ -294,28 +352,15 @@ struct _lit template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr auto try_parse(Reader reader) { - if constexpr (sizeof...(C) == 0) - { - end = reader.position(); - return std::true_type{}; - } - else - { - auto result - // Compare each code unit, bump on success, cancel on failure. - = ((reader.peek() == lexy::_detail::transcode_int(C) - ? (reader.bump(), true) - : false) - && ...); - end = reader.position(); - return result; - } + auto result = lexy::_detail::match_literal<0, CharT, C...>(reader); + end = reader.current(); + return result; } template @@ -325,7 +370,7 @@ struct _lit constexpr auto str = lexy::_detail::type_string::template c_str; auto begin = reader.position(); - auto index = lexy::_detail::range_size(begin, this->end); + auto index = lexy::_detail::range_size(begin, end.position()); auto err = lexy::error(begin, str, index, sizeof...(C)); context.on(_ev::error{}, err); } @@ -378,6 +423,12 @@ struct _lcp : token_base<_lcp>, _lit_base static constexpr auto lit_char_classes = lexy::_detail::char_class_list{}; using lit_case_folding = void; + template + static constexpr auto lit_first_char() -> typename Encoding::char_type + { + return _string.data[0]; + } + template static LEXY_CONSTEVAL std::size_t lit_insert(Trie& trie, std::size_t pos, std::size_t) { @@ -396,21 +447,17 @@ struct _lcp : token_base<_lcp>, _lit_base template struct tp> { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { using encoding = typename Reader::encoding; - auto result - // Compare each code unit, bump on success, cancel on failure. - = ((reader.peek() == encoding::to_int_type(_string.data[Idx]) - ? (reader.bump(), true) - : false) - && ...); - end = reader.position(); + auto result = lexy::_detail::match_literal<0, typename encoding::char_type, + _string.data[Idx]...>(reader); + end = reader.current(); return result; } @@ -420,7 +467,7 @@ struct _lcp : token_base<_lcp>, _lit_base using encoding = typename Reader::encoding; auto begin = reader.position(); - auto index = lexy::_detail::range_size(begin, end); + auto index = lexy::_detail::range_size(begin, end.position()); auto err = lexy::error(begin, _string.data, index, _string.length); context.on(_ev::error{}, err); @@ -492,9 +539,9 @@ struct _lset : token_base<_lset>, _lset_base template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { @@ -502,7 +549,7 @@ struct _lset : token_base<_lset>, _lset_base using matcher = lexy::_detail::lit_trie_matcher<_t, 0>; auto result = matcher::try_match(reader); - end = reader.position(); + end = reader.current(); return result != _t.node_no_match; } diff --git a/3rdparty/lexy/include/lexy/dsl/lookahead.hpp b/3rdparty/lexy/include/lexy/dsl/lookahead.hpp index b7245d7b8..3ee995a18 100644 --- a/3rdparty/lexy/include/lexy/dsl/lookahead.hpp +++ b/3rdparty/lexy/include/lexy/dsl/lookahead.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_LOOKAHEAD_HPP_INCLUDED @@ -50,6 +50,8 @@ struct _look : branch_base template struct bp { + static_assert(lexy::is_char_encoding); + typename Reader::iterator begin; typename Reader::iterator end; @@ -102,6 +104,7 @@ struct _look : branch_base template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { + static_assert(lexy::is_char_encoding); bp impl{}; if (!impl.try_parse(context.control_block, reader)) { diff --git a/3rdparty/lexy/include/lexy/dsl/loop.hpp b/3rdparty/lexy/include/lexy/dsl/loop.hpp index 55fd8fda2..e7988d270 100644 --- a/3rdparty/lexy/include/lexy/dsl/loop.hpp +++ b/3rdparty/lexy/include/lexy/dsl/loop.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_LOOP_HPP_INCLUDED @@ -94,7 +94,7 @@ struct _whl : rule_base template constexpr auto while_(Rule) { - static_assert(lexy::is_branch_rule, "while() requires a branch condition"); + LEXY_REQUIRE_BRANCH_RULE(Rule, "while()"); return _whl{}; } } // namespace lexyd @@ -105,7 +105,7 @@ namespace lexyd template constexpr auto while_one(Rule rule) { - static_assert(lexy::is_branch_rule, "while_one() requires a branch condition"); + LEXY_REQUIRE_BRANCH_RULE(Rule, "while_one()"); return rule >> while_(rule); } } // namespace lexyd diff --git a/3rdparty/lexy/include/lexy/dsl/member.hpp b/3rdparty/lexy/include/lexy/dsl/member.hpp index cc08a78a7..a478ef2dd 100644 --- a/3rdparty/lexy/include/lexy/dsl/member.hpp +++ b/3rdparty/lexy/include/lexy/dsl/member.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_MEMBER_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/newline.hpp b/3rdparty/lexy/include/lexy/dsl/newline.hpp index adba97118..24078f032 100644 --- a/3rdparty/lexy/include/lexy/dsl/newline.hpp +++ b/3rdparty/lexy/include/lexy/dsl/newline.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_NEWLINE_HPP_INCLUDED @@ -36,6 +36,8 @@ struct _eol : branch_base template struct bp { + static_assert(lexy::is_char_encoding); + constexpr bool try_parse(const void*, Reader reader) { return reader.peek() == Reader::encoding::eof() @@ -70,6 +72,7 @@ struct _eol : branch_base template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { + static_assert(lexy::is_char_encoding); return bp{}.template finish(context, reader, LEXY_FWD(args)...); } }; diff --git a/3rdparty/lexy/include/lexy/dsl/operator.hpp b/3rdparty/lexy/include/lexy/dsl/operator.hpp index 533150392..188ebf19a 100644 --- a/3rdparty/lexy/include/lexy/dsl/operator.hpp +++ b/3rdparty/lexy/include/lexy/dsl/operator.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_OPERATOR_HPP_INCLUDED @@ -75,8 +75,8 @@ struct op_lit_list template struct parsed_operator { - typename Reader::iterator pos; - std::size_t idx; + typename Reader::marker cur; + std::size_t idx; }; template @@ -85,7 +85,7 @@ constexpr auto parse_operator(Reader& reader) using encoding = typename Reader::encoding; using op_matcher = lexy::_detail::lit_trie_matcher, 0>; - auto begin = reader.position(); + auto begin = reader.current(); auto op = op_matcher::try_match(reader); return parsed_operator{begin, op}; } @@ -96,6 +96,11 @@ namespace lexyd template using _detect_op_tag_ctor = decltype(Tag(LEXY_DECLVAL(Reader).position())); +template +using _detect_op_tag_ctor_with_state + = decltype(Tag(*LEXY_DECLVAL(Context).control_block->parse_state, + LEXY_DECLVAL(Reader).position())); + template struct _op : branch_base { @@ -107,15 +112,20 @@ struct _op : branch_base lexy::_detail::parsed_operator op, Args&&... args) { - context.on(_ev::token{}, typename Literal::token_type{}, op.pos, reader.position()); + context.on(_ev::token{}, typename Literal::token_type{}, op.cur.position(), + reader.position()); using continuation = lexy::whitespace_parser, NextParser>>; if constexpr (std::is_void_v) return continuation::parse(context, reader, LEXY_FWD(args)...); + else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor_with_state, op_tag_type, + Reader, Context>) + return continuation::parse(context, reader, LEXY_FWD(args)..., + op_tag_type(*context.control_block->parse_state, op.pos)); else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor, op_tag_type, Reader>) return continuation::parse(context, reader, LEXY_FWD(args)..., - op_tag_type(reader.position())); + op_tag_type(op.cur.position())); else return continuation::parse(context, reader, LEXY_FWD(args)..., op_tag_type{}); } @@ -144,6 +154,12 @@ struct _op : branch_base if constexpr (std::is_void_v) return impl.template finish(context, reader, LEXY_FWD(args)...); + else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor_with_state, + op_tag_type, Reader, Context>) + return impl + .template finish(context, reader, LEXY_FWD(args)..., + op_tag_type(*context.control_block->parse_state, + reader.position())); else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor, op_tag_type, Reader>) return impl.template finish(context, reader, LEXY_FWD(args)..., op_tag_type(reader.position())); @@ -165,6 +181,10 @@ struct _op : branch_base = lexy::parser_for, NextParser>>; if constexpr (std::is_void_v) return continuation::parse(context, reader, LEXY_FWD(args)...); + else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor_with_state, + op_tag_type, Reader, Context>) + return continuation::parse(context, reader, LEXY_FWD(args)..., + op_tag_type(*context.control_block->parse_state, pos)); else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor, op_tag_type, Reader>) return continuation::parse(context, reader, LEXY_FWD(args)..., op_tag_type(pos)); else @@ -246,12 +266,12 @@ struct _opc : branch_base struct bp { lexy::_detail::parsed_operator op; - typename Reader::iterator end; + typename Reader::marker end; constexpr auto try_parse(const void*, Reader reader) { op = lexy::_detail::parse_operator(reader); - end = reader.position(); + end = reader.current(); return op.idx < op_literals::size; } @@ -262,7 +282,7 @@ struct _opc : branch_base template LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { - reader.set_position(end); + reader.reset(end); return op_finish(context, reader, op, LEXY_FWD(args)...); } }; @@ -276,7 +296,7 @@ struct _opc : branch_base bp impl{}; if (!impl.try_parse(context.control_block, reader)) { - auto err = lexy::error(impl.op.pos); + auto err = lexy::error(impl.op.cur.position()); context.on(_ev::error{}, err); return false; } @@ -311,4 +331,3 @@ constexpr auto operator/(_opc, _opc) } // namespace lexyd #endif // LEXY_DSL_OPERATOR_HPP_INCLUDED - diff --git a/3rdparty/lexy/include/lexy/dsl/option.hpp b/3rdparty/lexy/include/lexy/dsl/option.hpp index 614f8a141..0ba0c7404 100644 --- a/3rdparty/lexy/include/lexy/dsl/option.hpp +++ b/3rdparty/lexy/include/lexy/dsl/option.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_OPTION_HPP_INCLUDED @@ -80,7 +80,7 @@ struct _opt : rule_base template constexpr auto opt(Rule) { - static_assert(lexy::is_branch_rule, "opt() requires a branch condition"); + LEXY_REQUIRE_BRANCH_RULE(Rule, "opt()"); if constexpr (lexy::is_unconditional_branch_rule) // Branch is always taken, so don't wrap in opt(). return Rule{}; diff --git a/3rdparty/lexy/include/lexy/dsl/parse_as.hpp b/3rdparty/lexy/include/lexy/dsl/parse_as.hpp index 657aaf851..4efdde15a 100644 --- a/3rdparty/lexy/include/lexy/dsl/parse_as.hpp +++ b/3rdparty/lexy/include/lexy/dsl/parse_as.hpp @@ -1,14 +1,65 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_PARSE_AS_HPP_INCLUDED #define LEXY_DSL_PARSE_AS_HPP_INCLUDED +#include +#include #include -#include namespace lexyd { +// Custom handler that forwards events but overrides the value callback. +template +struct _pas_handler +{ + Handler& _handler; + + using event_handler = typename Handler::event_handler; + + // We are implicitly convertible to all handler types the original handler is convertible to. + // This is because the handler is passed to event_handler::on. + template (LEXY_DECLVAL(Handler&)))> + constexpr operator H&() const + { + return static_cast(_handler); + } + + // We use ::value to get a value. + // We can't use it unconditionally, as the initial production that contains the parse_as might + // not have one. So we silently fallback if that's the case - this might cause worse errors if + // the value is missing. + template + using value_callback + = std::conditional_t, + lexy::production_value_callback, + lexy::_detail::void_value_callback>; +}; + +struct _pas_final_parser +{ + template + LEXY_PARSER_FUNC static bool parse(Context&, Reader&, lexy::_detail::lazy_init& value, + Args&&... args) + { + value.emplace_result(lexy::construct, LEXY_FWD(args)...); + return true; + } +}; + +template +constexpr auto _make_pas_handler(Handler& handler) +{ + return _pas_handler{handler}; +} +// Prevent infinite nesting when parse_as itself is recursive. +template +constexpr auto _make_pas_handler(_pas_handler& handler) +{ + return handler; +} + template struct _pas : _copy_base { @@ -33,13 +84,27 @@ struct _pas : _copy_base template LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { + auto handler = _make_pas_handler(context.control_block->parse_handler); + lexy::_detail::parse_context_control_block cb(LEXY_MOV(handler), context.control_block); + using context_type + = lexy::_pc; + context_type sub_context(&cb); + sub_context.handler = LEXY_MOV(context).handler; + lexy::_detail::lazy_init value; + auto result + = rule_parser.template finish<_pas_final_parser>(sub_context, reader, value); - if (lexy::_detail::spc scan_context(value, context); - !rule_parser.template finish(scan_context, reader)) - return false; + context.control_block->copy_vars_from(&cb); + context.handler = LEXY_MOV(sub_context).handler; - if constexpr (Front) + if (!result) + return false; + else if constexpr (std::is_void_v) + // NOLINTNEXTLINE: clang-tidy wrongly thinks the branch is repeated. + return NextParser::parse(context, reader, LEXY_FWD(args)...); + else if constexpr (Front) return NextParser::parse(context, reader, *LEXY_MOV(value), LEXY_FWD(args)...); else return NextParser::parse(context, reader, LEXY_FWD(args)..., *LEXY_MOV(value)); @@ -52,13 +117,27 @@ struct _pas : _copy_base template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { + auto handler = _make_pas_handler(context.control_block->parse_handler); + lexy::_detail::parse_context_control_block cb(LEXY_MOV(handler), context.control_block); + using context_type + = lexy::_pc; + context_type sub_context(&cb); + sub_context.handler = LEXY_MOV(context).handler; + lexy::_detail::lazy_init value; + auto result + = lexy::parser_for::parse(sub_context, reader, value); - if (lexy::_detail::spc scan_context(value, context); - !lexy::parser_for::parse(scan_context, reader)) - return false; + context.control_block->copy_vars_from(&cb); + context.handler = LEXY_MOV(sub_context).handler; - if constexpr (Front) + if (!result) + return false; + else if constexpr (std::is_void_v) + // NOLINTNEXTLINE: clang-tidy wrongly thinks the branch is repeated. + return NextParser::parse(context, reader, LEXY_FWD(args)...); + else if constexpr (Front) return NextParser::parse(context, reader, *LEXY_MOV(value), LEXY_FWD(args)...); else return NextParser::parse(context, reader, LEXY_FWD(args)..., *LEXY_MOV(value)); diff --git a/3rdparty/lexy/include/lexy/dsl/parse_tree_node.hpp b/3rdparty/lexy/include/lexy/dsl/parse_tree_node.hpp new file mode 100644 index 000000000..c8aff2505 --- /dev/null +++ b/3rdparty/lexy/include/lexy/dsl/parse_tree_node.hpp @@ -0,0 +1,251 @@ +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors +// SPDX-License-Identifier: BSL-1.0 + +#ifndef LEXY_DSL_PARSE_TREE_NODE_HPP_INCLUDED +#define LEXY_DSL_PARSE_TREE_NODE_HPP_INCLUDED + +#include +#include + +#if !LEXY_EXPERIMENTAL +# error "lexy::dsl::tnode/pnode are experimental" +#endif + +//=== impl ===// +namespace lexyd +{ +template +struct _n; + +template +struct _nr : branch_base +{ + template + struct _cont + { + template + LEXY_PARSER_FUNC static bool parse(Context& context, ChildReader& child_reader, + bool& rule_succeded, Reader& reader, Args&&... args) + { + rule_succeded = true; + + if (child_reader.peek() != ChildReader::encoding::eof()) + { + auto begin = child_reader.position(); + auto end = reader.position(); + context.on(_ev::token{}, lexy::error_token_kind, begin, end); + + auto err = lexy::error(begin, end); + context.on(_ev::error{}, err); + } + + return lexy::whitespace_parser::parse(context, reader, + LEXY_FWD(args)...); + } + }; + + template + LEXY_PARSER_FUNC static bool _parse_rule(Context& context, Reader& reader, + typename Reader::marker end, Args&&... args) + { + auto child_reader = Derived::node_child_reader(reader); + reader.reset(end); + + using rule_parser + = lexy::whitespace_parser>>; + if (auto rule_succeded = false; + rule_parser::parse(context, child_reader, rule_succeded, reader, LEXY_FWD(args)...)) + { + return true; + } + else + { + if (!rule_succeded) + // Report an error token for the child span that wasn't able to be parsed. + context.on(_ev::token{}, lexy::error_token_kind, child_reader.position(), + end.position()); + return false; + } + } + + template + struct bp + { + typename Reader::marker end; + + constexpr bool try_parse(const void*, const Reader& reader) + { + lexy::token_parser_for<_n, Reader> parser(reader); + auto result = parser.try_parse(reader); + end = parser.end; + return result; + } + + template + constexpr void cancel(Context&) + {} + + template + LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) + { + return _parse_rule(context, reader, end, LEXY_FWD(args)...); + } + }; + + template + struct p + { + template + LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) + { + lexy::token_parser_for<_n, Reader> parser(reader); + if (!parser.try_parse(reader)) + { + LEXY_ASSERT(parser.end.position() == reader.position(), "impl should be LL(1)"); + parser.report_error(context, reader); + return false; + } + + return _parse_rule(context, reader, parser.end, LEXY_FWD(args)...); + } + }; +}; + +template +struct _n : token_base +{ + template + struct tp + { + typename Reader::marker end; + + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} + + constexpr auto try_parse(Reader reader) + { + if constexpr (lexy::is_node_encoding) + { + if (!Reader::encoding::match(reader.peek(), Derived::node_kind())) + return false; + + reader.bump(); + end = reader.current(); + return true; + } + else + { + // This happens when it is used as whitespace, which is inherited while parsing the + // token lexeme, we don't match anything in that case. + return std::false_type{}; + } + } + + template + constexpr void report_error(Context& context, Reader reader) + { + constexpr auto name = Derived::node_kind_name(); + + auto err = lexy::error(reader.position(), name); + context.on(_ev::error{}, err); + } + }; + + template + constexpr auto operator()(Rule) const + { + return _nr{}; + } +}; +} // namespace lexyd + +//=== dsl::tnode ===// +namespace lexy +{ +struct expected_token_end +{ + static LEXY_CONSTEVAL auto name() + { + return "expected token end"; + } +}; +} // namespace lexy + +namespace lexyd +{ +template +struct _tn : _n<_tn> +{ + static LEXY_CONSTEVAL auto node_kind() + { + return Kind; + } + + static LEXY_CONSTEVAL auto node_kind_name() + { + using lexy::token_kind_name; + return token_kind_name(Kind); + } + + using node_end_error = lexy::expected_token_end; + + template + static constexpr auto node_child_reader(Reader& reader) + { + return reader.lexeme_reader(); + } +}; + +template +constexpr auto tnode = _tn{}; +} // namespace lexyd + +namespace lexy +{ +template +constexpr auto token_kind_of> = Kind; +} // namespace lexy + +//=== dsl::pnode ===// +namespace lexy +{ +struct expected_production_end +{ + static LEXY_CONSTEVAL auto name() + { + return "expected production end"; + } +}; +} // namespace lexy + +namespace lexyd +{ +template +struct _pn : _n<_pn> +{ + static_assert(lexy::is_production); + + static LEXY_CONSTEVAL auto node_kind() + { + return Production{}; + } + + static LEXY_CONSTEVAL auto node_kind_name() + { + return lexy::production_name(); + } + + using node_end_error = lexy::expected_production_end; + + template + static constexpr auto node_child_reader(Reader& reader) + { + return reader.child_reader(); + } +}; + +template +constexpr auto pnode = _pn{}; +} // namespace lexyd + +#endif // LEXY_DSL_PARSE_TREE_NODE_HPP_INCLUDED + diff --git a/3rdparty/lexy/include/lexy/dsl/peek.hpp b/3rdparty/lexy/include/lexy/dsl/peek.hpp index 3118a4182..57b3576b8 100644 --- a/3rdparty/lexy/include/lexy/dsl/peek.hpp +++ b/3rdparty/lexy/include/lexy/dsl/peek.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_PEEK_HPP_INCLUDED @@ -37,7 +37,7 @@ struct _peek : branch_base struct bp { typename Reader::iterator begin; - typename Reader::iterator end; + typename Reader::marker end; constexpr bool try_parse(const void*, Reader reader) { @@ -54,13 +54,13 @@ struct _peek : branch_base template constexpr void cancel(Context& context) { - context.on(_ev::backtracked{}, begin, end); + context.on(_ev::backtracked{}, begin, end.position()); } template LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { - context.on(_ev::backtracked{}, begin, end); + context.on(_ev::backtracked{}, begin, end.position()); return NextParser::parse(context, reader, LEXY_FWD(args)...); } }; @@ -76,13 +76,13 @@ struct _peek : branch_base { // Report that we've failed. using tag = lexy::_detail::type_or; - auto err = lexy::error(impl.begin, impl.end); + auto err = lexy::error(impl.begin, impl.end.position()); context.on(_ev::error{}, err); // But recover immediately, as we wouldn't have consumed anything either way. } - context.on(_ev::backtracked{}, impl.begin, impl.end); + context.on(_ev::backtracked{}, impl.begin, impl.end.position()); return NextParser::parse(context, reader, LEXY_FWD(args)...); } }; @@ -98,7 +98,7 @@ struct _peekn : branch_base struct bp { typename Reader::iterator begin; - typename Reader::iterator end; + typename Reader::marker end; constexpr bool try_parse(const void*, Reader reader) { @@ -115,13 +115,13 @@ struct _peekn : branch_base template constexpr void cancel(Context& context) { - context.on(_ev::backtracked{}, begin, end); + context.on(_ev::backtracked{}, begin, end.position()); } template LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { - context.on(_ev::backtracked{}, begin, end); + context.on(_ev::backtracked{}, begin, end.position()); return NextParser::parse(context, reader, LEXY_FWD(args)...); } }; @@ -137,14 +137,22 @@ struct _peekn : branch_base { // Report that we've failed. using tag = lexy::_detail::type_or; - auto err = lexy::error(impl.begin, impl.end); + auto err = lexy::error(impl.begin, impl.end.position()); context.on(_ev::error{}, err); - // But recover immediately, as we wouldn't have consumed anything either way. - } + // And recover by consuming the input. + context.on(_ev::recovery_start{}, impl.begin); + context.on(_ev::token{}, lexy::error_token_kind, impl.begin, impl.end.position()); + context.on(_ev::recovery_finish{}, impl.end.position()); - context.on(_ev::backtracked{}, impl.begin, impl.end); - return NextParser::parse(context, reader, LEXY_FWD(args)...); + reader.reset(impl.end); + return NextParser::parse(context, reader, LEXY_FWD(args)...); + } + else + { + context.on(_ev::backtracked{}, impl.begin, impl.end.position()); + return NextParser::parse(context, reader, LEXY_FWD(args)...); + } } }; @@ -160,8 +168,7 @@ constexpr auto peek(Rule) return _peek{}; } -/// Check if at this reader position, the rule would not match, but don't actually consume any -/// characters if it does. +/// Checks if at this reader position, the rule would not match. template constexpr auto peek_not(Rule) { diff --git a/3rdparty/lexy/include/lexy/dsl/position.hpp b/3rdparty/lexy/include/lexy/dsl/position.hpp index 56b0fe217..088187bf9 100644 --- a/3rdparty/lexy/include/lexy/dsl/position.hpp +++ b/3rdparty/lexy/include/lexy/dsl/position.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_POSITION_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/production.hpp b/3rdparty/lexy/include/lexy/dsl/production.hpp index 5603a6d7e..ba74de2a7 100644 --- a/3rdparty/lexy/include/lexy/dsl/production.hpp +++ b/3rdparty/lexy/include/lexy/dsl/production.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_PRODUCTION_HPP_INCLUDED @@ -77,10 +77,8 @@ struct _prd template struct bp { - using parser_t = lexy::branch_parser_for, Reader>; - - parser_t parser; - typename Reader::iterator begin; + lexy::production_branch_parser parser; + typename Reader::iterator begin; template constexpr auto try_parse(const ControlBlock* cb, const Reader& reader) @@ -181,7 +179,7 @@ struct _recb : branch_base template struct bp { - static_assert(lexy::is_branch_rule>); + LEXY_REQUIRE_BRANCH_RULE(lexy::production_rule, "recurse_branch"); using impl = lexy::branch_parser_for<_prd, Reader>; impl _impl; diff --git a/3rdparty/lexy/include/lexy/dsl/punctuator.hpp b/3rdparty/lexy/include/lexy/dsl/punctuator.hpp index d283130d6..e53f7b0f2 100644 --- a/3rdparty/lexy/include/lexy/dsl/punctuator.hpp +++ b/3rdparty/lexy/include/lexy/dsl/punctuator.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_PUNCTUATOR_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/recover.hpp b/3rdparty/lexy/include/lexy/dsl/recover.hpp index 20915adbe..208d8c409 100644 --- a/3rdparty/lexy/include/lexy/dsl/recover.hpp +++ b/3rdparty/lexy/include/lexy/dsl/recover.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_RECOVER_HPP_INCLUDED @@ -38,17 +38,24 @@ struct _recovery_wrapper : _recovery_base LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { context.on(_ev::recovery_start{}, reader.position()); - auto recovery_finished = false; - auto result - = lexy::parser_for::parse(context, reader, recovery_finished, - LEXY_FWD(args)...); + + // As part of the recovery, we parse the rule and whitespace. + using parser = lexy::parser_for>; + auto result = parser::parse(context, reader, recovery_finished, LEXY_FWD(args)...); + if (!recovery_finished) context.on(_ev::recovery_cancel{}, reader.position()); return result; } }; }; + +struct _noop_recovery : rule_base +{ + template + using p = NextParser; +}; } // namespace lexyd namespace lexyd @@ -72,20 +79,20 @@ struct _find : _recovery_base context.on(_ev::recovery_start{}, begin); while (true) { - auto end = reader.position(); // *before* we've consumed Token/Limit + auto end = reader.current(); // *before* we've consumed Token/Limit auto result = matcher::try_match(reader); if (result == 0) { - context.on(_ev::token{}, lexy::error_token_kind, begin, end); - context.on(_ev::recovery_finish{}, end); - reader.set_position(end); // reset to before the token + context.on(_ev::token{}, lexy::error_token_kind, begin, end.position()); + context.on(_ev::recovery_finish{}, end.position()); + reader.reset(end); // reset to before the token return NextParser::parse(context, reader, LEXY_FWD(args)...); } else if (result == 1 || reader.peek() == Reader::encoding::eof()) { - context.on(_ev::token{}, lexy::error_token_kind, begin, end); - context.on(_ev::recovery_cancel{}, end); - reader.set_position(end); // reset to before the limit + context.on(_ev::token{}, lexy::error_token_kind, begin, end.position()); + context.on(_ev::recovery_cancel{}, end.position()); + reader.reset(end); // reset to before the limit return false; } else @@ -200,7 +207,7 @@ template constexpr auto recover(Branches...) { static_assert(sizeof...(Branches) > 0); - static_assert((lexy::is_branch_rule && ...)); + LEXY_REQUIRE_BRANCH_RULE(Branches..., "recover"); return _reco{}; } } // namespace lexyd @@ -231,13 +238,23 @@ struct _tryt : rule_base LEXY_PARSER_FUNC static bool recover(Context& context, Reader& reader, Args&&... args) { if constexpr (std::is_void_v) - return NextParser::parse(context, reader, LEXY_FWD(args)...); + { + using recovery_rule = _recovery_wrapper<_noop_recovery>; + return lexy::parser_for::parse(context, reader, + LEXY_FWD(args)...); + } else if constexpr (std::is_base_of_v<_recovery_base, Recover>) - return lexy::parser_for::parse(context, reader, - LEXY_FWD(args)...); + { + using recovery_rule = Recover; + return lexy::parser_for::parse(context, reader, + LEXY_FWD(args)...); + } else - return lexy::parser_for<_recovery_wrapper, - NextParser>::parse(context, reader, LEXY_FWD(args)...); + { + using recovery_rule = _recovery_wrapper; + return lexy::parser_for::parse(context, reader, + LEXY_FWD(args)...); + } } }; diff --git a/3rdparty/lexy/include/lexy/dsl/repeat.hpp b/3rdparty/lexy/include/lexy/dsl/repeat.hpp index 256ae2264..bb41404b6 100644 --- a/3rdparty/lexy/include/lexy/dsl/repeat.hpp +++ b/3rdparty/lexy/include/lexy/dsl/repeat.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_REPEAT_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/return.hpp b/3rdparty/lexy/include/lexy/dsl/return.hpp index 245f83ba3..46477f6f0 100644 --- a/3rdparty/lexy/include/lexy/dsl/return.hpp +++ b/3rdparty/lexy/include/lexy/dsl/return.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_RETURN_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/scan.hpp b/3rdparty/lexy/include/lexy/dsl/scan.hpp index 139d56cd7..d630fd61c 100644 --- a/3rdparty/lexy/include/lexy/dsl/scan.hpp +++ b/3rdparty/lexy/include/lexy/dsl/scan.hpp @@ -1,14 +1,12 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_SCAN_HPP_INCLUDED #define LEXY_DSL_SCAN_HPP_INCLUDED -#include -#include #include -#include #include +#include #include #include @@ -20,7 +18,9 @@ struct _prd; template struct _peek; template -struct _capt; +struct _cap; +template +struct _capr; template struct _int_dsl; @@ -133,106 +133,31 @@ scan_result(_detail::lazy_init&&) -> scan_result; //=== scanner implementation ===// namespace lexy::_detail { -template -using _value_callback_for = lexy::production_value_callback< - typename Context::production, - std::remove_pointer_tparse_state)>>; - -// The context used for a child production during scanning. -// It forwards all events but overrides the value callback. -template > -struct spc_child +template +struct scanner_input { - using production = typename Context::production; - using whitespace_production = typename Context::whitespace_production; - using value_type = typename ValueCallback::return_type; - - RootContext* root_context; - decltype(Context::handler) handler; - decltype(Context::control_block) control_block; - _detail::lazy_init value; - - constexpr explicit spc_child(RootContext& root, decltype(Context::control_block) cb) - : root_context(&root), control_block(cb) - {} - constexpr explicit spc_child(RootContext& root, const Context& context) - : root_context(&root), control_block(context.control_block) - {} - - template - constexpr auto sub_context(ChildProduction child) - { - using sub_context_t = decltype(LEXY_DECLVAL(Context).sub_context(child)); - if constexpr (std::is_same_v) - return spc_child(*root_context, - control_block); - else - return spc_child(*root_context, control_block); - } - - constexpr auto value_callback() - { - return ValueCallback(control_block->parse_state); - } + Reader _impl; - template - constexpr auto on(Event ev, Args&&... args) + constexpr auto reader() const& { - return handler.on(control_block->parse_handler, ev, LEXY_FWD(args)...); + return _impl; } }; -// The context used for a top-level rule parsing during scanning. -// It forwards all events but overrids the value callback to construct a T. -template -struct spc +struct scan_final_parser { - using production = typename Context::production; - using whitespace_production = typename Context::whitespace_production; - using value_type = T; - - Context* root_context; - decltype(Context::handler)& handler; - decltype(Context::control_block) control_block; - _detail::lazy_init& value; - - constexpr explicit spc(_detail::lazy_init& value, Context& context) - : root_context(&context), handler(context.handler), control_block(context.control_block), - value(value) - {} - - template - constexpr auto sub_context(ChildProduction child) + template + LEXY_PARSER_FUNC static bool parse(Context&, Reader&, lazy_init* dest, T&& value) { - using sub_context_t = decltype(LEXY_DECLVAL(Context).sub_context(child)); - if constexpr (std::is_void_v) - return spc_child(*root_context, - control_block); - else - return spc_child(*root_context, control_block); + dest->emplace(LEXY_MOV(value)); + return true; } - constexpr auto value_callback() + template + LEXY_PARSER_FUNC static bool parse(Context&, Reader&, lazy_init* dest) { - return lexy::construct; - } - - template - constexpr auto on(Event ev, Args&&... args) - { - return handler.on(control_block->parse_handler, ev, LEXY_FWD(args)...); - } -}; - -template -struct scanner_input -{ - Reader _impl; - - constexpr auto reader() const& - { - return _impl; + dest->emplace(); + return true; } }; @@ -243,7 +168,7 @@ class scanner public: using encoding = typename Reader::encoding; - constexpr scanner(const scanner&) noexcept = delete; + constexpr scanner(const scanner&) noexcept = delete; constexpr scanner& operator=(const scanner&) noexcept = delete; //=== status ===// @@ -261,6 +186,10 @@ class scanner { return _reader.position(); } + constexpr auto current() const noexcept -> typename Reader::marker + { + return _reader.current(); + } constexpr auto remaining_input() const noexcept { @@ -274,63 +203,24 @@ class scanner if (_state == _state_failed) return; - _detail::spc context(result._value, static_cast(*this).context()); - - using parser = lexy::parser_for; - auto success = parser::parse(context, _reader); + using parser = lexy::parser_for, scan_final_parser>; + auto success + = parser::parse(static_cast(*this).context(), _reader, &result._value); if (!success) _state = _state_failed; } - template > - constexpr auto parse(Production production = {}) + template > + constexpr auto parse(Production = {}) { - // Directly create a child context from the production context. - auto& root_context = static_cast(*this).context(); - auto context = _detail::spc_child(root_context, root_context.sub_context(production)); - if (_state == _state_failed) - return scan_result(LEXY_MOV(context.value)); + using context_t = LEXY_DECAY_DECLTYPE(static_cast(*this).context()); + using value_type = + typename lexy::production_value_callback::return_type; - // We manually parse the rule of the production, so need to raise events. - context.on(lexy::parse_events::production_start{}, _reader.position()); - - if constexpr (lexy::_production_defines_whitespace) - { - // Skip initial whitespace of the production. - using whitespace_parser - = lexy::whitespace_parser>; - if (!whitespace_parser::parse(context, _reader)) - { - context.on(lexy::parse_events::production_cancel{}, _reader.position()); - _state = _state_failed; - return scan_result(LEXY_MOV(context.value)); - } - } - - using parser = lexy::parser_for; - auto success = parser::parse(context, _reader); - if (!success) - { - context.on(lexy::parse_events::production_cancel{}, _reader.position()); - _state = _state_failed; - return scan_result(LEXY_MOV(context.value)); - } - - context.on(lexy::parse_events::production_finish{}, _reader.position()); - - if constexpr (lexy::is_token_production) - { - // Skip trailing whitespace of the parent. - using whitespace_parser = lexy::whitespace_parser>; - if (!whitespace_parser::parse(root_context, _reader)) - { - _state = _state_failed; - return scan_result(LEXY_MOV(context.value)); - } - } - - return scan_result(LEXY_MOV(context.value)); + scan_result result; + parse(result, lexyd::_prd{}); + return result; } template >> @@ -344,20 +234,20 @@ class scanner template >> constexpr bool branch(scan_result& result, Rule) { - static_assert(lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Rule, "branch"); if (_state == _state_failed) return false; - _detail::spc context(result._value, static_cast(*this).context()); - - lexy::branch_parser_for parser{}; + auto& context = static_cast(*this).context(); + lexy::branch_parser_for, Reader> parser{}; if (!parser.try_parse(context.control_block, _reader)) { parser.cancel(context); return false; // branch wasn't token } - auto success = parser.template finish(context, _reader); + auto success = parser.template finish(context, _reader, + &result._value); if (!success) _state = _state_failed; return true; // branch was taken @@ -380,7 +270,7 @@ class scanner class error_recovery_guard { public: - error_recovery_guard(const error_recovery_guard&) = delete; + error_recovery_guard(const error_recovery_guard&) = delete; error_recovery_guard& operator=(const error_recovery_guard&) = delete; constexpr void cancel() && @@ -439,6 +329,13 @@ class scanner context.on(parse_events::error{}, lexy::error(LEXY_FWD(args)...)); } + template + constexpr void error(const char* msg, Args&&... args) + { + auto& context = static_cast(*this).context(); + context.on(parse_events::error{}, lexy::error(LEXY_FWD(args)..., msg)); + } + template constexpr void fatal_error(Tag tag, Args&&... args) { @@ -446,6 +343,13 @@ class scanner _state = _state_failed; } + template + constexpr void fatal_error(const char* msg, Args&&... args) + { + error(msg, LEXY_FWD(args)...); + _state = _state_failed; + } + //=== convenience ===// template >> constexpr auto parse(Rule rule) @@ -477,25 +381,18 @@ class scanner return result; } - template - constexpr auto capture(Rule rule) -> scan_result> + template + constexpr auto capture(Token) { - static_assert(lexy::is_rule); - - auto begin = _reader.position(); - parse(rule); - auto end = _reader.position(); - - if (*this) - return lexeme(begin, end); - else - return scan_failed; + scan_result> result; + parse(result, lexyd::_cap{}); + return result; } - template - constexpr auto capture_token(Token) + template + constexpr auto capture(lexyd::_prd) { scan_result> result; - parse(result, lexyd::_capt{}); + parse(result, lexyd::_capr>{}); return result; } @@ -562,8 +459,9 @@ class rule_scanner : public _detail::scanner, Read namespace lexyd { -template -using _detect_scan_state = decltype(Context::production::scan(LEXY_DECLVAL(Scanner&), *StatePtr())); +template +using _detect_scan_state = decltype(Context::production::scan(LEXY_DECLVAL(Scanner&), *StatePtr(), + LEXY_DECLVAL(Args)...)); struct _scan : rule_base { @@ -574,16 +472,16 @@ struct _scan : rule_base LEXY_PARSER_FUNC static bool _parse(Scanner& scanner, Context& context, Reader& reader, Args&&... args) { - lexy::scan_result result = [&] { + typename Context::production::scan_result result = [&] { if constexpr (lexy::_detail::is_detected< _detect_scan_state, Context, decltype(scanner), - decltype(context.control_block->parse_state)>) + decltype(context.control_block->parse_state), Args&&...>) return Context::production::scan(scanner, *context.control_block->parse_state, LEXY_FWD(args)...); else return Context::production::scan(scanner, LEXY_FWD(args)...); }(); - reader.set_position(scanner.position()); + reader.reset(scanner.current()); if (!result) return false; @@ -599,15 +497,6 @@ struct _scan : rule_base lexy::rule_scanner scanner(context, reader); return _parse(scanner, context, reader, LEXY_FWD(args)...); } - template - LEXY_PARSER_FUNC static bool parse( - lexy::_detail::spc_child& context, Reader& reader, - Args&&... args) - { - lexy::rule_scanner scanner(*context.root_context, reader); - return _parse(scanner, context, reader, LEXY_FWD(args)...); - } }; }; diff --git a/3rdparty/lexy/include/lexy/dsl/separator.hpp b/3rdparty/lexy/include/lexy/dsl/separator.hpp index 64c032f2a..f50744d66 100644 --- a/3rdparty/lexy/include/lexy/dsl/separator.hpp +++ b/3rdparty/lexy/include/lexy/dsl/separator.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_SEPARATOR_HPP_INCLUDED @@ -90,7 +90,7 @@ struct _sep : _sep_base template constexpr auto sep(Branch) { - static_assert(lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Branch, "sep"); return _sep{}; } @@ -110,7 +110,7 @@ struct _tsep : _sep_base template constexpr auto trailing_sep(Branch) { - static_assert(lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Branch, "trailing_sep"); return _tsep{}; } diff --git a/3rdparty/lexy/include/lexy/dsl/sequence.hpp b/3rdparty/lexy/include/lexy/dsl/sequence.hpp index 4d2ba6dea..6715e7ae3 100644 --- a/3rdparty/lexy/include/lexy/dsl/sequence.hpp +++ b/3rdparty/lexy/include/lexy/dsl/sequence.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_SEQUENCE_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/sign.hpp b/3rdparty/lexy/include/lexy/dsl/sign.hpp index 355044167..5cf38de0d 100644 --- a/3rdparty/lexy/include/lexy/dsl/sign.hpp +++ b/3rdparty/lexy/include/lexy/dsl/sign.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_SIGN_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/subgrammar.hpp b/3rdparty/lexy/include/lexy/dsl/subgrammar.hpp index 9f4bbc97f..6a291dfc4 100644 --- a/3rdparty/lexy/include/lexy/dsl/subgrammar.hpp +++ b/3rdparty/lexy/include/lexy/dsl/subgrammar.hpp @@ -20,6 +20,9 @@ using _subgrammar_for = _subgrammar \ + constexpr auto production_has_value_callback = true; \ + \ template \ struct _subgrammar \ { \ @@ -104,4 +107,3 @@ constexpr auto subgrammar = _subg{}; } // namespace lexyd #endif // LEXY_DSL_SUBGRAMMAR_HPP_INCLUDED - diff --git a/3rdparty/lexy/include/lexy/dsl/symbol.hpp b/3rdparty/lexy/include/lexy/dsl/symbol.hpp index f9a1a4cfe..f94511068 100644 --- a/3rdparty/lexy/include/lexy/dsl/symbol.hpp +++ b/3rdparty/lexy/include/lexy/dsl/symbol.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_SYMBOL_HPP_INCLUDED @@ -268,7 +268,8 @@ struct _sym : branch_base template struct bp { - typename Reader::iterator end; + static_assert(lexy::is_char_encoding); + typename Reader::marker end; typename LEXY_DECAY_DECLTYPE(Table)::key_index symbol; constexpr auto value() const @@ -286,7 +287,7 @@ struct _sym : branch_base end = parser.end; // Check whether this is a symbol. - auto content = lexy::partial_input(reader, end); + auto content = lexy::partial_input(reader, end.position()); symbol = Table.parse(content); // Only succeed if it is a symbol. @@ -301,8 +302,8 @@ struct _sym : branch_base LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { // We need to consume and report the token. - context.on(_ev::token{}, Token{}, reader.position(), end); - reader.set_position(end); + context.on(_ev::token{}, Token{}, reader.position(), end.position()); + reader.reset(end); // And continue parsing with the symbol value after whitespace skipping. using continuation = lexy::whitespace_parser; @@ -340,6 +341,7 @@ struct _sym : branch_base template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { + static_assert(lexy::is_char_encoding); // Capture the token and continue with special continuation. return lexy::parser_for<_cap, _cont>::parse(context, reader, LEXY_FWD(args)...); @@ -360,8 +362,9 @@ struct _sym, Tag> : branch_base template struct bp { + static_assert(lexy::is_char_encoding); typename LEXY_DECAY_DECLTYPE(Table)::key_index symbol; - typename Reader::iterator end; + typename Reader::marker end; constexpr auto value() const { @@ -374,7 +377,7 @@ struct _sym, Tag> : branch_base symbol = Table.try_parse(reader); if (!symbol) return false; - end = reader.position(); + end = reader.current(); // We had a symbol, but it must not be the prefix of a valid identifier. return !lexy::try_match_token(T{}, reader); @@ -388,8 +391,8 @@ struct _sym, Tag> : branch_base LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { // We need to consume and report the identifier pattern. - context.on(_ev::token{}, _idp{}, reader.position(), end); - reader.set_position(end); + context.on(_ev::token{}, _idp{}, reader.position(), end.position()); + reader.reset(end); // And continue parsing with the symbol value after whitespace skipping. using continuation = lexy::whitespace_parser; @@ -403,6 +406,7 @@ struct _sym, Tag> : branch_base template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { + static_assert(lexy::is_char_encoding); auto begin = reader.position(); // Try to parse a symbol that is not the prefix of an identifier. @@ -427,9 +431,9 @@ struct _sym, Tag> : branch_base else { // We need to consume and report the identifier pattern. - auto end = symbol_reader.position(); - context.on(_ev::token{}, _idp{}, begin, end); - reader.set_position(end); + auto end = symbol_reader.current(); + context.on(_ev::token{}, _idp{}, begin, end.position()); + reader.reset(end); // And continue parsing with the symbol value after whitespace skipping. using continuation = lexy::whitespace_parser; @@ -449,8 +453,9 @@ struct _sym : branch_base template struct bp { + static_assert(lexy::is_char_encoding); typename LEXY_DECAY_DECLTYPE(Table)::key_index symbol; - typename Reader::iterator end; + typename Reader::marker end; constexpr auto value() const { @@ -461,7 +466,7 @@ struct _sym : branch_base { // Try to parse a symbol. symbol = Table.try_parse(reader); - end = reader.position(); + end = reader.current(); // Only succeed if it is a symbol. return static_cast(symbol); @@ -475,8 +480,9 @@ struct _sym : branch_base LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { // We need to consume and report the token. - context.on(_ev::token{}, lexy::identifier_token_kind, reader.position(), end); - reader.set_position(end); + context.on(_ev::token{}, lexy::identifier_token_kind, reader.position(), + end.position()); + reader.reset(end); // And continue parsing with the symbol value after whitespace skipping. using continuation = lexy::whitespace_parser; @@ -490,6 +496,7 @@ struct _sym : branch_base template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { + static_assert(lexy::is_char_encoding); bp impl{}; if (impl.try_parse(context.control_block, reader)) return impl.template finish(context, reader, LEXY_FWD(args)...); diff --git a/3rdparty/lexy/include/lexy/dsl/terminator.hpp b/3rdparty/lexy/include/lexy/dsl/terminator.hpp index a864db1f9..56a4e53df 100644 --- a/3rdparty/lexy/include/lexy/dsl/terminator.hpp +++ b/3rdparty/lexy/include/lexy/dsl/terminator.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_TERMINATOR_HPP_INCLUDED @@ -105,7 +105,7 @@ struct _term template constexpr auto terminator(Branch) { - static_assert(lexy::is_branch_rule); + LEXY_REQUIRE_BRANCH_RULE(Branch, "terminator"); return _term{}; } } // namespace lexyd diff --git a/3rdparty/lexy/include/lexy/dsl/times.hpp b/3rdparty/lexy/include/lexy/dsl/times.hpp index 23b42e4f1..7ebf327cd 100644 --- a/3rdparty/lexy/include/lexy/dsl/times.hpp +++ b/3rdparty/lexy/include/lexy/dsl/times.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_TIMES_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/dsl/token.hpp b/3rdparty/lexy/include/lexy/dsl/token.hpp index c70fbacb0..95c812c1c 100644 --- a/3rdparty/lexy/include/lexy/dsl/token.hpp +++ b/3rdparty/lexy/include/lexy/dsl/token.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_TOKEN_HPP_INCLUDED @@ -46,7 +46,7 @@ struct token_base : _token_inherit template struct bp { - typename Reader::iterator end; + typename Reader::marker end; constexpr auto try_parse(const void*, const Reader& reader) { @@ -63,8 +63,8 @@ struct token_base : _token_inherit template LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args) { - context.on(_ev::token{}, Derived{}, reader.position(), end); - reader.set_position(end); + context.on(_ev::token{}, Derived{}, reader.position(), end.position()); + reader.reset(end); return lexy::whitespace_parser::parse(context, reader, LEXY_FWD(args)...); } @@ -85,16 +85,17 @@ struct token_base : _token_inherit { if (!parser.try_parse(reader)) { - context.on(_ev::token{}, lexy::error_token_kind, reader.position(), parser.end); + context.on(_ev::token{}, lexy::error_token_kind, reader.position(), + parser.end.position()); parser.report_error(context, reader); - reader.set_position(parser.end); + reader.reset(parser.end); return false; } } - context.on(_ev::token{}, typename Derived::token_type{}, begin, parser.end); - reader.set_position(parser.end); + context.on(_ev::token{}, typename Derived::token_type{}, begin, parser.end.position()); + reader.reset(parser.end); return true; } @@ -130,6 +131,19 @@ struct _tokk : token_base<_tokk, Token> template struct _toke : token_base<_toke, Token> { + // If we're overriding the error for a char class rule, we also want to change its error + // reporting. Otherwise, rules such as `dsl::delimited()` building on char classes will generate + // the "wrong" error. + // + // If it's not a char class, adding this function doesn't hurt. + template + static constexpr void char_class_report_error(Context& context, + typename Reader::iterator position) + { + auto err = lexy::error(position, position); + context.on(_ev::error{}, err); + } + template struct tp : lexy::token_parser_for { @@ -140,7 +154,7 @@ struct _toke : token_base<_toke, Token> constexpr void report_error(Context& context, const Reader& reader) { // Report a different error. - auto err = lexy::error(reader.position(), this->end); + auto err = lexy::error(reader.position(), this->end.position()); context.on(_ev::error{}, err); } }; @@ -163,6 +177,7 @@ struct _token : token_base<_token> { struct _production { + static constexpr auto name = ""; static constexpr auto max_recursion_depth = 0; static constexpr auto rule = Rule{}; }; @@ -170,23 +185,26 @@ struct _token : token_base<_token> template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { // We match a dummy production that only consists of the rule. - auto success - = lexy::do_action<_production>(lexy::match_handler(), lexy::no_parse_state, reader); - end = reader.position(); + auto success = lexy::do_action< + _production, + lexy::match_action::template result_type>(lexy::_mh(), + lexy::no_parse_state, + reader); + end = reader.current(); return success; } template constexpr void report_error(Context& context, const Reader& reader) { - auto err = lexy::error(reader.position(), end); + auto err = lexy::error(reader.position(), end.position()); context.on(_ev::error{}, err); } }; diff --git a/3rdparty/lexy/include/lexy/dsl/unicode.hpp b/3rdparty/lexy/include/lexy/dsl/unicode.hpp index d4d174975..e895ae3c8 100644 --- a/3rdparty/lexy/include/lexy/dsl/unicode.hpp +++ b/3rdparty/lexy/include/lexy/dsl/unicode.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_UNICODE_HPP_INCLUDED @@ -11,6 +11,30 @@ namespace lexyd::unicode { +struct _nctrl : char_class_base<_nctrl> +{ + static LEXY_CONSTEVAL auto char_class_name() + { + return "code-point.non-control"; + } + + static LEXY_CONSTEVAL auto char_class_ascii() + { + return ascii::_print::char_class_ascii(); + } + + static constexpr bool char_class_match_cp(char32_t cp) + { + return !lexy::code_point(cp).is_control(); + } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_print::template char_class_match_swar(c); + } +}; + struct _control : char_class_base<_control> { static LEXY_CONSTEVAL auto char_class_name() @@ -27,9 +51,24 @@ struct _control : char_class_base<_control> { return lexy::code_point(cp).is_control(); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_control::template char_class_match_swar(c); + } }; inline constexpr auto control = _control{}; +constexpr auto operator-(_control) +{ + return _nctrl{}; +} +constexpr auto operator-(_nctrl) +{ + return _control{}; +} + //=== whitespace ===// struct _blank : char_class_base<_blank> { @@ -48,6 +87,12 @@ struct _blank : char_class_base<_blank> // tab already handled as part of ASCII return lexy::code_point(cp).general_category() == lexy::code_point::space_separator; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_blank::template char_class_match_swar(c); + } }; inline constexpr auto blank = _blank{}; @@ -68,6 +113,12 @@ struct _newline : char_class_base<_newline> // NEL, PARAGRAPH SEPARATOR, LINE SEPARATOR return cp == 0x85 || cp == 0x2029 || cp == 0x2028; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_newline::template char_class_match_swar(c); + } }; inline constexpr auto newline = _newline{}; @@ -84,6 +135,12 @@ struct _other_space : char_class_base<_other_space> } // The same as in ASCII, so no match function needed. + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_other_space::template char_class_match_swar(c); + } }; inline constexpr auto other_space = _other_space{}; @@ -103,6 +160,12 @@ struct _space : char_class_base<_space> { return lexy::_detail::code_point_has_properties(cp); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_space::template char_class_match_swar(c); + } }; inline constexpr auto space = _space{}; @@ -123,6 +186,12 @@ struct _lower : char_class_base<_lower> { return lexy::_detail::code_point_has_properties(cp); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_lower::template char_class_match_swar(c); + } }; inline constexpr auto lower = _lower{}; @@ -142,6 +211,12 @@ struct _upper : char_class_base<_upper> { return lexy::_detail::code_point_has_properties(cp); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_upper::template char_class_match_swar(c); + } }; inline constexpr auto upper = _upper{}; @@ -161,6 +236,12 @@ struct _alpha : char_class_base<_alpha> { return lexy::_detail::code_point_has_properties(cp); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_alpha::template char_class_match_swar(c); + } }; inline constexpr auto alpha = _alpha{}; @@ -181,6 +262,12 @@ struct _digit : char_class_base<_digit> { return lexy::code_point(cp).general_category() == lexy::code_point::decimal_number; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_digit::template char_class_match_swar(c); + } }; inline constexpr auto digit = _digit{}; @@ -201,6 +288,12 @@ struct _alnum : char_class_base<_alnum> return lexy::_detail::code_point_has_properties(cp) || lexy::code_point(cp).general_category() == lexy::code_point::decimal_number; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_alnum::template char_class_match_swar(c); + } }; inline constexpr auto alnum = _alnum{}; inline constexpr auto alpha_digit = alnum; @@ -227,6 +320,12 @@ struct _word : char_class_base<_word> return lexy::_detail::code_point_has_properties(cp); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_word::template char_class_match_swar(c); + } }; inline constexpr auto word = _word{}; @@ -252,6 +351,12 @@ struct _graph : char_class_base<_graph> && cp.general_category() != lexy::code_point::unassigned && !_space::char_class_match_cp(cp.value()); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_graph::template char_class_match_swar(c); + } }; inline constexpr auto graph = _graph{}; @@ -273,6 +378,12 @@ struct _print : char_class_base<_print> return !_control::char_class_match_cp(cp) && (_blank::char_class_match_cp(cp) || _graph::char_class_match_cp(cp)); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_print::template char_class_match_swar(c); + } }; inline constexpr auto print = _print{}; @@ -292,6 +403,12 @@ struct _char : char_class_base<_char> { return lexy::code_point(cp).general_category() != lexy::code_point::unassigned; } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_char::template char_class_match_swar(c); + } }; inline constexpr auto character = _char{}; } // namespace lexyd::unicode @@ -314,6 +431,12 @@ struct _xid_start : char_class_base<_xid_start> { return lexy::_detail::code_point_has_properties(cp); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_alpha::template char_class_match_swar(c); + } }; inline constexpr auto xid_start = _xid_start{}; @@ -334,6 +457,12 @@ struct _xid_start_underscore : char_class_base<_xid_start_underscore> // underscore handled as part of ASCII. return lexy::_detail::code_point_has_properties(cp); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_alphau::template char_class_match_swar(c); + } }; inline constexpr auto xid_start_underscore = _xid_start_underscore{}; @@ -354,6 +483,12 @@ struct _xid_continue : char_class_base<_xid_continue> // underscore handled as part of ASCII. return lexy::_detail::code_point_has_properties(cp); } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return ascii::_word::template char_class_match_swar(c); + } }; inline constexpr auto xid_continue = _xid_continue{}; } // namespace lexyd::unicode diff --git a/3rdparty/lexy/include/lexy/dsl/until.hpp b/3rdparty/lexy/include/lexy/dsl/until.hpp index 344c98b0b..626d0b4fd 100644 --- a/3rdparty/lexy/include/lexy/dsl/until.hpp +++ b/3rdparty/lexy/include/lexy/dsl/until.hpp @@ -1,28 +1,54 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_UNTIL_HPP_INCLUDED #define LEXY_DSL_UNTIL_HPP_INCLUDED +#include #include #include namespace lexyd { +struct _nl; + +template +constexpr void _until_swar([[maybe_unused]] Reader& reader) +{ + if constexpr (std::is_same_v // + && lexy::_detail::is_swar_reader) + { + // We use SWAR to skip characters until we have one that is <= 0xF or EOF. + // Then we need to inspect it in more detail. + using char_type = typename Reader::encoding::char_type; + + while (true) + { + auto cur = reader.peek_swar(); + if (lexy::_detail::swar_has_char(cur) + || lexy::_detail::swar_has_char_less(cur)) + break; + reader.bump_swar(); + } + } +} + template struct _until_eof : token_base<_until_eof, unconditional_branch_base> { template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr std::true_type try_parse(Reader reader) { while (true) { + _until_swar(reader); + // Check whether we've reached the end of the input or the condition. // Note that we're checking for EOF before the condition. // This is a potential optimization: as we're accepting EOF anyway, we don't need to @@ -38,7 +64,7 @@ struct _until_eof : token_base<_until_eof, unconditional_branch_base> reader.bump(); } - end = reader.position(); + end = reader.current(); return {}; } }; @@ -50,29 +76,31 @@ struct _until : token_base<_until> template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr bool try_parse(Reader reader) { while (true) { + _until_swar(reader); + // Try to parse the condition. if (lexy::try_match_token(Condition{}, reader)) { // It did match, we're done at that end. - end = reader.position(); + end = reader.current(); return true; } // Check whether we've reached the end of the input. - // We need to do it after checking for condition, as the condition might just accept - // EOF. + // We need to do it after checking for condition, as the condition might just + // accept EOF. if (reader.peek() == Reader::encoding::eof()) { // It did, so we did not succeed. - end = reader.position(); + end = reader.current(); return false; } @@ -89,7 +117,7 @@ struct _until : token_base<_until> // We need to trigger the error `Condition` would. // As such, we try parsing it, which will report an error. - reader.set_position(end); + reader.reset(end); LEXY_ASSERT(reader.peek() == Reader::encoding::eof(), "forgot to set end in try_parse()"); diff --git a/3rdparty/lexy/include/lexy/dsl/whitespace.hpp b/3rdparty/lexy/include/lexy/dsl/whitespace.hpp index fdee9a47f..f80b30dcf 100644 --- a/3rdparty/lexy/include/lexy/dsl/whitespace.hpp +++ b/3rdparty/lexy/include/lexy/dsl/whitespace.hpp @@ -1,9 +1,10 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_DSL_WHITESPACE_HPP_INCLUDED #define LEXY_DSL_WHITESPACE_HPP_INCLUDED +#include #include #include #include @@ -19,40 +20,55 @@ struct _wsr; //=== implementation ===// namespace lexy::_detail { -template +// Parse this production to skip whitespace. +template struct ws_production { + static constexpr auto name = ""; static constexpr auto max_recursion_depth = 0; - static constexpr auto rule = lexy::dsl::loop(Rule{} | lexy::dsl::break_); + static constexpr auto rule = lexy::dsl::loop(WhitespaceRule{} | lexy::dsl::break_); }; +// If the whitespace rule itself uses `dsl::whitespace`, strip it to avoid infinite recursion. +template +struct ws_production> : ws_production +{}; // A special handler for parsing whitespace. -// It only forwards errors to the context and ignores all other events. -template -class whitespace_handler +// It only forwards error events and ignores all others. +template +class ws_handler { public: - constexpr explicit whitespace_handler(Context& context) : _context(&context) {} + constexpr explicit ws_handler(Handler& handler, typename Handler::event_handler& evh) + : _handler(&handler), _event_handler(&evh) + {} + template + constexpr explicit ws_handler(Context& context) + : ws_handler(context.control_block->parse_handler, context.handler) + {} - template - struct event_handler - { - static_assert(_detail::error, - "whitespace rule must not contain `dsl::p` or `dsl::recurse`;" - "use `dsl::inline_` instead"); - }; - template - class event_handler> + class event_handler { public: + template + constexpr event_handler(ws_production) + {} + template + constexpr event_handler(Production) + { + static_assert(_detail::error, + "whitespace rule must not contain `dsl::p` or `dsl::recurse`;" + "use `dsl::inline_` instead"); + } + template - constexpr void on(whitespace_handler& handler, parse_events::error ev, Error&& error) + constexpr void on(ws_handler& handler, parse_events::error ev, const Error& error) { - handler._context->on(ev, LEXY_FWD(error)); + handler._event_handler->on(*handler._handler, ev, error); } template - constexpr int on(whitespace_handler&, Event, const Args&...) + constexpr int on(ws_handler&, Event, const Args&...) { return 0; // an operation_start event returns something } @@ -61,75 +77,96 @@ class whitespace_handler template using value_callback = _detail::void_value_callback; - constexpr bool get_result_void(bool rule_parse_result) && + template + constexpr bool get_result(bool rule_parse_result) && { return rule_parse_result; } + template + constexpr auto real_on(Event ev, Args&&... args) + { + return _event_handler->on(*_handler, ev, LEXY_FWD(args)...); + } + private: - Context* _context; + Handler* _handler; + typename Handler::event_handler* _event_handler; }; +template +ws_handler(Context& context) -> ws_handler; + +template +using ws_result = bool; -template -constexpr bool skip_whitespace(Context& context, Reader& reader) +template +constexpr bool space_is_definitely_whitespace() { - auto begin = reader.position(); - auto result = true; - if constexpr (lexy::is_token_rule) + if constexpr (lexy::is_char_class_rule) + return WhitespaceRule::char_class_ascii().contains[int(' ')]; + else + return false; +} + +template +constexpr auto skip_whitespace(ws_handler&& handler, Reader& reader) +{ + auto begin = reader.position(); + + if constexpr (lexy::is_token_rule) { // Parsing a token repeatedly cannot fail, so we can optimize it. - while (lexy::try_match_token(Rule{}, reader)) - {} + + if constexpr (_detail::is_swar_reader // + && space_is_definitely_whitespace()) + { + while (true) + { + // Skip as many spaces as possible. + using char_type = typename Reader::encoding::char_type; + while (reader.peek_swar() == _detail::swar_fill(char_type(' '))) + reader.bump_swar(); + + // We no longer have a space, skip the entire whitespace rule once. + if (!lexy::try_match_token(WhitespaceRule{}, reader)) + // If that fails, we definitely have no more whitespace. + break; + } + } + else + { + // Without SWAR, we just repeatedly skip the whitespace rule. + while (lexy::try_match_token(WhitespaceRule{}, reader)) + { + } + } + + handler.real_on(lexy::parse_events::token{}, lexy::whitespace_token_kind, begin, + reader.position()); + return std::true_type{}; } - else + else if constexpr (!std::is_void_v) { - // Parse the rule using a special handler that only forwards errors. - using production = ws_production; - result = lexy::do_action(whitespace_handler(context), lexy::no_parse_state, - reader); - } - auto end = reader.position(); + using production = ws_production; - if (result) - { - // Add a whitespace token node. - context.on(lexy::parse_events::token{}, lexy::whitespace_token_kind, begin, end); - return true; + // Parse the production using a special handler that only forwards errors. + auto result = lexy::do_action(LEXY_MOV(handler), + lexy::no_parse_state, reader); + + handler.real_on(lexy::parse_events::token{}, + result ? lexy::whitespace_token_kind : lexy::error_token_kind, begin, + reader.position()); + return result; } else { - context.on(lexy::parse_events::token{}, lexy::error_token_kind, begin, end); - return false; + (void)handler; + (void)reader; + (void)begin; + return std::true_type{}; } } -template -struct manual_ws_parser -{ - // Note that this is not marked force inline. - // Compile-time performance really suffers if that is the case. - // See #84. - template - constexpr static bool parse(Context& context, Reader& reader, Args&&... args) - { - auto result = skip_whitespace(context, reader); - if (!result) - return false; - - return NextParser::parse(context, reader, LEXY_FWD(args)...); - } -}; -template -struct manual_ws_parser, NextParser> : manual_ws_parser -{}; -template -struct manual_ws_parser : NextParser -{}; - -template -using context_whitespace = lexy::production_whitespace; - // Inherit from it in a continuation to disable automatic whitespace skipping. struct disable_whitespace_skipping {}; @@ -143,15 +180,13 @@ struct automatic_ws_parser if (!std::is_base_of_v // && context.control_block->enable_whitespace_skipping) { - // Skip the appropriate whitespace. - using rule = context_whitespace; - return manual_ws_parser::parse(context, reader, LEXY_FWD(args)...); - } - else - { - // Automatic whitespace skipping is disabled. - return NextParser::parse(context, reader, LEXY_FWD(args)...); + using whitespace = lexy::production_whitespace; + if (!skip_whitespace(ws_handler(context), reader)) + return false; } + + return NextParser::parse(context, reader, LEXY_FWD(args)...); } }; } // namespace lexy::_detail @@ -163,7 +198,19 @@ template struct _wsr : rule_base { template - using p = lexy::_detail::manual_ws_parser; + struct p + { + template + constexpr static bool parse(Context& context, Reader& reader, Args&&... args) + { + auto result + = lexy::_detail::skip_whitespace(lexy::_detail::ws_handler(context), reader); + if (!result) + return false; + + return NextParser::parse(context, reader, LEXY_FWD(args)...); + } + }; template friend constexpr auto operator|(_wsr, R r) @@ -213,7 +260,7 @@ struct _wsn : _copy_base constexpr auto try_parse(const ControlBlock* cb, const Reader& reader) { // Note that this can't skip whitespace as there is no way to access the whitespace - // rule. + // rule. We thus don't need to disable anything. return rule.try_parse(cb, reader); } @@ -238,9 +285,11 @@ struct _wsn : _copy_base template LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args) { - if constexpr (std::is_void_v>) + using whitespace = lexy::production_whitespace; + if constexpr (std::is_void_v) { - // No whitespace, just parse the rule. + // No whitespace, just parse the rule normally. return lexy::parser_for::parse(context, reader, LEXY_FWD(args)...); } diff --git a/3rdparty/lexy/include/lexy/encoding.hpp b/3rdparty/lexy/include/lexy/encoding.hpp index f3f95f45e..4529e67a1 100644 --- a/3rdparty/lexy/include/lexy/encoding.hpp +++ b/3rdparty/lexy/include/lexy/encoding.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_ENCODING_HPP_INCLUDED @@ -253,13 +253,35 @@ struct _deduce_encoding }; } // namespace lexy +//=== encoding traits ===// +namespace lexy +{ +template +constexpr auto is_unicode_encoding + = std::is_same_v || std::is_same_v + || std::is_same_v || std::is_same_v + || std::is_same_v; + +template +constexpr auto is_text_encoding + = is_unicode_encoding || std::is_same_v; + +template +constexpr auto is_byte_encoding = std::is_same_v; + +template +constexpr auto is_char_encoding = is_text_encoding || is_byte_encoding; + +template +constexpr auto is_node_encoding = false; +} // namespace lexy + //=== impls ===// namespace lexy::_detail { template -constexpr bool is_compatible_char_type - = std::is_same_v || Encoding::template is_secondary_char_type(); +constexpr bool is_compatible_char_type = std::is_same_v + || Encoding::template is_secondary_char_type(); template using require_secondary_char_type diff --git a/3rdparty/lexy/include/lexy/error.hpp b/3rdparty/lexy/include/lexy/error.hpp index 076999c8c..77a40937d 100644 --- a/3rdparty/lexy/include/lexy/error.hpp +++ b/3rdparty/lexy/include/lexy/error.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_ERROR_HPP_INCLUDED @@ -26,6 +26,13 @@ class error : _pos(begin), _end(end), _msg(msg) {} + template >> + constexpr operator error() const noexcept + { + return error(_pos, _end, _msg); + } + constexpr auto position() const noexcept { return _pos; @@ -35,6 +42,11 @@ class error { return _msg; } + template + constexpr bool is(Tag = {}) const noexcept + { + return _detail::string_view(_msg) == _detail::type_name(); + } constexpr auto begin() const noexcept { @@ -63,6 +75,13 @@ class error : public error typename Reader::iterator end) noexcept : error(begin, end, _detail::type_name()) {} + + template >> + constexpr operator error() const noexcept + { + return error(this->begin(), this->end()); + } }; /// Expected the literal character sequence. @@ -78,6 +97,13 @@ class error : _pos(pos), _str(str), _idx(index), _length(length) {} + template >> + constexpr operator error() const noexcept + { + return error(_pos, _str, _idx, _length); + } + constexpr auto position() const noexcept { return _pos; @@ -122,6 +148,13 @@ class error : _begin(begin), _end(end), _str(str), _length(length) {} + template >> + constexpr operator error() const noexcept + { + return error(_begin, _end, _str, _length); + } + constexpr auto position() const noexcept { return _begin; @@ -164,6 +197,13 @@ class error : _pos(pos), _name(name) {} + template >> + constexpr operator error() const noexcept + { + return error(_pos, _name); + } + constexpr auto position() const noexcept { return _pos; @@ -188,12 +228,9 @@ namespace lexy template using _detect_parent_input = decltype(LEXY_DECLVAL(Input).parent_input()); -template -class error_context; - /// Contains information about the context of an error, production is type-erased. template -class error_context +class error_context { public: constexpr explicit error_context(lexy::production_info production, const Input& input, @@ -227,31 +264,6 @@ class error_context typename input_reader::iterator _pos; const char* _production; }; - -/// Contains information about the context of an error. -template -class error_context : public error_context -{ -public: - constexpr explicit error_context(const Input& input, - typename input_reader::iterator pos) noexcept - : error_context(input, Production{}, pos) - {} - constexpr explicit error_context(Production production, const Input& input, - typename input_reader::iterator pos) noexcept - : error_context(production, input, pos) - {} - - // We override production to make it static and constexpr. - static LEXY_CONSTEVAL const char* production() - { - return production_name(); - } -}; - -template -error_context(production_info, const Input&, typename input_reader::iterator) - -> error_context; } // namespace lexy #endif // LEXY_ERROR_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/grammar.hpp b/3rdparty/lexy/include/lexy/grammar.hpp index db1448ccd..b1986594e 100644 --- a/3rdparty/lexy/include/lexy/grammar.hpp +++ b/3rdparty/lexy/include/lexy/grammar.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_GRAMMAR_HPP_INCLUDED @@ -65,8 +65,17 @@ constexpr auto is_separator = std::is_base_of_v; template constexpr auto is_operation = std::is_base_of_v; + +template +constexpr bool _require_branch_rule = (is_branch_rule && ...); } // namespace lexy +#define LEXY_REQUIRE_BRANCH_RULE(Rule, Name) \ + static_assert(lexy::_require_branch_rule, Name \ + " requires a branch condition." \ + " You may need to use `>>` to specify the condition that is used for dispatch." \ + " See https://lexy.foonathan.net/learn/branching/ for more information.") + //=== predefined_token_kind ===// namespace lexy { @@ -88,7 +97,12 @@ enum predefined_token_kind : std::uint_least16_t _smallest_predefined_token_kind = digits_token_kind, }; -constexpr const char* _kind_name(predefined_token_kind kind) noexcept +template +constexpr const char* token_kind_name(const T&) noexcept +{ + return "token"; +} +constexpr const char* token_kind_name(predefined_token_kind kind) noexcept { switch (kind) { @@ -177,26 +191,21 @@ using _enable_production_or_operation = std::enable_if_t || is_ struct production_info { - const char* name; - bool is_token; - bool is_transparent; + const char* const* id; + const char* name; + bool is_token; + bool is_transparent; template > constexpr production_info(Production) - : name(production_name()), is_token(is_token_production), + : id(_detail::type_id()), name(production_name()), + is_token(is_token_production), is_transparent(is_transparent_production) {} friend constexpr bool operator==(production_info lhs, production_info rhs) { - // We can safely compare pointers, strings are necessarily interned: - // if Production::name exists: same address for all types, - // otherwise we use __PRETTY_FUNCTION__ (or equivalent), which is a function-local static. - // - // This only fails if we have different productions with the same name and the compiler does - // string interning. But as the production name corresponds to the qualified C++ name (by - // default), this is only possible if the user does something weird. - return lhs.name == rhs.name; + return lhs.id == rhs.id; } friend constexpr bool operator!=(production_info lhs, production_info rhs) { @@ -249,6 +258,9 @@ using _detect_value_of = // qualify value_of() (it causes a hard error instead of going to ::value). typename decltype(LEXY_DECLVAL(ParseState&).value_of(Production{}))::return_type; +template +using _detect_value = decltype(Production::value); + template struct _sfinae_sink { @@ -275,6 +287,11 @@ struct _sfinae_sink } }; +template +constexpr bool production_has_value_callback + = lexy::_detail::is_detected<_detect_value_of, ParseState, Production> + || lexy::_detail::is_detected<_detect_value, Production>; + template class production_value_callback { @@ -328,12 +345,14 @@ class production_value_callback template constexpr return_type operator()(Args&&... args) const { - if constexpr (lexy::is_callback_for<_type, Args&&...>) + if constexpr (lexy::is_callback_with_state_for<_type, ParseState, Args&&...> + && !std::is_void_v) { - if constexpr (lexy::is_callback_state<_type, ParseState>) - return _get_value(_state)[*_state](LEXY_FWD(args)...); - else - return _get_value(_state)(LEXY_FWD(args)...); + return _get_value(_state)[*_state](LEXY_FWD(args)...); + } + else if constexpr (lexy::is_callback_for<_type, Args&&...>) + { + return _get_value(_state)(LEXY_FWD(args)...); } else if constexpr ((lexy::is_sink<_type> // || lexy::is_sink<_type, std::add_lvalue_reference_t>) // @@ -360,4 +379,3 @@ class production_value_callback } // namespace lexy #endif // LEXY_GRAMMAR_HPP_INCLUDED - diff --git a/3rdparty/lexy/include/lexy/input/argv_input.hpp b/3rdparty/lexy/include/lexy/input/argv_input.hpp index 423121c67..f4b4f1d70 100644 --- a/3rdparty/lexy/include/lexy/input/argv_input.hpp +++ b/3rdparty/lexy/include/lexy/input/argv_input.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_INPUT_ARGV_INPUT_HPP_INCLUDED @@ -121,9 +121,9 @@ class argv_input public: using encoding = Encoding; using char_type = typename encoding::char_type; - static_assert( - std::is_same_v || Encoding::template is_secondary_char_type(), - "invalid encoding for argv"); + static_assert(std::is_same_v + || Encoding::template is_secondary_char_type(), + "invalid encoding for argv"); //=== constructors ===// constexpr argv_input() = default; @@ -147,7 +147,7 @@ class argv_input argv_iterator _begin, _end; }; -argv_input(int argc, char* argv[])->argv_input<>; +argv_input(int argc, char* argv[]) -> argv_input<>; } // namespace lexy namespace lexy @@ -158,8 +158,8 @@ using argv_lexeme = lexeme_for>; template using argv_error = error_for, Tag>; -template -using argv_error_context = error_context>; +template +using argv_error_context = error_context>; } // namespace lexy namespace lexyd @@ -169,9 +169,9 @@ struct _argvsep : token_base<_argvsep> template struct tp { - typename Reader::iterator end; + typename Reader::marker end; - constexpr explicit tp(const Reader& reader) : end(reader.position()) {} + constexpr explicit tp(const Reader& reader) : end(reader.current()) {} constexpr auto try_parse([[maybe_unused]] Reader reader) { @@ -182,7 +182,7 @@ struct _argvsep : token_base<_argvsep> return false; reader.bump(); - end = reader.position(); + end = reader.current(); return true; } else diff --git a/3rdparty/lexy/include/lexy/input/base.hpp b/3rdparty/lexy/include/lexy/input/base.hpp index 2990e3884..2bc260e2f 100644 --- a/3rdparty/lexy/include/lexy/input/base.hpp +++ b/3rdparty/lexy/include/lexy/input/base.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_INPUT_BASE_HPP_INCLUDED @@ -18,6 +18,16 @@ class _rr using encoding = Encoding; using iterator = Iterator; + struct marker + { + iterator _it; + + constexpr iterator position() const noexcept + { + return _it; + } + }; + constexpr explicit _rr(Iterator begin, Sentinel end) noexcept : _cur(begin), _end(end) { LEXY_PRECONDITION(lexy::_detail::precedes(begin, end)); @@ -42,10 +52,14 @@ class _rr return _cur; } - constexpr void set_position(iterator new_pos) noexcept + constexpr marker current() const noexcept + { + return {_cur}; + } + constexpr void reset(marker m) noexcept { - LEXY_PRECONDITION(lexy::_detail::precedes(new_pos, _end)); - _cur = new_pos; + LEXY_PRECONDITION(lexy::_detail::precedes(m._it, _end)); + _cur = m._it; } private: diff --git a/3rdparty/lexy/include/lexy/input/buffer.hpp b/3rdparty/lexy/include/lexy/input/buffer.hpp index 590ac81fa..450ecf254 100644 --- a/3rdparty/lexy/include/lexy/input/buffer.hpp +++ b/3rdparty/lexy/include/lexy/input/buffer.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_INPUT_BUFFER_HPP_INCLUDED @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -14,12 +15,22 @@ namespace lexy { // The reader used by the buffer if it can use a sentinel. template -class _br +class _br : public _detail::swar_reader_base<_br> { public: using encoding = Encoding; using iterator = const typename Encoding::char_type*; + struct marker + { + iterator _it; + + constexpr iterator position() const noexcept + { + return _it; + } + }; + explicit _br(iterator begin) noexcept : _cur(begin) {} auto peek() const noexcept @@ -38,9 +49,13 @@ class _br return _cur; } - void set_position(iterator new_pos) noexcept + marker current() const noexcept + { + return {_cur}; + } + void reset(marker m) noexcept { - _cur = new_pos; + _cur = m._it; } private: @@ -79,13 +94,14 @@ namespace lexy template class buffer { + static_assert(lexy::is_char_encoding); static constexpr auto _has_sentinel = std::is_same_v; public: using encoding = Encoding; using char_type = typename encoding::char_type; - static_assert(std::is_trivial_v); + static_assert(std::is_trivially_copyable_v); //=== constructors ===// /// Allows the creation of an uninitialized buffer that is then filled by the user. @@ -118,6 +134,17 @@ class buffer buffer _buffer; }; + static buffer adopt(const char_type* data, std::size_t size, + MemoryResource* resource = _detail::get_memory_resource()) + { + buffer result(resource); + // We can cast away the const-ness, since we require that `data` came from a buffer + // origionally, where it wasn't const. + result._data = const_cast(data); + result._size = size; + return result; + } + constexpr buffer() noexcept : buffer(_detail::get_memory_resource()) {} constexpr explicit buffer(MemoryResource* resource) noexcept @@ -129,7 +156,8 @@ class buffer : _resource(resource), _size(size) { _data = allocate(size); - std::memcpy(_data, data, size * sizeof(char_type)); + if (size > 0) + std::memcpy(_data, data, size * sizeof(char_type)); } explicit buffer(const char_type* begin, const char_type* end, MemoryResource* resource = _detail::get_memory_resource()) @@ -176,7 +204,9 @@ class buffer return; if constexpr (_has_sentinel) - _resource->deallocate(_data, (_size + 1) * sizeof(char_type), alignof(char_type)); + _resource->deallocate(_data, + _detail::round_size_for_swar(_size + 1) * sizeof(char_type), + alignof(char_type)); else _resource->deallocate(_data, _size * sizeof(char_type), alignof(char_type)); } @@ -223,6 +253,14 @@ class buffer return _size; } + const char_type* release() && noexcept + { + auto result = _data; + _data = nullptr; + _size = 0; + return result; + } + //=== input ===// auto reader() const& noexcept { @@ -236,13 +274,21 @@ class buffer char_type* allocate(std::size_t size) const { if constexpr (_has_sentinel) - ++size; + { + auto mem_size = _detail::round_size_for_swar(size + 1); + auto memory = static_cast( + _resource->allocate(mem_size * sizeof(char_type), alignof(char_type))); - auto memory = static_cast( - _resource->allocate(size * sizeof(char_type), alignof(char_type))); - if constexpr (_has_sentinel) - memory[size - 1] = encoding::eof(); - return memory; + for (auto ptr = memory + size; ptr != memory + mem_size; ++ptr) + *ptr = encoding::eof(); + + return memory; + } + else + { + return static_cast( + _resource->allocate(size * sizeof(char_type), alignof(char_type))); + } } LEXY_EMPTY_MEMBER _detail::memory_resource_ptr _resource; @@ -406,6 +452,46 @@ struct _make_buffer template constexpr auto make_buffer_from_raw = _make_buffer{}; +//=== make_buffer_from_input ===// +template +using _detect_input_data = decltype(LEXY_DECLVAL(Input&).data()); + +template +constexpr auto make_buffer_from_input(const Input& input, + MemoryResource* resource + = _detail::get_memory_resource()) + -> buffer::encoding, MemoryResource> +{ + using type = buffer::encoding, MemoryResource>; + if constexpr (_detail::is_detected<_detect_input_data, Input>) + { + return type(input.data(), input.size(), resource); + } + else + { + auto reader = input.reader(); + auto begin = reader.position(); + while (reader.peek() != input_reader::encoding::eof()) + reader.bump(); + auto end = reader.position(); + + if constexpr (std::is_pointer_v) + { + return type(begin, end, resource); + } + else + { + auto size = _detail::range_size(begin, end); + typename type::builder builder(size, resource); + auto dest = builder.data(); + for (auto cur = begin; cur != end; ++cur) + *dest++ = *cur; // NOLINT: clang-analyzer thinks this might access zero memory for + // some reason?! + return LEXY_MOV(builder).finish(); + } + } +} + //=== convenience typedefs ===// template using buffer_lexeme = lexeme_for>; @@ -413,8 +499,8 @@ using buffer_lexeme = lexeme_for>; template using buffer_error = error_for, Tag>; -template -using buffer_error_context = error_context>; +template +using buffer_error_context = error_context>; } // namespace lexy #endif // LEXY_INPUT_BUFFER_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/input/file.hpp b/3rdparty/lexy/include/lexy/input/file.hpp index 1a80fbebf..8c4a2d57d 100644 --- a/3rdparty/lexy/include/lexy/input/file.hpp +++ b/3rdparty/lexy/include/lexy/input/file.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_INPUT_FILE_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/input/parse_tree_input.hpp b/3rdparty/lexy/include/lexy/input/parse_tree_input.hpp new file mode 100644 index 000000000..fdc7572f9 --- /dev/null +++ b/3rdparty/lexy/include/lexy/input/parse_tree_input.hpp @@ -0,0 +1,184 @@ +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors +// SPDX-License-Identifier: BSL-1.0 + +#ifndef LEXY_INPUT_PARSE_TREE_INPUT_HPP_INCLUDED +#define LEXY_INPUT_PARSE_TREE_INPUT_HPP_INCLUDED + +#include +#include +#include +#include + +#if !LEXY_EXPERIMENTAL +# error "lexy::parse_tree_input is experimental" +#endif + +namespace lexy +{ +template +struct parse_tree_input_traits; + +struct _parse_tree_eof // not real EOF, just no more siblings +{ + template + friend constexpr bool operator==(const Node& node, _parse_tree_eof) noexcept + { + return parse_tree_input_traits::is_null(node); + } + template + friend constexpr bool operator==(_parse_tree_eof, const Node& node) noexcept + { + return parse_tree_input_traits::is_null(node); + } + + template + friend constexpr bool operator!=(const Node& node, _parse_tree_eof) noexcept + { + return !parse_tree_input_traits::is_null(node); + } + template + friend constexpr bool operator!=(_parse_tree_eof, const Node& node) noexcept + { + return !parse_tree_input_traits::is_null(node); + } +}; + +template +class parse_tree_encoding +{ + using _traits = parse_tree_input_traits; + +public: + using char_encoding = typename _traits::char_encoding; + using char_type = typename char_encoding::char_type; + using value_type = Node; + + static LEXY_CONSTEVAL auto eof() + { + return _parse_tree_eof{}; + } + + template + static bool match(const Node& node, const NodeKind& node_kind) + { + return _traits::has_kind(node, node_kind); + } +}; +template +constexpr auto is_node_encoding> = true; + +template +class _ptr // parse tree reader +{ + using _traits = parse_tree_input_traits; + +public: + using encoding = parse_tree_encoding; + using iterator = typename _traits::iterator; + + struct marker + { + Node _parent = _traits::null(); + Node _cur = _traits::null(); + + constexpr iterator position() const noexcept + { + return _cur == _parse_tree_eof{} ? _traits::position_end(_parent) + : _traits::position_begin(_cur); + } + }; + + constexpr explicit _ptr(const Node& root) noexcept + : _parent(root), _cur(_traits::first_child(root)) + {} + + constexpr _ptr child_reader() const& noexcept + { + return _ptr(_cur); + } + constexpr auto lexeme_reader() const& noexcept + { + auto lexeme = _traits::lexeme(_cur); + return _range_reader(lexeme.begin(), lexeme.end()); + } + + constexpr const Node& peek() const noexcept + { + return _cur; + } + + constexpr void bump() noexcept + { + LEXY_PRECONDITION(_cur != _parse_tree_eof{}); + _cur = _traits::sibling(_cur); + } + + constexpr marker current() const noexcept + { + return {_parent, _cur}; + } + constexpr void reset(marker m) noexcept + { + _cur = m._cur; + } + + constexpr iterator position() const noexcept + { + return current().position(); + } + +private: + Node _parent; + Node _cur; +}; + +template +class parse_tree_input +{ +public: + using encoding = parse_tree_encoding; + using value_type = Node; + + //=== constructors ===// + constexpr parse_tree_input() noexcept : _root(nullptr) {} + + constexpr explicit parse_tree_input(Node root) noexcept : _root(LEXY_MOV(root)) {} + + template >> + constexpr explicit parse_tree_input(const ParseTree& tree) noexcept : _root(tree.root()) + {} + + //=== access ===// + constexpr const Node& root() const noexcept + { + return _root; + } + + //=== reader ===// + constexpr auto reader() const& noexcept + { + return _ptr(_root); + } + +private: + Node _root; +}; + +template +parse_tree_input(const ParseTree&) + -> parse_tree_input; + +//=== convenience typedefs ===// +template +using parse_tree_lexeme = lexeme_for>; + +template +using parse_tree_error = error_for, Tag>; + +template +using parse_tree_error_context = error_context>; +} // namespace lexy + +#endif // LEXY_INPUT_PARSE_TREE_INPUT_HPP_INCLUDED + diff --git a/3rdparty/lexy/include/lexy/input/range_input.hpp b/3rdparty/lexy/include/lexy/input/range_input.hpp index 7c214909a..c32c0d3a7 100644 --- a/3rdparty/lexy/include/lexy/input/range_input.hpp +++ b/3rdparty/lexy/include/lexy/input/range_input.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_INPUT_RANGE_INPUT_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/input/string_input.hpp b/3rdparty/lexy/include/lexy/input/string_input.hpp index 6b57e010c..a59a387da 100644 --- a/3rdparty/lexy/include/lexy/input/string_input.hpp +++ b/3rdparty/lexy/include/lexy/input/string_input.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_INPUT_STRING_INPUT_HPP_INCLUDED @@ -17,6 +17,8 @@ using _string_view_char_type = LEXY_DECAY_DECLTYPE(*LEXY_DECLVAL(View).data()); template class string_input { + static_assert(lexy::is_char_encoding); + public: using encoding = Encoding; using char_type = typename encoding::char_type; @@ -105,8 +107,8 @@ using string_lexeme = lexeme_for>; template using string_error = error_for, Tag>; -template -using string_error_context = error_context>; +template +using string_error_context = error_context>; } // namespace lexy #endif // LEXY_INPUT_STRING_INPUT_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/input_location.hpp b/3rdparty/lexy/include/lexy/input_location.hpp index 7e74b9dab..983d2aa02 100644 --- a/3rdparty/lexy/include/lexy/input_location.hpp +++ b/3rdparty/lexy/include/lexy/input_location.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_INPUT_LOCATION_HPP_INCLUDED @@ -16,18 +16,18 @@ namespace lexy template struct input_location_anchor { - using iterator = typename lexy::input_reader::iterator; + using marker = typename lexy::input_reader::marker; constexpr explicit input_location_anchor(const Input& input) - : _line_begin(input.reader().position()), _line_nr(1) + : _line_begin(input.reader().current()), _line_nr(1) {} // implementation detail - constexpr explicit input_location_anchor(iterator line_begin, unsigned line_nr) + constexpr explicit input_location_anchor(marker line_begin, unsigned line_nr) : _line_begin(line_begin), _line_nr(line_nr) {} - iterator _line_begin; + marker _line_begin; unsigned _line_nr; }; } // namespace lexy @@ -42,12 +42,14 @@ class code_unit_location_counting template constexpr bool try_match_newline(Reader& reader) { + static_assert(lexy::is_char_encoding); return lexy::try_match_token(lexy::dsl::newline, reader); } template constexpr void match_column(Reader& reader) { + static_assert(lexy::is_char_encoding); reader.bump(); } }; @@ -59,12 +61,14 @@ class code_point_location_counting template constexpr bool try_match_newline(Reader& reader) { + static_assert(lexy::is_char_encoding); return lexy::try_match_token(lexy::dsl::newline, reader); } template constexpr void match_column(Reader& reader) { + static_assert(lexy::is_char_encoding); if (!lexy::try_match_token(lexy::dsl::code_point, reader)) reader.bump(); } @@ -78,6 +82,7 @@ class byte_location_counting template constexpr bool try_match_newline(Reader& reader) { + static_assert(lexy::is_byte_encoding); LEXY_PRECONDITION(_cur_index <= LineWidth - 1); if (_cur_index == LineWidth - 1) { @@ -98,7 +103,7 @@ class byte_location_counting template constexpr void match_column(Reader& reader) { - static_assert(std::is_same_v); + static_assert(lexy::is_byte_encoding); reader.bump(); ++_cur_index; @@ -109,9 +114,20 @@ class byte_location_counting }; template -using _default_location_counting = std::conditional_t< - std::is_same_v::encoding, lexy::byte_encoding>, - byte_location_counting<>, code_unit_location_counting>; +auto _compute_default_location_counting() +{ + using encoding = typename lexy::input_reader::encoding; + if constexpr (lexy::is_byte_encoding) + return byte_location_counting{}; + else if constexpr (lexy::is_char_encoding) + return code_unit_location_counting{}; + else + static_assert(_detail::error, + "input encoding does not have a default location counting policy"); +} + +template +using _default_location_counting = decltype(_compute_default_location_counting()); } // namespace lexy //=== input_location ===// @@ -122,8 +138,14 @@ template > class input_location { using iterator = typename lexy::input_reader::iterator; + using marker = typename lexy::input_reader::marker; public: + constexpr explicit input_location(const Input& input) + : _line_begin(input.reader().current()), _column_begin(_line_begin.position()), _line_nr(1), + _column_nr(1) + {} + /// The closest previous anchor. constexpr input_location_anchor anchor() const { @@ -158,7 +180,7 @@ class input_location { if (lhs._line_nr != rhs._line_nr) return lhs._line_nr < rhs._line_nr; - return lhs._column_nr < rhs._colum_nr; + return lhs._column_nr < rhs._column_nr; } friend constexpr bool operator<=(const input_location& lhs, const input_location& rhs) { @@ -174,12 +196,13 @@ class input_location } private: - constexpr input_location(iterator line_begin, unsigned line_nr, iterator column_begin, + constexpr input_location(marker line_begin, unsigned line_nr, iterator column_begin, unsigned column_nr) : _line_begin(line_begin), _column_begin(column_begin), _line_nr(line_nr), _column_nr(column_nr) {} - iterator _line_begin, _column_begin; + marker _line_begin; + iterator _column_begin; unsigned _line_nr, _column_nr; template @@ -197,7 +220,7 @@ constexpr auto get_input_location(const Input& i -> input_location { auto reader = input.reader(); - reader.set_position(anchor._line_begin); + reader.reset(anchor._line_begin); auto line_begin = anchor._line_begin; auto line_nr = anchor._line_nr; @@ -223,8 +246,10 @@ constexpr auto get_input_location(const Input& i else if (counting.try_match_newline(reader)) { // [column_begin, newline_end) covers the newline. - auto newline_end = reader.position(); - if (lexy::_detail::min_range_end(column_begin, newline_end, position) != newline_end) + auto newline_end = reader.current(); + if (lexy::_detail::min_range_end(column_begin.position(), newline_end.position(), + position) + != newline_end.position()) break; // Advance to the next line. @@ -238,8 +263,10 @@ constexpr auto get_input_location(const Input& i counting.match_column(reader); // [column_begin, column_end) covers the column. - auto column_end = reader.position(); - if (lexy::_detail::min_range_end(column_begin, column_end, position) != column_end) + auto column_end = reader.current(); + if (lexy::_detail::min_range_end(column_begin.position(), column_end.position(), + position) + != column_end.position()) break; // Advance to the next column. @@ -248,7 +275,7 @@ constexpr auto get_input_location(const Input& i } } - return {line_begin, line_nr, column_begin, column_nr}; + return {line_begin, line_nr, column_begin.position(), column_nr}; } template @@ -277,11 +304,11 @@ constexpr auto get_input_location(const Input& i namespace lexy::_detail { template -constexpr auto get_input_line(const Input& input, - typename lexy::input_reader::iterator line_begin) +constexpr auto get_input_line(const Input& input, + typename lexy::input_reader::marker line_begin) { auto reader = input.reader(); - reader.set_position(line_begin); + reader.reset(line_begin); auto line_end = reader.position(); for (Counting counting; @@ -297,7 +324,7 @@ constexpr auto get_input_line(const Input& input lexy::lexeme_for line; lexy::lexeme_for newline; }; - return result_t{{line_begin, line_end}, {line_end, newline_end}}; + return result_t{{line_begin.position(), line_end}, {line_end, newline_end}}; } // Advances the iterator to the beginning of the next code point. diff --git a/3rdparty/lexy/include/lexy/lexeme.hpp b/3rdparty/lexy/include/lexy/lexeme.hpp index 2fa5e4ab3..69d609854 100644 --- a/3rdparty/lexy/include/lexy/lexeme.hpp +++ b/3rdparty/lexy/include/lexy/lexeme.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_LEXEME_HPP_INCLUDED @@ -17,8 +17,8 @@ class lexeme { public: using encoding = typename Reader::encoding; - using char_type = typename encoding::char_type; using iterator = typename Reader::iterator; + using char_type = LEXY_DECAY_DECLTYPE(*LEXY_DECLVAL(iterator&)); constexpr lexeme() noexcept : _begin(), _end() {} constexpr lexeme(iterator begin, iterator end) noexcept : _begin(begin), _end(end) {} @@ -30,6 +30,13 @@ class lexeme : _begin(begin), _end(reader.position()) {} + template >> + constexpr operator lexeme() const noexcept + { + return lexeme(this->begin(), this->end()); + } + constexpr bool empty() const noexcept { return _begin == _end; diff --git a/3rdparty/lexy/include/lexy/parse_tree.hpp b/3rdparty/lexy/include/lexy/parse_tree.hpp index 63a47d57e..b82efb1bd 100644 --- a/3rdparty/lexy/include/lexy/parse_tree.hpp +++ b/3rdparty/lexy/include/lexy/parse_tree.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_PARSE_TREE_HPP_INCLUDED @@ -11,6 +11,12 @@ #include #include +namespace lexy +{ +template +struct parse_tree_input_traits; +} + //=== internal: pt_node ===// namespace lexy::_detail { @@ -125,7 +131,8 @@ struct pt_node_token : pt_node { if constexpr (_optimize_end) { - static_assert(sizeof(pt_node_token) == 3 * sizeof(void*)); + static_assert(!std::is_pointer_v + || sizeof(pt_node_token) == 3 * sizeof(void*)); auto size = std::size_t(end - begin); LEXY_PRECONDITION(size <= UINT_LEAST32_MAX); @@ -133,7 +140,8 @@ struct pt_node_token : pt_node } else { - static_assert(sizeof(pt_node_token) <= 4 * sizeof(void*)); + static_assert(!std::is_pointer_v + || sizeof(pt_node_token) <= 4 * sizeof(void*)); end_impl = end; } @@ -145,13 +153,13 @@ struct pt_node_production : pt_node { static constexpr std::size_t child_count_bits = sizeof(std::size_t) * CHAR_BIT - 2; - const char* name; - std::size_t child_count : child_count_bits; - std::size_t token_production : 1; - std::size_t first_child_adjacent : 1; + const char* const* id; + std::size_t child_count : child_count_bits; + std::size_t token_production : 1; + std::size_t first_child_adjacent : 1; explicit pt_node_production(production_info info) noexcept - : pt_node(pt_node::type_production), name(info.name), child_count(0), + : pt_node(pt_node::type_production), id(info.id), child_count(0), token_production(info.is_token), first_child_adjacent(true) { static_assert(sizeof(pt_node_production) == 3 * sizeof(void*)); @@ -327,9 +335,16 @@ class pt_buffer //=== parse_tree ===// namespace lexy { +template +class _pt_node_kind; +template +class _pt_node; + template class parse_tree { + static_assert(lexy::is_char_encoding); + public: //=== construction ===// class builder; @@ -363,8 +378,8 @@ class parse_tree } //=== node access ===// - class node; - class node_kind; + using node_kind = _pt_node_kind; + using node = _pt_node; node root() const noexcept { @@ -390,7 +405,11 @@ class parse_tree //=== remaining input ===// lexy::lexeme remaining_input() const noexcept { - return _remaining_input; + if (empty()) + return {}; + + auto token = _root->next_node()->as_token(); + return {token->begin, token->end()}; } private: @@ -398,7 +417,6 @@ class parse_tree _detail::pt_node_production* _root; std::size_t _size; std::size_t _depth; - lexy::lexeme _remaining_input; }; template @@ -542,12 +560,29 @@ class parse_tree::builder } explicit builder(production_info production) : builder(parse_tree(), production) {} - parse_tree&& finish(lexy::lexeme remaining_input = {}) && + [[deprecated("Pass the remaining input, or `input.end()` if there is none.")]] parse_tree&& + finish() && + { + return LEXY_MOV(*this).finish(lexy::lexeme()); + } + parse_tree&& finish(typename Reader::iterator end) && + { + return LEXY_MOV(*this).finish({end, end}); + } + parse_tree&& finish(lexy::lexeme remaining_input) && { LEXY_PRECONDITION(_cur.prod == _result._root); + _cur.insert_children_into(_cur.prod); _cur.update_size_depth(_result._size, _result._depth); - _result._remaining_input = remaining_input; + + _result._buffer.reserve(sizeof(_detail::pt_node_token)); + auto node = _result._buffer + .template allocate<_detail::pt_node_token>(lexy::eof_token_kind, + remaining_input.begin(), + remaining_input.end()); + _result._root->set_sibling(node); + return LEXY_MOV(_result); } @@ -591,7 +626,7 @@ class parse_tree::builder void cancel_production(marker&& m) { - LEXY_PRECONDITION(_cur.prod); + LEXY_PRECONDITION(_cur.prod || m.prod == _cur.prod); if (_cur.prod == m.prod) // We're backtracking a transparent production, do nothing. return; @@ -712,13 +747,19 @@ class parse_tree::builder } } + //=== accessors ===// + std::size_t current_child_count() const noexcept + { + return _cur.child_count; + } + private: parse_tree _result; marker _cur; }; -template -class parse_tree::node_kind +template +class _pt_node_kind { public: bool is_token() const noexcept @@ -732,8 +773,10 @@ class parse_tree::node_kind bool is_root() const noexcept { - // Root node has no next node. - return _ptr->next_node() == nullptr; + // Root node has a next node (the remaining input node) which has no next node. + // We assume that _ptr is never the remaining input node, so we know that we have a next + // node. + return _ptr->next_node()->next_node() == nullptr; } bool is_token_production() const noexcept { @@ -743,7 +786,7 @@ class parse_tree::node_kind const char* name() const noexcept { if (auto prod = _ptr->as_production()) - return prod->name; + return *prod->id; else if (auto token = _ptr->as_token()) return token_kind::from_raw(token->kind).name(); else @@ -753,67 +796,65 @@ class parse_tree::node_kind } } - friend bool operator==(node_kind lhs, node_kind rhs) + friend bool operator==(_pt_node_kind lhs, _pt_node_kind rhs) { if (lhs.is_token() && rhs.is_token()) return lhs._ptr->as_token()->kind == rhs._ptr->as_token()->kind; else - // See the `operator==` for productions for rationale why this works. - return lhs._ptr->as_production()->name == rhs._ptr->as_production()->name; + return lhs._ptr->as_production()->id == rhs._ptr->as_production()->id; } - friend bool operator!=(node_kind lhs, node_kind rhs) + friend bool operator!=(_pt_node_kind lhs, _pt_node_kind rhs) { return !(lhs == rhs); } - friend bool operator==(node_kind nk, token_kind tk) + friend bool operator==(_pt_node_kind nk, token_kind tk) { if (auto token = nk._ptr->as_token()) return token_kind::from_raw(token->kind) == tk; else return false; } - friend bool operator==(token_kind tk, node_kind nk) + friend bool operator==(token_kind tk, _pt_node_kind nk) { return nk == tk; } - friend bool operator!=(node_kind nk, token_kind tk) + friend bool operator!=(_pt_node_kind nk, token_kind tk) { return !(nk == tk); } - friend bool operator!=(token_kind tk, node_kind nk) + friend bool operator!=(token_kind tk, _pt_node_kind nk) { return !(nk == tk); } - friend bool operator==(node_kind nk, production_info info) + friend bool operator==(_pt_node_kind nk, production_info info) { - // Just like `production_info::operator==`, we can compare strings. - return nk.is_production() && nk._ptr->as_production()->name == info.name; + return nk.is_production() && nk._ptr->as_production()->id == info.id; } - friend bool operator==(production_info info, node_kind nk) + friend bool operator==(production_info info, _pt_node_kind nk) { return nk == info; } - friend bool operator!=(node_kind nk, production_info info) + friend bool operator!=(_pt_node_kind nk, production_info info) { return !(nk == info); } - friend bool operator!=(production_info info, node_kind nk) + friend bool operator!=(production_info info, _pt_node_kind nk) { return !(nk == info); } private: - explicit node_kind(_detail::pt_node* ptr) : _ptr(ptr) {} + explicit _pt_node_kind(_detail::pt_node* ptr) : _ptr(ptr) {} _detail::pt_node* _ptr; - friend parse_tree::node; + friend _pt_node; }; -template -class parse_tree::node +template +class _pt_node { public: void* address() const noexcept @@ -823,7 +864,7 @@ class parse_tree::node auto kind() const noexcept { - return node_kind(_ptr); + return _pt_node_kind(_ptr); } auto parent() const noexcept @@ -836,20 +877,20 @@ class parse_tree::node auto cur = _ptr; while (cur->next_role() == _detail::pt_node::role_sibling) cur = cur->next_node(); - return node(cur->next_node()); + return _pt_node(cur->next_node()); } class children_range { public: - class iterator : public _detail::forward_iterator_base + class iterator : public _detail::forward_iterator_base { public: iterator() noexcept : _cur(nullptr) {} - node deref() const noexcept + auto deref() const noexcept { - return node(_cur); + return _pt_node(_cur); } void increment() noexcept @@ -905,7 +946,7 @@ class parse_tree::node _detail::pt_node* _node; - friend node; + friend _pt_node; }; auto children() const noexcept @@ -916,14 +957,14 @@ class parse_tree::node class sibling_range { public: - class iterator : public _detail::forward_iterator_base + class iterator : public _detail::forward_iterator_base { public: iterator() noexcept : _cur() {} - node deref() const noexcept + auto deref() const noexcept { - return node(_cur); + return _pt_node(_cur); } void increment() noexcept @@ -971,7 +1012,7 @@ class parse_tree::node _detail::pt_node* _node; - friend node; + friend _pt_node; }; auto siblings() const noexcept @@ -985,6 +1026,19 @@ class parse_tree::node return _ptr->next_role() == _detail::pt_node::role_parent; } + auto position() const noexcept -> typename Reader::iterator + { + // Find the first descendant that is a token. + auto cur = _ptr; + while (cur->type() == _detail::pt_node::type_production) + { + cur = cur->as_production()->first_child(); + LEXY_PRECONDITION(cur); + } + + return cur->as_token()->begin; + } + auto lexeme() const noexcept { if (auto token = _ptr->as_token()) @@ -993,6 +1047,28 @@ class parse_tree::node return lexy::lexeme(); } + auto covering_lexeme() const noexcept + { + if (auto token = _ptr->as_token()) + return lexy::lexeme(token->begin, token->end()); + + auto begin = position(); + + auto sibling = _ptr; + while (true) + { + auto next_role = sibling->next_role(); + sibling = sibling->next_node(); + // If we went to parent, we need to continue finding siblings. + if (next_role == _detail::pt_node::role_sibling) + break; + } + auto end = _pt_node(sibling).position(); + + LEXY_PRECONDITION(begin == end || end != typename Reader::iterator()); + return lexy::lexeme(begin, end); + } + auto token() const noexcept { LEXY_PRECONDITION(kind().is_token()); @@ -1002,21 +1078,22 @@ class parse_tree::node return lexy::token(kind, token->begin, token->end()); } - friend bool operator==(node lhs, node rhs) noexcept + friend bool operator==(_pt_node lhs, _pt_node rhs) noexcept { return lhs._ptr == rhs._ptr; } - friend bool operator!=(node lhs, node rhs) noexcept + friend bool operator!=(_pt_node lhs, _pt_node rhs) noexcept { return lhs._ptr != rhs._ptr; } private: - explicit node(_detail::pt_node* ptr) noexcept : _ptr(ptr) {} + explicit _pt_node(_detail::pt_node* ptr) noexcept : _ptr(ptr) {} _detail::pt_node* _ptr; - friend parse_tree; + friend parse_tree; + friend parse_tree_input_traits<_pt_node>; }; enum class traverse_event @@ -1145,5 +1222,70 @@ class parse_tree::traverse_range }; } // namespace lexy +#if LEXY_EXPERIMENTAL +namespace lexy +{ +template +struct parse_tree_input_traits<_pt_node> +{ + using _node = _pt_node; + + using char_encoding = typename Reader::encoding; + + static bool is_null(_node cur) noexcept + { + return cur._ptr == nullptr; + } + + static _node null() noexcept + { + return _node(nullptr); + } + + static _node first_child(_node cur) noexcept + { + LEXY_PRECONDITION(!is_null(cur)); + if (auto prod = cur._ptr->as_production()) + return _node(prod->first_child()); + else + return _node(nullptr); + } + + static _node sibling(_node cur) noexcept + { + LEXY_PRECONDITION(!is_null(cur)); + return cur._ptr->next_role() == _detail::pt_node::role_sibling + ? _node(cur._ptr->next_node()) + : _node(nullptr); + } + + template + static bool has_kind(_node cur, const Kind& kind) noexcept + { + return !is_null(cur) && cur.kind() == kind; + } + + using iterator = typename Reader::iterator; + + static iterator position_begin(_node cur) noexcept + { + LEXY_PRECONDITION(!is_null(cur)); + return cur.position(); + } + static iterator position_end(_node cur) noexcept + { + LEXY_PRECONDITION(!is_null(cur)); + return cur.covering_lexeme().end(); + } + + static auto lexeme(_node cur) noexcept + { + LEXY_PRECONDITION(!is_null(cur)); + return cur.lexeme(); + } +}; +} // namespace lexy +#endif + #endif // LEXY_PARSE_TREE_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy/token.hpp b/3rdparty/lexy/include/lexy/token.hpp index 6c69208b7..a7b1442e5 100644 --- a/3rdparty/lexy/include/lexy/token.hpp +++ b/3rdparty/lexy/include/lexy/token.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_TOKEN_HPP_INCLUDED @@ -88,9 +88,6 @@ inline constexpr auto token_kind_map_for = token_kind_map; namespace lexy { -template -using _detect_token_kind_name = decltype(token_kind_name(TokenKind{})); - template constexpr auto _has_special_token_kind = [] { using kind = LEXY_DECAY_DECLTYPE(lexy::token_kind_of); @@ -174,12 +171,13 @@ class token_kind constexpr const char* name() const noexcept { if (is_predefined()) - return _kind_name(static_cast(_value)); - else if constexpr (lexy::_detail::is_detected<_detect_token_kind_name, TokenKind>) - return token_kind_name(get()); // ADL + return token_kind_name(static_cast(_value)); else - // We only have a generic name. - return "token"; + return token_kind_name(get()); // ADL + } + friend constexpr const char* token_kind_name(token_kind kind) noexcept + { + return kind.name(); } constexpr _underlying_type get() const noexcept diff --git a/3rdparty/lexy/include/lexy/visualize.hpp b/3rdparty/lexy/include/lexy/visualize.hpp index d198a6220..42b3fb0be 100644 --- a/3rdparty/lexy/include/lexy/visualize.hpp +++ b/3rdparty/lexy/include/lexy/visualize.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_VISUALIZE_HPP_INCLUDED @@ -363,32 +363,7 @@ OutputIt visualize_to(OutputIt out, lexy::lexeme lexeme, }; using encoding = typename Reader::encoding; - if constexpr (std::is_same_v // - || std::is_same_v) - { - auto count = 0u; - for (char c : lexeme) - { - // If the character is in fact ASCII, visualize the code point. - // Otherwise, visualize as byte. - if (lexy::_detail::is_ascii(c)) - out = visualize_to(out, lexy::code_point(static_cast(c)), opts); - else - out = write_escaped_byte(out, static_cast(c)); - - ++count; - if (count == opts.max_lexeme_width) - { - out = _detail::write_ellipsis(out, opts); - break; - } - } - return out; - } - else if constexpr (std::is_same_v // - || std::is_same_v // - || std::is_same_v // - || std::is_same_v) + if constexpr (lexy::is_unicode_encoding) { // Parse the individual code points, and write them out. lexy::range_input input(lexeme.begin(), lexeme.end()); @@ -406,7 +381,7 @@ OutputIt visualize_to(OutputIt out, lexy::lexeme lexeme, else if (result.error == lexy::_detail::cp_error::success) { // Consume and visualize. - reader.set_position(result.end); + reader.reset(result.end); out = visualize_to(out, lexy::code_point(result.cp), opts); } else @@ -461,7 +436,28 @@ OutputIt visualize_to(OutputIt out, lexy::lexeme lexeme, } return out; } - else if constexpr (std::is_same_v) + else if constexpr (lexy::is_text_encoding) + { + auto count = 0u; + for (char c : lexeme) + { + // If the character is in fact ASCII, visualize the code point. + // Otherwise, visualize as byte. + if (lexy::_detail::is_ascii(c)) + out = visualize_to(out, lexy::code_point(static_cast(c)), opts); + else + out = write_escaped_byte(out, static_cast(c)); + + ++count; + if (count == opts.max_lexeme_width) + { + out = _detail::write_ellipsis(out, opts); + break; + } + } + return out; + } + else if constexpr (lexy::is_byte_encoding) { auto count = 0u; for (auto iter = lexeme.begin(); iter != lexeme.end(); ++iter) @@ -480,6 +476,13 @@ OutputIt visualize_to(OutputIt out, lexy::lexeme lexeme, } return out; } + else if constexpr (lexy::is_node_encoding) + { + // Visualize as an iterator range of characters. + lexy::range_input + input(lexeme.begin(), lexeme.end()); + return visualize_to(out, lexy::lexeme_for(input.begin(), input.end())); + } else { static_assert(lexy::_detail::error, "unknown encoding"); @@ -622,6 +625,41 @@ struct cfile_output_iterator } }; +struct stderr_output_iterator +{ + auto operator*() const noexcept + { + return *this; + } + auto operator++(int) const noexcept + { + return *this; + } + + stderr_output_iterator& operator=(char c) + { + std::fputc(c, stderr); + return *this; + } +}; +struct stdout_output_iterator +{ + auto operator*() const noexcept + { + return *this; + } + auto operator++(int) const noexcept + { + return *this; + } + + stdout_output_iterator& operator=(char c) + { + std::fputc(c, stdout); + return *this; + } +}; + /// Writes the visualization to the FILE. template void visualize(std::FILE* file, const T& obj, visualization_options opts = {}) diff --git a/3rdparty/lexy/include/lexy_ext/compiler_explorer.hpp b/3rdparty/lexy/include/lexy_ext/compiler_explorer.hpp index 2b5734303..220adf4ab 100644 --- a/3rdparty/lexy/include/lexy_ext/compiler_explorer.hpp +++ b/3rdparty/lexy/include/lexy_ext/compiler_explorer.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_EXT_COMPILER_EXPLORER_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy_ext/parse_tree_algorithm.hpp b/3rdparty/lexy/include/lexy_ext/parse_tree_algorithm.hpp index 21c77567a..f8242fab6 100644 --- a/3rdparty/lexy/include/lexy_ext/parse_tree_algorithm.hpp +++ b/3rdparty/lexy/include/lexy_ext/parse_tree_algorithm.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_EXT_PARSE_TREE_ALGORITHM_HPP_INCLUDED @@ -216,7 +216,7 @@ class _filtered_node_range template _filtered_node_range(Predicate&& pred, Iterator begin, Sentinel end) noexcept - ->_filtered_node_range, Iterator, Sentinel>; + -> _filtered_node_range, Iterator, Sentinel>; /// Returns the children that of node that match the predicate. /// diff --git a/3rdparty/lexy/include/lexy_ext/parse_tree_doctest.hpp b/3rdparty/lexy/include/lexy_ext/parse_tree_doctest.hpp index 8d3f758b3..18b12dead 100644 --- a/3rdparty/lexy/include/lexy_ext/parse_tree_doctest.hpp +++ b/3rdparty/lexy/include/lexy_ext/parse_tree_doctest.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_EXT_PARSE_TREE_DOCTEST_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy_ext/report_error.hpp b/3rdparty/lexy/include/lexy_ext/report_error.hpp index 710054ae5..0ee6c32a2 100644 --- a/3rdparty/lexy/include/lexy_ext/report_error.hpp +++ b/3rdparty/lexy/include/lexy_ext/report_error.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_EXT_REPORT_ERROR_HPP_INCLUDED @@ -233,8 +233,8 @@ class diagnostic_writer namespace lexy_ext::_detail { -template -OutputIt write_error(OutputIt out, const lexy::error_context& context, +template +OutputIt write_error(OutputIt out, const lexy::error_context& context, const lexy::error& error, lexy::visualization_options opts, const char* path) { @@ -317,7 +317,7 @@ OutputIt write_error(OutputIt out, const lexy::error_context& namespace lexy_ext { -template +template struct _report_error { OutputIterator _iter; @@ -333,22 +333,18 @@ struct _report_error using return_type = std::size_t; - template - void operator()(const lexy::error_context& context, - const lexy::error& error) + template + void operator()(const lexy::error_context& context, + const lexy::error& error) { - if constexpr (std::is_same_v) - _detail::write_error(lexy::cfile_output_iterator{stderr}, context, error, _opts, - _path); - else - _iter = _detail::write_error(_iter, context, error, _opts, _path); + _iter = _detail::write_error(_iter, context, error, _opts, _path); ++_count; } std::size_t finish() && { if (_count != 0) - std::fputs("\n", stderr); + *_iter++ = '\n'; return _count; } }; @@ -378,7 +374,7 @@ struct _report_error }; /// An error callback that uses diagnostic_writer to print to stderr (by default). -constexpr auto report_error = _report_error<>{}; +constexpr auto report_error = _report_error{}; } // namespace lexy_ext #endif // LEXY_EXT_REPORT_ERROR_HPP_INCLUDED diff --git a/3rdparty/lexy/include/lexy_ext/shell.hpp b/3rdparty/lexy/include/lexy_ext/shell.hpp index ba0ab83fb..66865bbda 100644 --- a/3rdparty/lexy/include/lexy_ext/shell.hpp +++ b/3rdparty/lexy/include/lexy_ext/shell.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 #ifndef LEXY_EXT_SHELL_HPP_INCLUDED @@ -168,6 +168,16 @@ class shell using encoding = typename Prompt::encoding; using iterator = typename lexy::_detail::buffer_builder::stable_iterator; + struct marker + { + iterator _it; + + constexpr iterator position() const noexcept + { + return _it; + } + }; + auto reader() const& { return *this; @@ -191,9 +201,13 @@ class shell return iterator(_shell->_buffer, _idx); } - void set_position(iterator new_pos) noexcept + marker current() const noexcept + { + return {position()}; + } + void reset(marker m) noexcept { - _idx = new_pos.index(); + _idx = m._it.index(); } private: @@ -242,7 +256,7 @@ class shell class writer { public: - writer(const writer&) = delete; + writer(const writer&) = delete; writer& operator=(const writer&) = delete; ~writer() noexcept @@ -400,8 +414,8 @@ using shell_lexeme = lexy::lexeme_for>; template > using shell_error = lexy::error_for, Tag>; -template > -using shell_error_context = lexy::error_context>; +template > +using shell_error_context = lexy::error_context>; } // namespace lexy_ext #endif // LEXY_EXT_SHELL_HPP_INCLUDED diff --git a/3rdparty/lexy/src/CMakeLists.txt b/3rdparty/lexy/src/CMakeLists.txt index 96cc3d1b9..709cc831f 100644 --- a/3rdparty/lexy/src/CMakeLists.txt +++ b/3rdparty/lexy/src/CMakeLists.txt @@ -1,9 +1,10 @@ -# Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +# Copyright (C) 2020-2024 Jonathan Müller and lexy contributors # SPDX-License-Identifier: BSL-1.0 get_filename_component(include_dir ${CMAKE_CURRENT_SOURCE_DIR}/../include/lexy ABSOLUTE) get_filename_component(ext_include_dir ${CMAKE_CURRENT_SOURCE_DIR}/../include/lexy_ext ABSOLUTE) set(header_files + ${include_dir}/_detail/any_ref.hpp ${include_dir}/_detail/assert.hpp ${include_dir}/_detail/buffer_builder.hpp ${include_dir}/_detail/code_point.hpp @@ -18,6 +19,7 @@ set(header_files ${include_dir}/_detail/stateless_lambda.hpp ${include_dir}/_detail/std.hpp ${include_dir}/_detail/string_view.hpp + ${include_dir}/_detail/swar.hpp ${include_dir}/_detail/tuple.hpp ${include_dir}/_detail/type_name.hpp @@ -78,6 +80,7 @@ set(header_files ${include_dir}/dsl/option.hpp ${include_dir}/dsl/operator.hpp ${include_dir}/dsl/parse_as.hpp + ${include_dir}/dsl/parse_tree_node.hpp ${include_dir}/dsl/peek.hpp ${include_dir}/dsl/position.hpp ${include_dir}/dsl/production.hpp @@ -103,6 +106,7 @@ set(header_files ${include_dir}/input/buffer.hpp ${include_dir}/input/file.hpp ${include_dir}/input/lexeme_input.hpp + ${include_dir}/input/parse_tree_input.hpp ${include_dir}/input/range_input.hpp ${include_dir}/input/string_input.hpp @@ -147,9 +151,16 @@ if (LEXY_USER_CONFIG_HEADER) endif() endif() +function(add_alias name target) + add_library(foonathan::${name} ALIAS ${target}) + set_target_properties(${target} PROPERTIES EXPORT_NAME ${name}) +endfunction() + +include(GNUInstallDirs) + # Core library. add_library(lexy_core INTERFACE) -add_library(foonathan::lexy::core ALIAS lexy_core) +add_alias(lexy::core lexy_core) target_link_libraries(lexy_core INTERFACE _lexy_base) target_include_directories(lexy_core SYSTEM INTERFACE $ @@ -158,7 +169,7 @@ target_include_directories(lexy_core SYSTEM INTERFACE # Core library with warnings; for development only. add_library(lexy_dev INTERFACE) -add_library(foonathan::lexy::dev ALIAS lexy_dev) +add_alias(lexy::dev lexy_dev) target_link_libraries(lexy_dev INTERFACE _lexy_base) target_include_directories(lexy_dev INTERFACE $ @@ -182,27 +193,32 @@ elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") # GCC's arry bounds, maybe uninitialized, and restrict warning seems to have false positives. target_compile_options(lexy_dev INTERFACE -Wno-array-bounds -Wno-maybe-uninitialized -Wno-restrict) elseif(MSVC) - target_compile_options(lexy_dev INTERFACE /WX /W3 /D _CRT_SECURE_NO_WARNINGS /wd5105) + target_compile_options(lexy_dev INTERFACE /WX /W3 /D _CRT_SECURE_NO_WARNINGS /wd5105 /utf-8) endif() # Link to have FILE I/O. add_library(lexy_file STATIC) -add_library(foonathan::lexy::file ALIAS lexy_file) +add_alias(lexy::file lexy_file) target_link_libraries(lexy_file PRIVATE foonathan::lexy::dev) target_sources(lexy_file PRIVATE input/file.cpp) # Link to enable unicode database. add_library(lexy_unicode INTERFACE) -add_library(foonathan::lexy::unicode ALIAS lexy_unicode) +add_alias(lexy::unicode lexy_unicode) target_compile_definitions(lexy_unicode INTERFACE LEXY_HAS_UNICODE_DATABASE=1) # Link to have extension headers. add_library(lexy_ext INTERFACE) -add_library(foonathan::lexy::ext ALIAS lexy_ext) +add_alias(lexy::ext lexy_ext) target_sources(lexy_ext INTERFACE ${ext_headers_files}) # Umbrella target with all components. add_library(lexy INTERFACE) -add_library(foonathan::lexy ALIAS lexy) +add_alias(lexy lexy) target_link_libraries(lexy INTERFACE foonathan::lexy::core foonathan::lexy::file foonathan::lexy::unicode foonathan::lexy::ext) +# Link to enable experimental features. +add_library(lexy_experimental INTERFACE) +add_alias(lexy::experimental lexy_experimental) +target_compile_definitions(lexy_experimental INTERFACE LEXY_EXPERIMENTAL=1) + diff --git a/3rdparty/lexy/src/input/file.cpp b/3rdparty/lexy/src/input/file.cpp index 66f8b1f85..028fb6de3 100644 --- a/3rdparty/lexy/src/input/file.cpp +++ b/3rdparty/lexy/src/input/file.cpp @@ -1,6 +1,7 @@ -// Copyright (C) 2020-2022 Jonathan Müller and lexy contributors +// Copyright (C) 2020-2024 Jonathan Müller and lexy contributors // SPDX-License-Identifier: BSL-1.0 +#include #include #include @@ -20,7 +21,7 @@ class raii_fd public: explicit raii_fd(int file) noexcept : _file(file) {} - raii_fd(const raii_fd&) = delete; + raii_fd(const raii_fd&) = delete; raii_fd& operator=(const raii_fd&) = delete; ~raii_fd() noexcept @@ -56,8 +57,8 @@ lexy::file_error get_file_error() noexcept } } -constexpr std::size_t small_file_size = 4 * 1024; -constexpr std::size_t medium_file_size = 32 * 1024; +constexpr std::size_t small_file_size = std::size_t(4) * 1024; +constexpr std::size_t medium_file_size = std::size_t(32) * 1024; } // namespace lexy::file_error lexy::_detail::read_file(const char* path, file_callback cb, void* user_data) @@ -116,7 +117,7 @@ class raii_file public: explicit raii_file(std::FILE* file) noexcept : _file(file) {} - raii_file(const raii_file&) = delete; + raii_file(const raii_file&) = delete; raii_file& operator=(const raii_file&) = delete; ~raii_file() noexcept diff --git a/3rdparty/minicoro/LICENSE b/3rdparty/minicoro/LICENSE new file mode 100644 index 000000000..2c46487fe --- /dev/null +++ b/3rdparty/minicoro/LICENSE @@ -0,0 +1,47 @@ +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright (c) 2021-2022 Eduardo Bart (https://github.com/edubart/minicoro) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/3rdparty/minicoro/README.md b/3rdparty/minicoro/README.md new file mode 100644 index 000000000..d4c7743e9 --- /dev/null +++ b/3rdparty/minicoro/README.md @@ -0,0 +1,357 @@ +# minicoro + +Minicoro is single-file library for using asymmetric coroutines in C. +The API is inspired by [Lua coroutines](https://www.lua.org/manual/5.4/manual.html#6.2) but with C use in mind. + +The project is being developed mainly to be a coroutine backend +for the [Nelua](https://github.com/edubart/nelua-lang) programming language. + +The library assembly implementation is inspired by [Lua Coco](https://coco.luajit.org/index.html) by Mike Pall. + +# Features + +- Stackful asymmetric coroutines. +- Supports nesting coroutines (resuming a coroutine from another coroutine). +- Supports custom allocators. +- Storage system to allow passing values between yield and resume. +- Customizable stack size. +- Coroutine API design inspired by Lua with C use in mind. +- Yield across any C function. +- Made to work in multithread applications. +- Cross platform. +- Minimal, self contained and no external dependencies. +- Readable sources and documented. +- Implemented via assembly, ucontext or fibers. +- Lightweight and very efficient. +- Works in most C89 compilers. +- Error prone API, returning proper error codes on misuse. +- Support running with Valgrind, ASan (AddressSanitizer) and TSan (ThreadSanitizer). + +# Supported Platforms + +Most platforms are supported through different methods: + +| Platform | Assembly Method | Fallback Method | +|--------------|------------------|-------------------| +| Android | ARM/ARM64 | N/A | +| iOS | ARM/ARM64 | N/A | +| Windows | x86_64 | Windows fibers | +| Linux | x86_64/i686 | ucontext | +| Mac OS X | x86_64/ARM/ARM64 | ucontext | +| WebAssembly | N/A | Emscripten fibers / Binaryen asyncify | +| Raspberry Pi | ARM | ucontext | +| RISC-V | rv64/rv32 | ucontext | + +The assembly method is used by default if supported by the compiler and CPU, +otherwise ucontext or fiber method is used as a fallback. + +The assembly method is very efficient, it just take a few cycles +to create, resume, yield or destroy a coroutine. + +# Caveats + +- Don't use coroutines with C++ exceptions, this is not supported. +- When using C++ RAII (i.e. destructors) you must resume the coroutine until it dies to properly execute all destructors. +- To use in multithread applications, you must compile with C compiler that supports `thread_local` qualifier. +- Some unsupported sanitizers for C may trigger false warnings when using coroutines. +- The `mco_coro` object is not thread safe, you should lock each coroutine into a thread. +- Stack space is fixed, it cannot grow. By default it has about 56KB of space, this can be changed on coroutine creation. +- Take care to not cause stack overflows (run out of stack space), otherwise your program may crash or not, the behavior is undefined. +- On WebAssembly you must compile with Emscripten flag `-s ASYNCIFY=1`. +- The WebAssembly Binaryen asyncify method can be used when explicitly enabled, +you may want to do this only to use minicoro with WebAssembly native interpreters +(no Web browser). This method is confirmed to work well with Emscripten toolchain, +however it fails on other WebAssembly toolchains like WASI SDK. + +# Introduction + +A coroutine represents an independent "green" thread of execution. +Unlike threads in multithread systems, however, +a coroutine only suspends its execution by explicitly calling a yield function. + +You create a coroutine by calling `mco_create`. +Its sole argument is a `mco_desc` structure with a description for the coroutine. +The `mco_create` function only creates a new coroutine and returns a handle to it, it does not start the coroutine. + +You execute a coroutine by calling `mco_resume`. +When calling a resume function the coroutine starts its execution by calling its body function. +After the coroutine starts running, it runs until it terminates or yields. + +A coroutine yields by calling `mco_yield`. +When a coroutine yields, the corresponding resume returns immediately, +even if the yield happens inside nested function calls (that is, not in the main function). +The next time you resume the same coroutine, it continues its execution from the point where it yielded. + +To associate a persistent value with the coroutine, +you can optionally set `user_data` on its creation and later retrieve with `mco_get_user_data`. + +To pass values between resume and yield, +you can optionally use `mco_push` and `mco_pop` APIs, +they are intended to pass temporary values using a LIFO (Last In, First Out) style buffer. +The storage system can also be used to send and receive initial values on coroutine creation or before it finishes. + +# Usage + +To use minicoro, do the following in one .c file: + +```c +#define MINICORO_IMPL +#include "minicoro.h" +``` + +You can do `#include "minicoro.h"` in other parts of the program just like any other header. + +## Minimal Example + +The following simple example demonstrates on how to use the library: + +```c +#define MINICORO_IMPL +#include "minicoro.h" +#include + +// Coroutine entry function. +void coro_entry(mco_coro* co) { + printf("coroutine 1\n"); + mco_yield(co); + printf("coroutine 2\n"); +} + +int main() { + // First initialize a `desc` object through `mco_desc_init`. + mco_desc desc = mco_desc_init(coro_entry, 0); + // Configure `desc` fields when needed (e.g. customize user_data or allocation functions). + desc.user_data = NULL; + // Call `mco_create` with the output coroutine pointer and `desc` pointer. + mco_coro* co; + mco_result res = mco_create(&co, &desc); + assert(res == MCO_SUCCESS); + // The coroutine should be now in suspended state. + assert(mco_status(co) == MCO_SUSPENDED); + // Call `mco_resume` to start for the first time, switching to its context. + res = mco_resume(co); // Should print "coroutine 1". + assert(res == MCO_SUCCESS); + // We get back from coroutine context in suspended state (because it's unfinished). + assert(mco_status(co) == MCO_SUSPENDED); + // Call `mco_resume` to resume for a second time. + res = mco_resume(co); // Should print "coroutine 2". + assert(res == MCO_SUCCESS); + // The coroutine finished and should be now dead. + assert(mco_status(co) == MCO_DEAD); + // Call `mco_destroy` to destroy the coroutine. + res = mco_destroy(co); + assert(res == MCO_SUCCESS); + return 0; +} +``` + +_NOTE_: In case you don't want to use the minicoro allocator system you should +allocate a coroutine object yourself using `mco_desc.coro_size` and call `mco_init`, +then later to destroy call `mco_uninit` and deallocate it. + +## Yielding from anywhere + +You can yield the current running coroutine from anywhere +without having to pass `mco_coro` pointers around, +to this just use `mco_yield(mco_running())`. + +## Passing data between yield and resume + +The library has the storage interface to assist passing data between yield and resume. +It's usage is straightforward, +use `mco_push` to send data before a `mco_resume` or `mco_yield`, +then later use `mco_pop` after a `mco_resume` or `mco_yield` to receive data. +Take care to not mismatch a push and pop, otherwise these functions will return +an error. + +## Error handling + +The library return error codes in most of its API in case of misuse or system error, +the user is encouraged to handle them properly. + +## Library customization + +The following can be defined to change the library behavior: + +- `MCO_API` - Public API qualifier. Default is `extern`. +- `MCO_MIN_STACK_SIZE` - Minimum stack size when creating a coroutine. Default is 32768. +- `MCO_DEFAULT_STORAGE_SIZE` - Size of coroutine storage buffer. Default is 1024. +- `MCO_DEFAULT_STACK_SIZE` - Default stack size when creating a coroutine. Default is 57344. +- `MCO_MALLOC` - Default allocation function. Default is `malloc`. +- `MCO_FREE` - Default deallocation function. Default is `free`. +- `MCO_DEBUG` - Enable debug mode, logging any runtime error to stdout. Defined automatically unless `NDEBUG` or `MCO_NO_DEBUG` is defined. +- `MCO_NO_DEBUG` - Disable debug mode. +- `MCO_NO_MULTITHREAD` - Disable multithread usage. Multithread is supported when `thread_local` is supported. +- `MCO_NO_DEFAULT_ALLOCATORS` - Disable the default allocator using `MCO_MALLOC` and `MCO_FREE`. +- `MCO_ZERO_MEMORY` - Zero memory of stack for new coroutines and when poping storage, intended for garbage collected environments. +- `MCO_USE_ASM` - Force use of assembly context switch implementation. +- `MCO_USE_UCONTEXT` - Force use of ucontext context switch implementation. +- `MCO_USE_FIBERS` - Force use of fibers context switch implementation. +- `MCO_USE_ASYNCIFY` - Force use of Binaryen asyncify context switch implementation. +- `MCO_USE_VALGRIND` - Define if you want run with valgrind to fix accessing memory errors. + +# Benchmarks + +The coroutine library was benchmarked for x86_64 counting CPU cycles +for context switch (triggered in resume or yield) and initialization. + +| CPU Arch | OS | Method | Context switch | Initialize | Uninitialize | +|----------|----------|----------|----------------|--------------|--------------| +| x86_64 | Linux | assembly | 9 cycles | 31 cycles | 14 cycles | +| x86_64 | Linux | ucontext | 352 cycles | 383 cycles | 14 cycles | +| x86_64 | Windows | fibers | 69 cycles | 10564 cycles | 11167 cycles | +| x86_64 | Windows | assembly | 33 cycles | 74 cycles | 14 cycles | + +_NOTE_: Tested on Intel Core i7-8750H CPU @ 2.20GHz with pre allocated coroutines. + +# Cheatsheet + +Here is a list of all library functions for quick reference: + +```c +/* Structure used to initialize a coroutine. */ +typedef struct mco_desc { + void (*func)(mco_coro* co); /* Entry point function for the coroutine. */ + void* user_data; /* Coroutine user data, can be get with `mco_get_user_data`. */ + /* Custom allocation interface. */ + void* (*malloc_cb)(size_t size, void* allocator_data); /* Custom allocation function. */ + void (*free_cb)(void* ptr, void* allocator_data); /* Custom deallocation function. */ + void* allocator_data; /* User data pointer passed to `malloc`/`free` allocation functions. */ + size_t storage_size; /* Coroutine storage size, to be used with the storage APIs. */ + /* These must be initialized only through `mco_init_desc`. */ + size_t coro_size; /* Coroutine structure size. */ + size_t stack_size; /* Coroutine stack size. */ +} mco_desc; + +/* Coroutine functions. */ +mco_desc mco_desc_init(void (*func)(mco_coro* co), size_t stack_size); /* Initialize description of a coroutine. When stack size is 0 then MCO_DEFAULT_STACK_SIZE is used. */ +mco_result mco_init(mco_coro* co, mco_desc* desc); /* Initialize the coroutine. */ +mco_result mco_uninit(mco_coro* co); /* Uninitialize the coroutine, may fail if it's not dead or suspended. */ +mco_result mco_create(mco_coro** out_co, mco_desc* desc); /* Allocates and initializes a new coroutine. */ +mco_result mco_destroy(mco_coro* co); /* Uninitialize and deallocate the coroutine, may fail if it's not dead or suspended. */ +mco_result mco_resume(mco_coro* co); /* Starts or continues the execution of the coroutine. */ +mco_result mco_yield(mco_coro* co); /* Suspends the execution of a coroutine. */ +mco_state mco_status(mco_coro* co); /* Returns the status of the coroutine. */ +void* mco_get_user_data(mco_coro* co); /* Get coroutine user data supplied on coroutine creation. */ + +/* Storage interface functions, used to pass values between yield and resume. */ +mco_result mco_push(mco_coro* co, const void* src, size_t len); /* Push bytes to the coroutine storage. Use to send values between yield and resume. */ +mco_result mco_pop(mco_coro* co, void* dest, size_t len); /* Pop bytes from the coroutine storage. Use to get values between yield and resume. */ +mco_result mco_peek(mco_coro* co, void* dest, size_t len); /* Like `mco_pop` but it does not consumes the storage. */ +size_t mco_get_bytes_stored(mco_coro* co); /* Get the available bytes that can be retrieved with a `mco_pop`. */ +size_t mco_get_storage_size(mco_coro* co); /* Get the total storage size. */ + +/* Misc functions. */ +mco_coro* mco_running(void); /* Returns the running coroutine for the current thread. */ +const char* mco_result_description(mco_result res); /* Get the description of a result. */ +``` + +# Complete Example + +The following is a more complete example, generating Fibonacci numbers: + +```c +#define MINICORO_IMPL +#include "minicoro.h" +#include + +static void fail(const char* message, mco_result res) { + printf("%s: %s\n", message, mco_result_description(res)); + exit(-1); +} + +static void fibonacci_coro(mco_coro* co) { + unsigned long m = 1; + unsigned long n = 1; + + /* Retrieve max value. */ + unsigned long max; + mco_result res = mco_pop(co, &max, sizeof(max)); + if(res != MCO_SUCCESS) + fail("Failed to retrieve coroutine storage", res); + + while(1) { + /* Yield the next Fibonacci number. */ + mco_push(co, &m, sizeof(m)); + res = mco_yield(co); + if(res != MCO_SUCCESS) + fail("Failed to yield coroutine", res); + + unsigned long tmp = m + n; + m = n; + n = tmp; + if(m >= max) + break; + } + + /* Yield the last Fibonacci number. */ + mco_push(co, &m, sizeof(m)); +} + +int main() { + /* Create the coroutine. */ + mco_coro* co; + mco_desc desc = mco_desc_init(fibonacci_coro, 0); + mco_result res = mco_create(&co, &desc); + if(res != MCO_SUCCESS) + fail("Failed to create coroutine", res); + + /* Set storage. */ + unsigned long max = 1000000000; + mco_push(co, &max, sizeof(max)); + + int counter = 1; + while(mco_status(co) == MCO_SUSPENDED) { + /* Resume the coroutine. */ + res = mco_resume(co); + if(res != MCO_SUCCESS) + fail("Failed to resume coroutine", res); + + /* Retrieve storage set in last coroutine yield. */ + unsigned long ret = 0; + res = mco_pop(co, &ret, sizeof(ret)); + if(res != MCO_SUCCESS) + fail("Failed to retrieve coroutine storage", res); + printf("fib %d = %lu\n", counter, ret); + counter = counter + 1; + } + + /* Destroy the coroutine. */ + res = mco_destroy(co); + if(res != MCO_SUCCESS) + fail("Failed to destroy coroutine", res); + return 0; +} +``` + +# Updates + +- **08-Jun-2022**: Minicoro has been awarded by the [Icculus Microgrant 2021](https://icculus.org/microgrant/), thanks @icculus for supporting open source work. +- **26-Jan-2022**: Added support for WebAssembly outside the WebBrowser using Binaryen asyncify pass. +- **01-Sep-2021**: Added support for DOSBox (MS-DOS Emulator). +- **30-Aug-2021**: Fix stack overflow crash on Windows 32 bits. +- **22-Aug-2021**: Added checks for stack overflow and iOS support (thanks @srberg). +- **12-Mar-2021**: Added support for RISC-V RV32. +- **19-Jan-2021**: Fix compilation and issues on Mac OS X, release v0.1.1. +- **19-Jan-2021**: First release, v0.1.0. +- **18-Jan-2021**: Fix issues when using Clang on Linux. +- **17-Jan-2021**: Add support for RISC-V 64 bits. +- **16-Jan-2021**: Add support for Mac OS X x86_64, thanks @RandyGaul for testing, debugging and researching about it. +- **15-Jan-2021**: Make assembly method the default one on Windows x86_64. Redesigned the storage API, thanks @RandyGaul for the suggestion. +- **14-Jan-2021**: Add support for running with ASan (AddressSanitizer) and TSan (ThreadSanitizer). +- **13-Jan-2021**: Add support for ARM and WebAssembly. Add Public Domain and MIT No Attribution license. +- **12-Jan-2021**: Some API changes and improvements. +- **11-Jan-2021**: Support valgrind and add benchmarks. +- **10-Jan-2021**: Minor API improvements and document more. +- **09-Jan-2021**: Library created. + +# Donation + +I'm a full-time open source developer. +Any amount of the donation will be appreciated and could bring me encouragement to keep supporting this and other open source projects. + +[![Become a Patron](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/edubart) + +# License + +Your choice of either Public Domain or MIT No Attribution, see LICENSE file. diff --git a/3rdparty/minicoro/minicoro.h b/3rdparty/minicoro/minicoro.h new file mode 100644 index 000000000..a93421db0 --- /dev/null +++ b/3rdparty/minicoro/minicoro.h @@ -0,0 +1,1965 @@ +/* +Minimal asymmetric stackful cross-platform coroutine library in pure C. +minicoro - v0.1.3 - 27/Jan/2022 +Eduardo Bart - edub4rt@gmail.com +https://github.com/edubart/minicoro + +Minicoro is single file library for using asymmetric coroutines in C. +The API is inspired by Lua coroutines but with C use in mind. + +# Features + +- Stackful asymmetric coroutines. +- Supports nesting coroutines (resuming a coroutine from another coroutine). +- Supports custom allocators. +- Storage system to allow passing values between yield and resume. +- Customizable stack size. +- Coroutine API design inspired by Lua with C use in mind. +- Yield across any C function. +- Made to work in multithread applications. +- Cross platform. +- Minimal, self contained and no external dependencies. +- Readable sources and documented. +- Implemented via assembly, ucontext or fibers. +- Lightweight and very efficient. +- Works in most C89 compilers. +- Error prone API, returning proper error codes on misuse. +- Support running with Valgrind, ASan (AddressSanitizer) and TSan (ThreadSanitizer). + +# Supported Platforms + +Most platforms are supported through different methods: + +| Platform | Assembly Method | Fallback Method | +|--------------|------------------|-------------------| +| Android | ARM/ARM64 | N/A | +| iOS | ARM/ARM64 | N/A | +| Windows | x86_64 | Windows fibers | +| Linux | x86_64/i686 | ucontext | +| Mac OS X | x86_64/ARM/ARM64 | ucontext | +| WebAssembly | N/A | Emscripten fibers / Binaryen asyncify | +| Raspberry Pi | ARM | ucontext | +| RISC-V | rv64/rv32 | ucontext | + +The assembly method is used by default if supported by the compiler and CPU, +otherwise ucontext or fiber method is used as a fallback. + +The assembly method is very efficient, it just take a few cycles +to create, resume, yield or destroy a coroutine. + +# Caveats + +- Don't use coroutines with C++ exceptions, this is not supported. +- When using C++ RAII (i.e. destructors) you must resume the coroutine until it dies to properly execute all destructors. +- To use in multithread applications, you must compile with C compiler that supports `thread_local` qualifier. +- Some unsupported sanitizers for C may trigger false warnings when using coroutines. +- The `mco_coro` object is not thread safe, you should lock each coroutine into a thread. +- Stack space is fixed, it cannot grow. By default it has about 56KB of space, this can be changed on coroutine creation. +- Take care to not cause stack overflows (run out of stack space), otherwise your program may crash or not, the behavior is undefined. +- On WebAssembly you must compile with Emscripten flag `-s ASYNCIFY=1`. +- The WebAssembly Binaryen asyncify method can be used when explicitly enabled, +you may want to do this only to use minicoro with WebAssembly native interpreters +(no Web browser). This method is confirmed to work well with Emscripten toolchain, +however it fails on other WebAssembly toolchains like WASI SDK. + +# Introduction + +A coroutine represents an independent "green" thread of execution. +Unlike threads in multithread systems, however, +a coroutine only suspends its execution by explicitly calling a yield function. + +You create a coroutine by calling `mco_create`. +Its sole argument is a `mco_desc` structure with a description for the coroutine. +The `mco_create` function only creates a new coroutine and returns a handle to it, it does not start the coroutine. + +You execute a coroutine by calling `mco_resume`. +When calling a resume function the coroutine starts its execution by calling its body function. +After the coroutine starts running, it runs until it terminates or yields. + +A coroutine yields by calling `mco_yield`. +When a coroutine yields, the corresponding resume returns immediately, +even if the yield happens inside nested function calls (that is, not in the main function). +The next time you resume the same coroutine, it continues its execution from the point where it yielded. + +To associate a persistent value with the coroutine, +you can optionally set `user_data` on its creation and later retrieve with `mco_get_user_data`. + +To pass values between resume and yield, +you can optionally use `mco_push` and `mco_pop` APIs, +they are intended to pass temporary values using a LIFO style buffer. +The storage system can also be used to send and receive initial values on coroutine creation or before it finishes. + +# Usage + +To use minicoro, do the following in one .c file: + +```c +#define MINICORO_IMPL +#include "minicoro.h" +``` + +You can do `#include "minicoro.h"` in other parts of the program just like any other header. + +## Minimal Example + +The following simple example demonstrates on how to use the library: + +```c +#define MINICORO_IMPL +#include "minicoro.h" +#include + +// Coroutine entry function. +void coro_entry(mco_coro* co) { + printf("coroutine 1\n"); + mco_yield(co); + printf("coroutine 2\n"); +} + +int main() { + // First initialize a `desc` object through `mco_desc_init`. + mco_desc desc = mco_desc_init(coro_entry, 0); + // Configure `desc` fields when needed (e.g. customize user_data or allocation functions). + desc.user_data = NULL; + // Call `mco_create` with the output coroutine pointer and `desc` pointer. + mco_coro* co; + mco_result res = mco_create(&co, &desc); + assert(res == MCO_SUCCESS); + // The coroutine should be now in suspended state. + assert(mco_status(co) == MCO_SUSPENDED); + // Call `mco_resume` to start for the first time, switching to its context. + res = mco_resume(co); // Should print "coroutine 1". + assert(res == MCO_SUCCESS); + // We get back from coroutine context in suspended state (because it's unfinished). + assert(mco_status(co) == MCO_SUSPENDED); + // Call `mco_resume` to resume for a second time. + res = mco_resume(co); // Should print "coroutine 2". + assert(res == MCO_SUCCESS); + // The coroutine finished and should be now dead. + assert(mco_status(co) == MCO_DEAD); + // Call `mco_destroy` to destroy the coroutine. + res = mco_destroy(co); + assert(res == MCO_SUCCESS); + return 0; +} +``` + +_NOTE_: In case you don't want to use the minicoro allocator system you should +allocate a coroutine object yourself using `mco_desc.coro_size` and call `mco_init`, +then later to destroy call `mco_uninit` and deallocate it. + +## Yielding from anywhere + +You can yield the current running coroutine from anywhere +without having to pass `mco_coro` pointers around, +to this just use `mco_yield(mco_running())`. + +## Passing data between yield and resume + +The library has the storage interface to assist passing data between yield and resume. +It's usage is straightforward, +use `mco_push` to send data before a `mco_resume` or `mco_yield`, +then later use `mco_pop` after a `mco_resume` or `mco_yield` to receive data. +Take care to not mismatch a push and pop, otherwise these functions will return +an error. + +## Error handling + +The library return error codes in most of its API in case of misuse or system error, +the user is encouraged to handle them properly. + +## Library customization + +The following can be defined to change the library behavior: + +- `MCO_API` - Public API qualifier. Default is `extern`. +- `MCO_MIN_STACK_SIZE` - Minimum stack size when creating a coroutine. Default is 32768. +- `MCO_DEFAULT_STORAGE_SIZE` - Size of coroutine storage buffer. Default is 1024. +- `MCO_DEFAULT_STACK_SIZE` - Default stack size when creating a coroutine. Default is 57344. +- `MCO_MALLOC` - Default allocation function. Default is `malloc`. +- `MCO_FREE` - Default deallocation function. Default is `free`. +- `MCO_DEBUG` - Enable debug mode, logging any runtime error to stdout. Defined automatically unless `NDEBUG` or `MCO_NO_DEBUG` is defined. +- `MCO_NO_DEBUG` - Disable debug mode. +- `MCO_NO_MULTITHREAD` - Disable multithread usage. Multithread is supported when `thread_local` is supported. +- `MCO_NO_DEFAULT_ALLOCATORS` - Disable the default allocator using `MCO_MALLOC` and `MCO_FREE`. +- `MCO_ZERO_MEMORY` - Zero memory of stack for new coroutines and when poping storage, intended for garbage collected environments. +- `MCO_USE_ASM` - Force use of assembly context switch implementation. +- `MCO_USE_UCONTEXT` - Force use of ucontext context switch implementation. +- `MCO_USE_FIBERS` - Force use of fibers context switch implementation. +- `MCO_USE_ASYNCIFY` - Force use of Binaryen asyncify context switch implementation. +- `MCO_USE_VALGRIND` - Define if you want run with valgrind to fix accessing memory errors. + +# License + +Your choice of either Public Domain or MIT No Attribution, see end of file. +*/ + + +#ifndef MINICORO_H +#define MINICORO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Public API qualifier. */ +#ifndef MCO_API +#define MCO_API extern +#endif + +/* Size of coroutine storage buffer. */ +#ifndef MCO_DEFAULT_STORAGE_SIZE +#define MCO_DEFAULT_STORAGE_SIZE 1024 +#endif + +#include /* for size_t */ + +/* ---------------------------------------------------------------------------------------------- */ + +/* Coroutine states. */ +typedef enum mco_state { + MCO_DEAD = 0, /* The coroutine has finished normally or was uninitialized before finishing. */ + MCO_NORMAL, /* The coroutine is active but not running (that is, it has resumed another coroutine). */ + MCO_RUNNING, /* The coroutine is active and running. */ + MCO_SUSPENDED /* The coroutine is suspended (in a call to yield, or it has not started running yet). */ +} mco_state; + +/* Coroutine result codes. */ +typedef enum mco_result { + MCO_SUCCESS = 0, + MCO_GENERIC_ERROR, + MCO_INVALID_POINTER, + MCO_INVALID_COROUTINE, + MCO_NOT_SUSPENDED, + MCO_NOT_RUNNING, + MCO_MAKE_CONTEXT_ERROR, + MCO_SWITCH_CONTEXT_ERROR, + MCO_NOT_ENOUGH_SPACE, + MCO_OUT_OF_MEMORY, + MCO_INVALID_ARGUMENTS, + MCO_INVALID_OPERATION, + MCO_STACK_OVERFLOW, +} mco_result; + +/* Coroutine structure. */ +typedef struct mco_coro mco_coro; +struct mco_coro { + void* context; + mco_state state; + void (*func)(mco_coro* co); + mco_coro* prev_co; + void* user_data; + void* allocator_data; + void (*free_cb)(void* ptr, void* allocator_data); + void* stack_base; /* Stack base address, can be used to scan memory in a garbage collector. */ + size_t stack_size; + unsigned char* storage; + size_t bytes_stored; + size_t storage_size; + void* asan_prev_stack; /* Used by address sanitizer. */ + void* tsan_prev_fiber; /* Used by thread sanitizer. */ + void* tsan_fiber; /* Used by thread sanitizer. */ + size_t magic_number; /* Used to check stack overflow. */ +}; + +/* Structure used to initialize a coroutine. */ +typedef struct mco_desc { + void (*func)(mco_coro* co); /* Entry point function for the coroutine. */ + void* user_data; /* Coroutine user data, can be get with `mco_get_user_data`. */ + /* Custom allocation interface. */ + void* (*malloc_cb)(size_t size, void* allocator_data); /* Custom allocation function. */ + void (*free_cb)(void* ptr, void* allocator_data); /* Custom deallocation function. */ + void* allocator_data; /* User data pointer passed to `malloc`/`free` allocation functions. */ + size_t storage_size; /* Coroutine storage size, to be used with the storage APIs. */ + /* These must be initialized only through `mco_init_desc`. */ + size_t coro_size; /* Coroutine structure size. */ + size_t stack_size; /* Coroutine stack size. */ +} mco_desc; + +/* Coroutine functions. */ +MCO_API mco_desc mco_desc_init(void (*func)(mco_coro* co), size_t stack_size); /* Initialize description of a coroutine. When stack size is 0 then MCO_DEFAULT_STACK_SIZE is used. */ +MCO_API mco_result mco_init(mco_coro* co, mco_desc* desc); /* Initialize the coroutine. */ +MCO_API mco_result mco_uninit(mco_coro* co); /* Uninitialize the coroutine, may fail if it's not dead or suspended. */ +MCO_API mco_result mco_create(mco_coro** out_co, mco_desc* desc); /* Allocates and initializes a new coroutine. */ +MCO_API mco_result mco_destroy(mco_coro* co); /* Uninitialize and deallocate the coroutine, may fail if it's not dead or suspended. */ +MCO_API mco_result mco_resume(mco_coro* co); /* Starts or continues the execution of the coroutine. */ +MCO_API mco_result mco_yield(mco_coro* co); /* Suspends the execution of a coroutine. */ +MCO_API mco_state mco_status(mco_coro* co); /* Returns the status of the coroutine. */ +MCO_API void* mco_get_user_data(mco_coro* co); /* Get coroutine user data supplied on coroutine creation. */ + +/* Storage interface functions, used to pass values between yield and resume. */ +MCO_API mco_result mco_push(mco_coro* co, const void* src, size_t len); /* Push bytes to the coroutine storage. Use to send values between yield and resume. */ +MCO_API mco_result mco_pop(mco_coro* co, void* dest, size_t len); /* Pop bytes from the coroutine storage. Use to get values between yield and resume. */ +MCO_API mco_result mco_peek(mco_coro* co, void* dest, size_t len); /* Like `mco_pop` but it does not consumes the storage. */ +MCO_API size_t mco_get_bytes_stored(mco_coro* co); /* Get the available bytes that can be retrieved with a `mco_pop`. */ +MCO_API size_t mco_get_storage_size(mco_coro* co); /* Get the total storage size. */ + +/* Misc functions. */ +MCO_API mco_coro* mco_running(void); /* Returns the running coroutine for the current thread. */ +MCO_API const char* mco_result_description(mco_result res); /* Get the description of a result. */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINICORO_H */ + +#ifdef MINICORO_IMPL + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---------------------------------------------------------------------------------------------- */ + +/* Minimum stack size when creating a coroutine. */ +#ifndef MCO_MIN_STACK_SIZE +#define MCO_MIN_STACK_SIZE 32768 +#endif + +/* Default stack size when creating a coroutine. */ +#ifndef MCO_DEFAULT_STACK_SIZE +#define MCO_DEFAULT_STACK_SIZE 57344 /* Don't use multiples of 64K to avoid D-cache aliasing conflicts. */ +#endif + +/* Number used only to assist checking for stack overflows. */ +#define MCO_MAGIC_NUMBER 0x7E3CB1A9 + +/* Detect implementation based on OS, arch and compiler. */ +#if !defined(MCO_USE_UCONTEXT) && !defined(MCO_USE_FIBERS) && !defined(MCO_USE_ASM) && !defined(MCO_USE_ASYNCIFY) + #if defined(_WIN32) + #if (defined(__GNUC__) && defined(__x86_64__)) || (defined(_MSC_VER) && defined(_M_X64)) + #define MCO_USE_ASM + #else + #define MCO_USE_FIBERS + #endif + #elif defined(__CYGWIN__) /* MSYS */ + #define MCO_USE_UCONTEXT + #elif defined(__EMSCRIPTEN__) + #define MCO_USE_FIBERS + #elif defined(__wasm__) + #define MCO_USE_ASYNCIFY + #else + #if __GNUC__ >= 3 /* Assembly extension supported. */ + #if defined(__x86_64__) || \ + defined(__i386) || defined(__i386__) || \ + defined(__ARM_EABI__) || defined(__aarch64__) || \ + defined(__riscv) + #define MCO_USE_ASM + #else + #define MCO_USE_UCONTEXT + #endif + #else + #define MCO_USE_UCONTEXT + #endif + #endif +#endif + +#define _MCO_UNUSED(x) (void)(x) + +#if !defined(MCO_NO_DEBUG) && !defined(NDEBUG) && !defined(MCO_DEBUG) +#define MCO_DEBUG +#endif + +#ifndef MCO_LOG + #ifdef MCO_DEBUG + #include + #define MCO_LOG(s) puts(s) + #else + #define MCO_LOG(s) + #endif +#endif + +#ifndef MCO_ASSERT + #ifdef MCO_DEBUG + #include + #define MCO_ASSERT(c) assert(c) + #else + #define MCO_ASSERT(c) + #endif +#endif + +#ifndef MCO_THREAD_LOCAL + #ifdef MCO_NO_MULTITHREAD + #define MCO_THREAD_LOCAL + #else + #ifdef thread_local + #define MCO_THREAD_LOCAL thread_local + #elif __STDC_VERSION__ >= 201112 && !defined(__STDC_NO_THREADS__) + #define MCO_THREAD_LOCAL _Thread_local + #elif defined(_WIN32) && (defined(_MSC_VER) || defined(__ICL) || defined(__DMC__) || defined(__BORLANDC__)) + #define MCO_THREAD_LOCAL __declspec(thread) + #elif defined(__GNUC__) || defined(__SUNPRO_C) || defined(__xlC__) + #define MCO_THREAD_LOCAL __thread + #else /* No thread local support, `mco_running` will be thread unsafe. */ + #define MCO_THREAD_LOCAL + #define MCO_NO_MULTITHREAD + #endif + #endif +#endif + +#ifndef MCO_FORCE_INLINE + #ifdef _MSC_VER + #define MCO_FORCE_INLINE __forceinline + #elif defined(__GNUC__) + #if defined(__STRICT_ANSI__) + #define MCO_FORCE_INLINE __inline__ __attribute__((always_inline)) + #else + #define MCO_FORCE_INLINE inline __attribute__((always_inline)) + #endif + #elif defined(__BORLANDC__) || defined(__DMC__) || defined(__SC__) || defined(__WATCOMC__) || defined(__LCC__) || defined(__DECC) + #define MCO_FORCE_INLINE __inline + #else /* No inline support. */ + #define MCO_FORCE_INLINE + #endif +#endif + +#ifndef MCO_NO_INLINE + #ifdef __GNUC__ + #define MCO_NO_INLINE __attribute__((noinline)) + #elif defined(_MSC_VER) + #define MCO_NO_INLINE __declspec(noinline) + #else + #define MCO_NO_INLINE + #endif +#endif + +#ifndef MCO_NO_DEFAULT_ALLOCATORS +#ifndef MCO_MALLOC + #include + #define MCO_MALLOC malloc + #define MCO_FREE free +#endif +static void* mco_malloc(size_t size, void* allocator_data) { + _MCO_UNUSED(allocator_data); + return MCO_MALLOC(size); +} +static void mco_free(void* ptr, void* allocator_data) { + _MCO_UNUSED(allocator_data); + MCO_FREE(ptr); +} +#endif /* MCO_NO_DEFAULT_ALLOCATORS */ + +#if defined(__has_feature) + #if __has_feature(address_sanitizer) + #define _MCO_USE_ASAN + #endif + #if __has_feature(thread_sanitizer) + #define _MCO_USE_TSAN + #endif +#endif +#if defined(__SANITIZE_ADDRESS__) + #define _MCO_USE_ASAN +#endif +#if defined(__SANITIZE_THREAD__) + #define _MCO_USE_TSAN +#endif +#ifdef _MCO_USE_ASAN +void __sanitizer_start_switch_fiber(void** fake_stack_save, const void *bottom, size_t size); +void __sanitizer_finish_switch_fiber(void* fake_stack_save, const void **bottom_old, size_t *size_old); +#endif +#ifdef _MCO_USE_TSAN +void* __tsan_get_current_fiber(void); +void* __tsan_create_fiber(unsigned flags); +void __tsan_destroy_fiber(void* fiber); +void __tsan_switch_to_fiber(void* fiber, unsigned flags); +#endif + +#include /* For memcpy and memset. */ + +/* Utility for aligning addresses. */ +static MCO_FORCE_INLINE size_t _mco_align_forward(size_t addr, size_t align) { + return (addr + (align-1)) & ~(align-1); +} + +/* Variable holding the current running coroutine per thread. */ +static MCO_THREAD_LOCAL mco_coro* mco_current_co = NULL; + +static MCO_FORCE_INLINE void _mco_prepare_jumpin(mco_coro* co) { + /* Set the old coroutine to normal state and update it. */ + mco_coro* prev_co = mco_running(); /* Must access through `mco_running`. */ + MCO_ASSERT(co->prev_co == NULL); + co->prev_co = prev_co; + if(prev_co) { + MCO_ASSERT(prev_co->state == MCO_RUNNING); + prev_co->state = MCO_NORMAL; + } + mco_current_co = co; +#ifdef _MCO_USE_ASAN + if(prev_co) { + void* bottom_old = NULL; + size_t size_old = 0; + __sanitizer_finish_switch_fiber(prev_co->asan_prev_stack, (const void**)&bottom_old, &size_old); + prev_co->asan_prev_stack = NULL; + } + __sanitizer_start_switch_fiber(&co->asan_prev_stack, co->stack_base, co->stack_size); +#endif +#ifdef _MCO_USE_TSAN + co->tsan_prev_fiber = __tsan_get_current_fiber(); + __tsan_switch_to_fiber(co->tsan_fiber, 0); +#endif +} + +static MCO_FORCE_INLINE void _mco_prepare_jumpout(mco_coro* co) { + /* Switch back to the previous running coroutine. */ + /* MCO_ASSERT(mco_running() == co); */ + mco_coro* prev_co = co->prev_co; + co->prev_co = NULL; + if(prev_co) { + /* MCO_ASSERT(prev_co->state == MCO_NORMAL); */ + prev_co->state = MCO_RUNNING; + } + mco_current_co = prev_co; +#ifdef _MCO_USE_ASAN + void* bottom_old = NULL; + size_t size_old = 0; + __sanitizer_finish_switch_fiber(co->asan_prev_stack, (const void**)&bottom_old, &size_old); + co->asan_prev_stack = NULL; + if(prev_co) { + __sanitizer_start_switch_fiber(&prev_co->asan_prev_stack, bottom_old, size_old); + } +#endif +#ifdef _MCO_USE_TSAN + void* tsan_prev_fiber = co->tsan_prev_fiber; + co->tsan_prev_fiber = NULL; + __tsan_switch_to_fiber(tsan_prev_fiber, 0); +#endif +} + +static void _mco_jumpin(mco_coro* co); +static void _mco_jumpout(mco_coro* co); + +static MCO_NO_INLINE void _mco_main(mco_coro* co) { + co->func(co); /* Run the coroutine function. */ + co->state = MCO_DEAD; /* Coroutine finished successfully, set state to dead. */ + _mco_jumpout(co); /* Jump back to the old context .*/ +} + +/* ---------------------------------------------------------------------------------------------- */ + +#if defined(MCO_USE_UCONTEXT) || defined(MCO_USE_ASM) + +/* +Some of the following assembly code is taken from LuaCoco by Mike Pall. +See https://coco.luajit.org/index.html + +MIT license + +Copyright (C) 2004-2016 Mike Pall. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifdef MCO_USE_ASM + +#if defined(__x86_64__) || defined(_M_X64) + +#ifdef _WIN32 + +typedef struct _mco_ctxbuf { + void *rip, *rsp, *rbp, *rbx, *r12, *r13, *r14, *r15, *rdi, *rsi; + void* xmm[20]; /* xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15 */ + void* fiber_storage; + void* dealloc_stack; + void* stack_limit; + void* stack_base; +} _mco_ctxbuf; + +#if defined(__GNUC__) +#define _MCO_ASM_BLOB __attribute__((section(".text"))) +#elif defined(_MSC_VER) +#define _MCO_ASM_BLOB __declspec(allocate(".text")) +#pragma section(".text") +#endif + +_MCO_ASM_BLOB static unsigned char _mco_wrap_main_code[] = { + 0x4c, 0x89, 0xe9, /* mov %r13,%rcx */ + 0x41, 0xff, 0xe4, /* jmpq *%r12 */ + 0xc3, /* retq */ + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 /* nop */ +}; + +_MCO_ASM_BLOB static unsigned char _mco_switch_code[] = { + 0x48, 0x8d, 0x05, 0x3e, 0x01, 0x00, 0x00, /* lea 0x13e(%rip),%rax */ + 0x48, 0x89, 0x01, /* mov %rax,(%rcx) */ + 0x48, 0x89, 0x61, 0x08, /* mov %rsp,0x8(%rcx) */ + 0x48, 0x89, 0x69, 0x10, /* mov %rbp,0x10(%rcx) */ + 0x48, 0x89, 0x59, 0x18, /* mov %rbx,0x18(%rcx) */ + 0x4c, 0x89, 0x61, 0x20, /* mov %r12,0x20(%rcx) */ + 0x4c, 0x89, 0x69, 0x28, /* mov %r13,0x28(%rcx) */ + 0x4c, 0x89, 0x71, 0x30, /* mov %r14,0x30(%rcx) */ + 0x4c, 0x89, 0x79, 0x38, /* mov %r15,0x38(%rcx) */ + 0x48, 0x89, 0x79, 0x40, /* mov %rdi,0x40(%rcx) */ + 0x48, 0x89, 0x71, 0x48, /* mov %rsi,0x48(%rcx) */ + 0x0f, 0x11, 0x71, 0x50, /* movups %xmm6,0x50(%rcx) */ + 0x0f, 0x11, 0x79, 0x60, /* movups %xmm7,0x60(%rcx) */ + 0x44, 0x0f, 0x11, 0x41, 0x70, /* movups %xmm8,0x70(%rcx) */ + 0x44, 0x0f, 0x11, 0x89, 0x80, 0x00, 0x00, 0x00, /* movups %xmm9,0x80(%rcx) */ + 0x44, 0x0f, 0x11, 0x91, 0x90, 0x00, 0x00, 0x00, /* movups %xmm10,0x90(%rcx) */ + 0x44, 0x0f, 0x11, 0x99, 0xa0, 0x00, 0x00, 0x00, /* movups %xmm11,0xa0(%rcx) */ + 0x44, 0x0f, 0x11, 0xa1, 0xb0, 0x00, 0x00, 0x00, /* movups %xmm12,0xb0(%rcx) */ + 0x44, 0x0f, 0x11, 0xa9, 0xc0, 0x00, 0x00, 0x00, /* movups %xmm13,0xc0(%rcx) */ + 0x44, 0x0f, 0x11, 0xb1, 0xd0, 0x00, 0x00, 0x00, /* movups %xmm14,0xd0(%rcx) */ + 0x44, 0x0f, 0x11, 0xb9, 0xe0, 0x00, 0x00, 0x00, /* movups %xmm15,0xe0(%rcx) */ + 0x65, 0x4c, 0x8b, 0x14, 0x25, 0x30, 0x00, 0x00, 0x00, /* mov %gs:0x30,%r10 */ + 0x49, 0x8b, 0x42, 0x20, /* mov 0x20(%r10),%rax */ + 0x48, 0x89, 0x81, 0xf0, 0x00, 0x00, 0x00, /* mov %rax,0xf0(%rcx) */ + 0x49, 0x8b, 0x82, 0x78, 0x14, 0x00, 0x00, /* mov 0x1478(%r10),%rax */ + 0x48, 0x89, 0x81, 0xf8, 0x00, 0x00, 0x00, /* mov %rax,0xf8(%rcx) */ + 0x49, 0x8b, 0x42, 0x10, /* mov 0x10(%r10),%rax */ + 0x48, 0x89, 0x81, 0x00, 0x01, 0x00, 0x00, /* mov %rax,0x100(%rcx) */ + 0x49, 0x8b, 0x42, 0x08, /* mov 0x8(%r10),%rax */ + 0x48, 0x89, 0x81, 0x08, 0x01, 0x00, 0x00, /* mov %rax,0x108(%rcx) */ + 0x48, 0x8b, 0x82, 0x08, 0x01, 0x00, 0x00, /* mov 0x108(%rdx),%rax */ + 0x49, 0x89, 0x42, 0x08, /* mov %rax,0x8(%r10) */ + 0x48, 0x8b, 0x82, 0x00, 0x01, 0x00, 0x00, /* mov 0x100(%rdx),%rax */ + 0x49, 0x89, 0x42, 0x10, /* mov %rax,0x10(%r10) */ + 0x48, 0x8b, 0x82, 0xf8, 0x00, 0x00, 0x00, /* mov 0xf8(%rdx),%rax */ + 0x49, 0x89, 0x82, 0x78, 0x14, 0x00, 0x00, /* mov %rax,0x1478(%r10) */ + 0x48, 0x8b, 0x82, 0xf0, 0x00, 0x00, 0x00, /* mov 0xf0(%rdx),%rax */ + 0x49, 0x89, 0x42, 0x20, /* mov %rax,0x20(%r10) */ + 0x44, 0x0f, 0x10, 0xba, 0xe0, 0x00, 0x00, 0x00, /* movups 0xe0(%rdx),%xmm15 */ + 0x44, 0x0f, 0x10, 0xb2, 0xd0, 0x00, 0x00, 0x00, /* movups 0xd0(%rdx),%xmm14 */ + 0x44, 0x0f, 0x10, 0xaa, 0xc0, 0x00, 0x00, 0x00, /* movups 0xc0(%rdx),%xmm13 */ + 0x44, 0x0f, 0x10, 0xa2, 0xb0, 0x00, 0x00, 0x00, /* movups 0xb0(%rdx),%xmm12 */ + 0x44, 0x0f, 0x10, 0x9a, 0xa0, 0x00, 0x00, 0x00, /* movups 0xa0(%rdx),%xmm11 */ + 0x44, 0x0f, 0x10, 0x92, 0x90, 0x00, 0x00, 0x00, /* movups 0x90(%rdx),%xmm10 */ + 0x44, 0x0f, 0x10, 0x8a, 0x80, 0x00, 0x00, 0x00, /* movups 0x80(%rdx),%xmm9 */ + 0x44, 0x0f, 0x10, 0x42, 0x70, /* movups 0x70(%rdx),%xmm8 */ + 0x0f, 0x10, 0x7a, 0x60, /* movups 0x60(%rdx),%xmm7 */ + 0x0f, 0x10, 0x72, 0x50, /* movups 0x50(%rdx),%xmm6 */ + 0x48, 0x8b, 0x72, 0x48, /* mov 0x48(%rdx),%rsi */ + 0x48, 0x8b, 0x7a, 0x40, /* mov 0x40(%rdx),%rdi */ + 0x4c, 0x8b, 0x7a, 0x38, /* mov 0x38(%rdx),%r15 */ + 0x4c, 0x8b, 0x72, 0x30, /* mov 0x30(%rdx),%r14 */ + 0x4c, 0x8b, 0x6a, 0x28, /* mov 0x28(%rdx),%r13 */ + 0x4c, 0x8b, 0x62, 0x20, /* mov 0x20(%rdx),%r12 */ + 0x48, 0x8b, 0x5a, 0x18, /* mov 0x18(%rdx),%rbx */ + 0x48, 0x8b, 0x6a, 0x10, /* mov 0x10(%rdx),%rbp */ + 0x48, 0x8b, 0x62, 0x08, /* mov 0x8(%rdx),%rsp */ + 0xff, 0x22, /* jmpq *(%rdx) */ + 0xc3, /* retq */ + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, /* nop */ + 0x90, 0x90, /* nop */ +}; + +void (*_mco_wrap_main)(void) = (void(*)(void))(void*)_mco_wrap_main_code; +void (*_mco_switch)(_mco_ctxbuf* from, _mco_ctxbuf* to) = (void(*)(_mco_ctxbuf* from, _mco_ctxbuf* to))(void*)_mco_switch_code; + +static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { + stack_size = stack_size - 32; /* Reserve 32 bytes for the shadow space. */ + void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - sizeof(size_t)); + stack_high_ptr[0] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */ + ctx->rip = (void*)(_mco_wrap_main); + ctx->rsp = (void*)(stack_high_ptr); + ctx->r12 = (void*)(_mco_main); + ctx->r13 = (void*)(co); + void* stack_top = (void*)((size_t)stack_base + stack_size); + ctx->stack_base = stack_top; + ctx->stack_limit = stack_base; + ctx->dealloc_stack = stack_base; + return MCO_SUCCESS; +} + +#else /* not _WIN32 */ + +typedef struct _mco_ctxbuf { + void *rip, *rsp, *rbp, *rbx, *r12, *r13, *r14, *r15; +} _mco_ctxbuf; + +void _mco_wrap_main(void); +int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to); + +__asm__( + ".text\n" +#ifdef __MACH__ /* Mac OS X assembler */ + ".globl __mco_wrap_main\n" + "__mco_wrap_main:\n" +#else /* Linux assembler */ + ".globl _mco_wrap_main\n" + ".type _mco_wrap_main @function\n" + ".hidden _mco_wrap_main\n" + "_mco_wrap_main:\n" +#endif + " movq %r13, %rdi\n" + " jmpq *%r12\n" +#ifndef __MACH__ + ".size _mco_wrap_main, .-_mco_wrap_main\n" +#endif +); + +__asm__( + ".text\n" +#ifdef __MACH__ /* Mac OS assembler */ + ".globl __mco_switch\n" + "__mco_switch:\n" +#else /* Linux assembler */ + ".globl _mco_switch\n" + ".type _mco_switch @function\n" + ".hidden _mco_switch\n" + "_mco_switch:\n" +#endif + " leaq 0x3d(%rip), %rax\n" + " movq %rax, (%rdi)\n" + " movq %rsp, 8(%rdi)\n" + " movq %rbp, 16(%rdi)\n" + " movq %rbx, 24(%rdi)\n" + " movq %r12, 32(%rdi)\n" + " movq %r13, 40(%rdi)\n" + " movq %r14, 48(%rdi)\n" + " movq %r15, 56(%rdi)\n" + " movq 56(%rsi), %r15\n" + " movq 48(%rsi), %r14\n" + " movq 40(%rsi), %r13\n" + " movq 32(%rsi), %r12\n" + " movq 24(%rsi), %rbx\n" + " movq 16(%rsi), %rbp\n" + " movq 8(%rsi), %rsp\n" + " jmpq *(%rsi)\n" + " ret\n" +#ifndef __MACH__ + ".size _mco_switch, .-_mco_switch\n" +#endif +); + +static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { + stack_size = stack_size - 128; /* Reserve 128 bytes for the Red Zone space (System V AMD64 ABI). */ + void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - sizeof(size_t)); + stack_high_ptr[0] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */ + ctx->rip = (void*)(_mco_wrap_main); + ctx->rsp = (void*)(stack_high_ptr); + ctx->r12 = (void*)(_mco_main); + ctx->r13 = (void*)(co); + return MCO_SUCCESS; +} + +#endif /* not _WIN32 */ + +#elif defined(__riscv) + +typedef struct _mco_ctxbuf { + void* s[12]; /* s0-s11 */ + void* ra; + void* pc; + void* sp; +#ifdef __riscv_flen +#if __riscv_flen == 64 + double fs[12]; /* fs0-fs11 */ +#elif __riscv_flen == 32 + float fs[12]; /* fs0-fs11 */ +#endif +#endif /* __riscv_flen */ +} _mco_ctxbuf; + +void _mco_wrap_main(void); +int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to); + +__asm__( + ".text\n" + ".globl _mco_wrap_main\n" + ".type _mco_wrap_main @function\n" + ".hidden _mco_wrap_main\n" + "_mco_wrap_main:\n" + " mv a0, s0\n" + " jr s1\n" + ".size _mco_wrap_main, .-_mco_wrap_main\n" +); + +__asm__( + ".text\n" + ".globl _mco_switch\n" + ".type _mco_switch @function\n" + ".hidden _mco_switch\n" + "_mco_switch:\n" + #if __riscv_xlen == 64 + " sd s0, 0x00(a0)\n" + " sd s1, 0x08(a0)\n" + " sd s2, 0x10(a0)\n" + " sd s3, 0x18(a0)\n" + " sd s4, 0x20(a0)\n" + " sd s5, 0x28(a0)\n" + " sd s6, 0x30(a0)\n" + " sd s7, 0x38(a0)\n" + " sd s8, 0x40(a0)\n" + " sd s9, 0x48(a0)\n" + " sd s10, 0x50(a0)\n" + " sd s11, 0x58(a0)\n" + " sd ra, 0x60(a0)\n" + " sd ra, 0x68(a0)\n" /* pc */ + " sd sp, 0x70(a0)\n" + #ifdef __riscv_flen + #if __riscv_flen == 64 + " fsd fs0, 0x78(a0)\n" + " fsd fs1, 0x80(a0)\n" + " fsd fs2, 0x88(a0)\n" + " fsd fs3, 0x90(a0)\n" + " fsd fs4, 0x98(a0)\n" + " fsd fs5, 0xa0(a0)\n" + " fsd fs6, 0xa8(a0)\n" + " fsd fs7, 0xb0(a0)\n" + " fsd fs8, 0xb8(a0)\n" + " fsd fs9, 0xc0(a0)\n" + " fsd fs10, 0xc8(a0)\n" + " fsd fs11, 0xd0(a0)\n" + " fld fs0, 0x78(a1)\n" + " fld fs1, 0x80(a1)\n" + " fld fs2, 0x88(a1)\n" + " fld fs3, 0x90(a1)\n" + " fld fs4, 0x98(a1)\n" + " fld fs5, 0xa0(a1)\n" + " fld fs6, 0xa8(a1)\n" + " fld fs7, 0xb0(a1)\n" + " fld fs8, 0xb8(a1)\n" + " fld fs9, 0xc0(a1)\n" + " fld fs10, 0xc8(a1)\n" + " fld fs11, 0xd0(a1)\n" + #else + #error "Unsupported RISC-V FLEN" + #endif + #endif /* __riscv_flen */ + " ld s0, 0x00(a1)\n" + " ld s1, 0x08(a1)\n" + " ld s2, 0x10(a1)\n" + " ld s3, 0x18(a1)\n" + " ld s4, 0x20(a1)\n" + " ld s5, 0x28(a1)\n" + " ld s6, 0x30(a1)\n" + " ld s7, 0x38(a1)\n" + " ld s8, 0x40(a1)\n" + " ld s9, 0x48(a1)\n" + " ld s10, 0x50(a1)\n" + " ld s11, 0x58(a1)\n" + " ld ra, 0x60(a1)\n" + " ld a2, 0x68(a1)\n" /* pc */ + " ld sp, 0x70(a1)\n" + " jr a2\n" + #elif __riscv_xlen == 32 + " sw s0, 0x00(a0)\n" + " sw s1, 0x04(a0)\n" + " sw s2, 0x08(a0)\n" + " sw s3, 0x0c(a0)\n" + " sw s4, 0x10(a0)\n" + " sw s5, 0x14(a0)\n" + " sw s6, 0x18(a0)\n" + " sw s7, 0x1c(a0)\n" + " sw s8, 0x20(a0)\n" + " sw s9, 0x24(a0)\n" + " sw s10, 0x28(a0)\n" + " sw s11, 0x2c(a0)\n" + " sw ra, 0x30(a0)\n" + " sw ra, 0x34(a0)\n" /* pc */ + " sw sp, 0x38(a0)\n" + #ifdef __riscv_flen + #if __riscv_flen == 64 + " fsd fs0, 0x3c(a0)\n" + " fsd fs1, 0x44(a0)\n" + " fsd fs2, 0x4c(a0)\n" + " fsd fs3, 0x54(a0)\n" + " fsd fs4, 0x5c(a0)\n" + " fsd fs5, 0x64(a0)\n" + " fsd fs6, 0x6c(a0)\n" + " fsd fs7, 0x74(a0)\n" + " fsd fs8, 0x7c(a0)\n" + " fsd fs9, 0x84(a0)\n" + " fsd fs10, 0x8c(a0)\n" + " fsd fs11, 0x94(a0)\n" + " fld fs0, 0x3c(a1)\n" + " fld fs1, 0x44(a1)\n" + " fld fs2, 0x4c(a1)\n" + " fld fs3, 0x54(a1)\n" + " fld fs4, 0x5c(a1)\n" + " fld fs5, 0x64(a1)\n" + " fld fs6, 0x6c(a1)\n" + " fld fs7, 0x74(a1)\n" + " fld fs8, 0x7c(a1)\n" + " fld fs9, 0x84(a1)\n" + " fld fs10, 0x8c(a1)\n" + " fld fs11, 0x94(a1)\n" + #elif __riscv_flen == 32 + " fsw fs0, 0x3c(a0)\n" + " fsw fs1, 0x40(a0)\n" + " fsw fs2, 0x44(a0)\n" + " fsw fs3, 0x48(a0)\n" + " fsw fs4, 0x4c(a0)\n" + " fsw fs5, 0x50(a0)\n" + " fsw fs6, 0x54(a0)\n" + " fsw fs7, 0x58(a0)\n" + " fsw fs8, 0x5c(a0)\n" + " fsw fs9, 0x60(a0)\n" + " fsw fs10, 0x64(a0)\n" + " fsw fs11, 0x68(a0)\n" + " flw fs0, 0x3c(a1)\n" + " flw fs1, 0x40(a1)\n" + " flw fs2, 0x44(a1)\n" + " flw fs3, 0x48(a1)\n" + " flw fs4, 0x4c(a1)\n" + " flw fs5, 0x50(a1)\n" + " flw fs6, 0x54(a1)\n" + " flw fs7, 0x58(a1)\n" + " flw fs8, 0x5c(a1)\n" + " flw fs9, 0x60(a1)\n" + " flw fs10, 0x64(a1)\n" + " flw fs11, 0x68(a1)\n" + #else + #error "Unsupported RISC-V FLEN" + #endif + #endif /* __riscv_flen */ + " lw s0, 0x00(a1)\n" + " lw s1, 0x04(a1)\n" + " lw s2, 0x08(a1)\n" + " lw s3, 0x0c(a1)\n" + " lw s4, 0x10(a1)\n" + " lw s5, 0x14(a1)\n" + " lw s6, 0x18(a1)\n" + " lw s7, 0x1c(a1)\n" + " lw s8, 0x20(a1)\n" + " lw s9, 0x24(a1)\n" + " lw s10, 0x28(a1)\n" + " lw s11, 0x2c(a1)\n" + " lw ra, 0x30(a1)\n" + " lw a2, 0x34(a1)\n" /* pc */ + " lw sp, 0x38(a1)\n" + " jr a2\n" + #else + #error "Unsupported RISC-V XLEN" + #endif /* __riscv_xlen */ + ".size _mco_switch, .-_mco_switch\n" +); + +static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { + ctx->s[0] = (void*)(co); + ctx->s[1] = (void*)(_mco_main); + ctx->pc = (void*)(_mco_wrap_main); +#if __riscv_xlen == 64 + ctx->ra = (void*)(0xdeaddeaddeaddead); +#elif __riscv_xlen == 32 + ctx->ra = (void*)(0xdeaddead); +#endif + ctx->sp = (void*)((size_t)stack_base + stack_size); + return MCO_SUCCESS; +} + +#elif defined(__i386) || defined(__i386__) + +typedef struct _mco_ctxbuf { + void *eip, *esp, *ebp, *ebx, *esi, *edi; +} _mco_ctxbuf; + +void _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to); + +__asm__( +#ifdef __DJGPP__ /* DOS compiler */ + "__mco_switch:\n" +#else + ".text\n" + ".globl _mco_switch\n" + ".type _mco_switch @function\n" + ".hidden _mco_switch\n" + "_mco_switch:\n" +#endif + " call 1f\n" + " 1:\n" + " popl %ecx\n" + " addl $(2f-1b), %ecx\n" + " movl 4(%esp), %eax\n" + " movl 8(%esp), %edx\n" + " movl %ecx, (%eax)\n" + " movl %esp, 4(%eax)\n" + " movl %ebp, 8(%eax)\n" + " movl %ebx, 12(%eax)\n" + " movl %esi, 16(%eax)\n" + " movl %edi, 20(%eax)\n" + " movl 20(%edx), %edi\n" + " movl 16(%edx), %esi\n" + " movl 12(%edx), %ebx\n" + " movl 8(%edx), %ebp\n" + " movl 4(%edx), %esp\n" + " jmp *(%edx)\n" + " 2:\n" + " ret\n" +#ifndef __DJGPP__ + ".size _mco_switch, .-_mco_switch\n" +#endif +); + +static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { + void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - 16 - 1*sizeof(size_t)); + stack_high_ptr[0] = (void*)(0xdeaddead); /* Dummy return address. */ + stack_high_ptr[1] = (void*)(co); + ctx->eip = (void*)(_mco_main); + ctx->esp = (void*)(stack_high_ptr); + return MCO_SUCCESS; +} + +#elif defined(__ARM_EABI__) + +typedef struct _mco_ctxbuf { +#ifndef __SOFTFP__ + void* f[16]; +#endif + void *d[4]; /* d8-d15 */ + void *r[4]; /* r4-r11 */ + void *lr; + void *sp; +} _mco_ctxbuf; + +void _mco_wrap_main(void); +int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to); + +__asm__( + ".text\n" +#ifdef __APPLE__ + ".globl __mco_switch\n" + "__mco_switch:\n" +#else + ".globl _mco_switch\n" + ".type _mco_switch #function\n" + ".hidden _mco_switch\n" + "_mco_switch:\n" +#endif +#ifndef __SOFTFP__ + " vstmia r0!, {d8-d15}\n" +#endif + " stmia r0, {r4-r11, lr}\n" + " str sp, [r0, #9*4]\n" +#ifndef __SOFTFP__ + " vldmia r1!, {d8-d15}\n" +#endif + " ldr sp, [r1, #9*4]\n" + " ldmia r1, {r4-r11, pc}\n" +#ifndef __APPLE__ + ".size _mco_switch, .-_mco_switch\n" +#endif +); + +__asm__( + ".text\n" +#ifdef __APPLE__ + ".globl __mco_wrap_main\n" + "__mco_wrap_main:\n" +#else + ".globl _mco_wrap_main\n" + ".type _mco_wrap_main #function\n" + ".hidden _mco_wrap_main\n" + "_mco_wrap_main:\n" +#endif + " mov r0, r4\n" + " mov ip, r5\n" + " mov lr, r6\n" + " bx ip\n" +#ifndef __APPLE__ + ".size _mco_wrap_main, .-_mco_wrap_main\n" +#endif +); + +static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { + ctx->d[0] = (void*)(co); + ctx->d[1] = (void*)(_mco_main); + ctx->d[2] = (void*)(0xdeaddead); /* Dummy return address. */ + ctx->lr = (void*)(_mco_wrap_main); + ctx->sp = (void*)((size_t)stack_base + stack_size); + return MCO_SUCCESS; +} + +#elif defined(__aarch64__) + +typedef struct _mco_ctxbuf { + void *x[12]; /* x19-x30 */ + void *sp; + void *lr; + void *d[8]; /* d8-d15 */ +} _mco_ctxbuf; + +void _mco_wrap_main(void); +int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to); + +__asm__( + ".text\n" +#ifdef __APPLE__ + ".globl __mco_switch\n" + "__mco_switch:\n" +#else + ".globl _mco_switch\n" + ".type _mco_switch #function\n" + ".hidden _mco_switch\n" + "_mco_switch:\n" +#endif + + " mov x10, sp\n" + " mov x11, x30\n" + " stp x19, x20, [x0, #(0*16)]\n" + " stp x21, x22, [x0, #(1*16)]\n" + " stp d8, d9, [x0, #(7*16)]\n" + " stp x23, x24, [x0, #(2*16)]\n" + " stp d10, d11, [x0, #(8*16)]\n" + " stp x25, x26, [x0, #(3*16)]\n" + " stp d12, d13, [x0, #(9*16)]\n" + " stp x27, x28, [x0, #(4*16)]\n" + " stp d14, d15, [x0, #(10*16)]\n" + " stp x29, x30, [x0, #(5*16)]\n" + " stp x10, x11, [x0, #(6*16)]\n" + " ldp x19, x20, [x1, #(0*16)]\n" + " ldp x21, x22, [x1, #(1*16)]\n" + " ldp d8, d9, [x1, #(7*16)]\n" + " ldp x23, x24, [x1, #(2*16)]\n" + " ldp d10, d11, [x1, #(8*16)]\n" + " ldp x25, x26, [x1, #(3*16)]\n" + " ldp d12, d13, [x1, #(9*16)]\n" + " ldp x27, x28, [x1, #(4*16)]\n" + " ldp d14, d15, [x1, #(10*16)]\n" + " ldp x29, x30, [x1, #(5*16)]\n" + " ldp x10, x11, [x1, #(6*16)]\n" + " mov sp, x10\n" + " br x11\n" +#ifndef __APPLE__ + ".size _mco_switch, .-_mco_switch\n" +#endif +); + +__asm__( + ".text\n" +#ifdef __APPLE__ + ".globl __mco_wrap_main\n" + "__mco_wrap_main:\n" +#else + ".globl _mco_wrap_main\n" + ".type _mco_wrap_main #function\n" + ".hidden _mco_wrap_main\n" + "_mco_wrap_main:\n" +#endif + " mov x0, x19\n" + " mov x30, x21\n" + " br x20\n" +#ifndef __APPLE__ + ".size _mco_wrap_main, .-_mco_wrap_main\n" +#endif +); + +static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { + ctx->x[0] = (void*)(co); + ctx->x[1] = (void*)(_mco_main); + ctx->x[2] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */ + ctx->sp = (void*)((size_t)stack_base + stack_size); + ctx->lr = (void*)(_mco_wrap_main); + return MCO_SUCCESS; +} + +#else + +#error "Unsupported architecture for assembly method." + +#endif /* ARCH */ + +#elif defined(MCO_USE_UCONTEXT) + +#include + +typedef ucontext_t _mco_ctxbuf; + +#if defined(_LP64) || defined(__LP64__) +static void _mco_wrap_main(unsigned int lo, unsigned int hi) { + mco_coro* co = (mco_coro*)(((size_t)lo) | (((size_t)hi) << 32)); /* Extract coroutine pointer. */ + _mco_main(co); +} +#else +static void _mco_wrap_main(unsigned int lo) { + mco_coro* co = (mco_coro*)((size_t)lo); /* Extract coroutine pointer. */ + _mco_main(co); +} +#endif + +static MCO_FORCE_INLINE void _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to) { + int res = swapcontext(from, to); + _MCO_UNUSED(res); + MCO_ASSERT(res == 0); +} + +static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) { + /* Initialize ucontext. */ + if(getcontext(ctx) != 0) { + MCO_LOG("failed to get ucontext"); + return MCO_MAKE_CONTEXT_ERROR; + } + ctx->uc_link = NULL; /* We never exit from _mco_wrap_main. */ + ctx->uc_stack.ss_sp = stack_base; + ctx->uc_stack.ss_size = stack_size; + unsigned int lo = (unsigned int)((size_t)co); +#if defined(_LP64) || defined(__LP64__) + unsigned int hi = (unsigned int)(((size_t)co)>>32); + makecontext(ctx, (void (*)(void))_mco_wrap_main, 2, lo, hi); +#else + makecontext(ctx, (void (*)(void))_mco_wrap_main, 1, lo); +#endif + return MCO_SUCCESS; +} + +#endif /* defined(MCO_USE_UCONTEXT) */ + +#ifdef MCO_USE_VALGRIND +#include +#endif + +typedef struct _mco_context { +#ifdef MCO_USE_VALGRIND + unsigned int valgrind_stack_id; +#endif + _mco_ctxbuf ctx; + _mco_ctxbuf back_ctx; +} _mco_context; + +static void _mco_jumpin(mco_coro* co) { + _mco_context* context = (_mco_context*)co->context; + _mco_prepare_jumpin(co); + _mco_switch(&context->back_ctx, &context->ctx); /* Do the context switch. */ +} + +static void _mco_jumpout(mco_coro* co) { + _mco_context* context = (_mco_context*)co->context; + _mco_prepare_jumpout(co); + _mco_switch(&context->ctx, &context->back_ctx); /* Do the context switch. */ +} + +static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) { + /* Determine the context and stack address. */ + size_t co_addr = (size_t)co; + size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16); + size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16); + size_t stack_addr = _mco_align_forward(storage_addr + desc->storage_size, 16); + /* Initialize context. */ + _mco_context* context = (_mco_context*)context_addr; + memset(context, 0, sizeof(_mco_context)); + /* Initialize storage. */ + unsigned char* storage = (unsigned char*)storage_addr; + memset(storage, 0, desc->storage_size); + /* Initialize stack. */ + void *stack_base = (void*)stack_addr; + size_t stack_size = desc->stack_size; +#ifdef MCO_ZERO_MEMORY + memset(stack_base, 0, stack_size); +#endif + /* Make the context. */ + mco_result res = _mco_makectx(co, &context->ctx, stack_base, stack_size); + if(res != MCO_SUCCESS) { + return res; + } +#ifdef MCO_USE_VALGRIND + context->valgrind_stack_id = VALGRIND_STACK_REGISTER(stack_addr, stack_addr + stack_size); +#endif + co->context = context; + co->stack_base = stack_base; + co->stack_size = stack_size; + co->storage = storage; + co->storage_size = desc->storage_size; + return MCO_SUCCESS; +} + +static void _mco_destroy_context(mco_coro* co) { +#ifdef MCO_USE_VALGRIND + _mco_context* context = (_mco_context*)co->context; + if(context && context->valgrind_stack_id != 0) { + VALGRIND_STACK_DEREGISTER(context->valgrind_stack_id); + context->valgrind_stack_id = 0; + } +#else + _MCO_UNUSED(co); +#endif +} + +static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) { + desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) + + _mco_align_forward(sizeof(_mco_context), 16) + + _mco_align_forward(desc->storage_size, 16) + + stack_size + 16; + desc->stack_size = stack_size; /* This is just a hint, it won't be the real one. */ +} + +#endif /* defined(MCO_USE_UCONTEXT) || defined(MCO_USE_ASM) */ + +/* ---------------------------------------------------------------------------------------------- */ + +#ifdef MCO_USE_FIBERS + +#ifdef _WIN32 + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +typedef struct _mco_context { + void* fib; + void* back_fib; +} _mco_context; + +static void _mco_jumpin(mco_coro* co) { + void *cur_fib = GetCurrentFiber(); + if(!cur_fib || cur_fib == (void*)0x1e00) { /* See http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx */ + cur_fib = ConvertThreadToFiber(NULL); + } + MCO_ASSERT(cur_fib != NULL); + _mco_context* context = (_mco_context*)co->context; + context->back_fib = cur_fib; + _mco_prepare_jumpin(co); + SwitchToFiber(context->fib); +} + +static void CALLBACK _mco_wrap_main(void* co) { + _mco_main((mco_coro*)co); +} + +static void _mco_jumpout(mco_coro* co) { + _mco_context* context = (_mco_context*)co->context; + void* back_fib = context->back_fib; + MCO_ASSERT(back_fib != NULL); + context->back_fib = NULL; + _mco_prepare_jumpout(co); + SwitchToFiber(back_fib); +} + +/* Reverse engineered Fiber struct, used to get stack base. */ +typedef struct _mco_fiber { + LPVOID param; /* fiber param */ + void* except; /* saved exception handlers list */ + void* stack_base; /* top of fiber stack */ + void* stack_limit; /* fiber stack low-water mark */ + void* stack_allocation; /* base of the fiber stack allocation */ + CONTEXT context; /* fiber context */ + DWORD flags; /* fiber flags */ + LPFIBER_START_ROUTINE start; /* start routine */ + void **fls_slots; /* fiber storage slots */ +} _mco_fiber; + +static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) { + /* Determine the context address. */ + size_t co_addr = (size_t)co; + size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16); + size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16); + /* Initialize context. */ + _mco_context* context = (_mco_context*)context_addr; + memset(context, 0, sizeof(_mco_context)); + /* Initialize storage. */ + unsigned char* storage = (unsigned char*)storage_addr; + memset(storage, 0, desc->storage_size); + /* Create the fiber. */ + _mco_fiber* fib = (_mco_fiber*)CreateFiberEx(desc->stack_size, desc->stack_size, FIBER_FLAG_FLOAT_SWITCH, _mco_wrap_main, co); + if(!fib) { + MCO_LOG("failed to create fiber"); + return MCO_MAKE_CONTEXT_ERROR; + } + context->fib = fib; + co->context = context; + co->stack_base = (void*)((size_t)fib->stack_base - desc->stack_size); + co->stack_size = desc->stack_size; + co->storage = storage; + co->storage_size = desc->storage_size; + return MCO_SUCCESS; +} + +static void _mco_destroy_context(mco_coro* co) { + _mco_context* context = (_mco_context*)co->context; + if(context && context->fib) { + DeleteFiber(context->fib); + context->fib = NULL; + } +} + +static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) { + desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) + + _mco_align_forward(sizeof(_mco_context), 16) + + _mco_align_forward(desc->storage_size, 16) + + 16; + desc->stack_size = stack_size; +} + +#elif defined(__EMSCRIPTEN__) + +#include + +#ifndef MCO_ASYNCFY_STACK_SIZE +#define MCO_ASYNCFY_STACK_SIZE 16384 +#endif + +typedef struct _mco_context { + emscripten_fiber_t fib; + emscripten_fiber_t* back_fib; +} _mco_context; + +static emscripten_fiber_t* running_fib = NULL; +static unsigned char main_asyncify_stack[MCO_ASYNCFY_STACK_SIZE]; +static emscripten_fiber_t main_fib; + +static void _mco_wrap_main(void* co) { + _mco_main((mco_coro*)co); +} + +static void _mco_jumpin(mco_coro* co) { + _mco_context* context = (_mco_context*)co->context; + emscripten_fiber_t* back_fib = running_fib; + if(!back_fib) { + back_fib = &main_fib; + emscripten_fiber_init_from_current_context(back_fib, main_asyncify_stack, MCO_ASYNCFY_STACK_SIZE); + } + running_fib = &context->fib; + context->back_fib = back_fib; + _mco_prepare_jumpin(co); + emscripten_fiber_swap(back_fib, &context->fib); /* Do the context switch. */ +} + +static void _mco_jumpout(mco_coro* co) { + _mco_context* context = (_mco_context*)co->context; + running_fib = context->back_fib; + _mco_prepare_jumpout(co); + emscripten_fiber_swap(&context->fib, context->back_fib); /* Do the context switch. */ +} + +static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) { + if(emscripten_has_asyncify() != 1) { + MCO_LOG("failed to create fiber because ASYNCIFY is not enabled"); + return MCO_MAKE_CONTEXT_ERROR; + } + /* Determine the context address. */ + size_t co_addr = (size_t)co; + size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16); + size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16); + size_t stack_addr = _mco_align_forward(storage_addr + desc->storage_size, 16); + size_t asyncify_stack_addr = _mco_align_forward(stack_addr + desc->stack_size, 16); + /* Initialize context. */ + _mco_context* context = (_mco_context*)context_addr; + memset(context, 0, sizeof(_mco_context)); + /* Initialize storage. */ + unsigned char* storage = (unsigned char*)storage_addr; + memset(storage, 0, desc->storage_size); + /* Initialize stack. */ + void *stack_base = (void*)stack_addr; + size_t stack_size = asyncify_stack_addr - stack_addr; + void *asyncify_stack_base = (void*)asyncify_stack_addr; + size_t asyncify_stack_size = co_addr + desc->coro_size - asyncify_stack_addr; +#ifdef MCO_ZERO_MEMORY + memset(stack_base, 0, stack_size); + memset(asyncify_stack_base, 0, asyncify_stack_size); +#endif + /* Create the fiber. */ + emscripten_fiber_init(&context->fib, _mco_wrap_main, co, stack_base, stack_size, asyncify_stack_base, asyncify_stack_size); + co->context = context; + co->stack_base = stack_base; + co->stack_size = stack_size; + co->storage = storage; + co->storage_size = desc->storage_size; + return MCO_SUCCESS; +} + +static void _mco_destroy_context(mco_coro* co) { + /* Nothing to do. */ + _MCO_UNUSED(co); +} + +static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) { + desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) + + _mco_align_forward(sizeof(_mco_context), 16) + + _mco_align_forward(desc->storage_size, 16) + + _mco_align_forward(stack_size, 16) + + _mco_align_forward(MCO_ASYNCFY_STACK_SIZE, 16) + + 16; + desc->stack_size = stack_size; /* This is just a hint, it won't be the real one. */ +} + +#else + +#error "Unsupported architecture for fibers method." + +#endif + +#endif /* MCO_USE_FIBERS */ + +/* ---------------------------------------------------------------------------------------------- */ + +#ifdef MCO_USE_ASYNCIFY + +typedef struct _asyncify_stack_region { + void* start; + void* limit; +} _asyncify_stack_region; + +typedef struct _mco_context { + int rewind_id; + _asyncify_stack_region stack_region; +} _mco_context; + +__attribute__((import_module("asyncify"), import_name("start_unwind"))) void _asyncify_start_unwind(void*); +__attribute__((import_module("asyncify"), import_name("stop_unwind"))) void _asyncify_stop_unwind(); +__attribute__((import_module("asyncify"), import_name("start_rewind"))) void _asyncify_start_rewind(void*); +__attribute__((import_module("asyncify"), import_name("stop_rewind"))) void _asyncify_stop_rewind(); + +MCO_NO_INLINE void _mco_jumpin(mco_coro* co) { + _mco_context* context = (_mco_context*)co->context; + _mco_prepare_jumpin(co); + if(context->rewind_id > 0) { /* Begin rewinding until last yield point. */ + _asyncify_start_rewind(&context->stack_region); + } + _mco_main(co); /* Run the coroutine function. */ + _asyncify_stop_unwind(); /* Stop saving coroutine stack. */ +} + +static MCO_NO_INLINE void _mco_finish_jumpout(mco_coro* co, volatile int rewind_id) { + _mco_context* context = (_mco_context*)co->context; + int next_rewind_id = context->rewind_id + 1; + if(rewind_id == next_rewind_id) { /* Begins unwinding the stack (save locals and call stack to rewind later) */ + _mco_prepare_jumpout(co); + context->rewind_id = next_rewind_id; + _asyncify_start_unwind(&context->stack_region); + } else if(rewind_id == context->rewind_id) { /* Continue from yield point. */ + _asyncify_stop_rewind(); + } + /* Otherwise, we should be rewinding, let it continue... */ +} + +MCO_NO_INLINE void _mco_jumpout(mco_coro* co) { + _mco_context* context = (_mco_context*)co->context; + /* + Save rewind point into a local, that should be restored when rewinding. + That is "rewind_id != co->rewind_id + 1" may be true when rewinding. + Use volatile here just to be safe from compiler optimizing this out. + */ + volatile int rewind_id = context->rewind_id + 1; + _mco_finish_jumpout(co, rewind_id); +} + +static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) { + /* Determine the context address. */ + size_t co_addr = (size_t)co; + size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16); + size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16); + size_t stack_addr = _mco_align_forward(storage_addr + desc->storage_size, 16); + /* Initialize context. */ + _mco_context* context = (_mco_context*)context_addr; + memset(context, 0, sizeof(_mco_context)); + /* Initialize storage. */ + unsigned char* storage = (unsigned char*)storage_addr; + memset(storage, 0, desc->storage_size); + /* Initialize stack. */ + void *stack_base = (void*)stack_addr; + size_t stack_size = desc->stack_size; +#ifdef MCO_ZERO_MEMORY + memset(stack_base, 0, stack_size); +#endif + context->stack_region.start = stack_base; + context->stack_region.limit = (void*)((size_t)stack_base + stack_size); + co->context = context; + co->stack_base = stack_base; + co->stack_size = stack_size; + co->storage = storage; + co->storage_size = desc->storage_size; + return MCO_SUCCESS; +} + +static void _mco_destroy_context(mco_coro* co) { + /* Nothing to do. */ + _MCO_UNUSED(co); +} + +static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) { + desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) + + _mco_align_forward(sizeof(_mco_context), 16) + + _mco_align_forward(desc->storage_size, 16) + + _mco_align_forward(stack_size, 16) + + 16; + desc->stack_size = stack_size; /* This is just a hint, it won't be the real one. */ +} + +#endif /* MCO_USE_ASYNCIFY */ + +/* ---------------------------------------------------------------------------------------------- */ + +mco_desc mco_desc_init(void (*func)(mco_coro* co), size_t stack_size) { + if(stack_size != 0) { + /* Stack size should be at least `MCO_MIN_STACK_SIZE`. */ + if(stack_size < MCO_MIN_STACK_SIZE) { + stack_size = MCO_MIN_STACK_SIZE; + } + } else { + stack_size = MCO_DEFAULT_STACK_SIZE; + } + stack_size = _mco_align_forward(stack_size, 16); /* Stack size should be aligned to 16 bytes. */ + mco_desc desc; + memset(&desc, 0, sizeof(mco_desc)); +#ifndef MCO_NO_DEFAULT_ALLOCATORS + /* Set default allocators. */ + desc.malloc_cb = mco_malloc; + desc.free_cb = mco_free; +#endif + desc.func = func; + desc.storage_size = MCO_DEFAULT_STORAGE_SIZE; + _mco_init_desc_sizes(&desc, stack_size); + return desc; +} + +static mco_result _mco_validate_desc(mco_desc* desc) { + if(!desc) { + MCO_LOG("coroutine description is NULL"); + return MCO_INVALID_ARGUMENTS; + } + if(!desc->func) { + MCO_LOG("coroutine function in invalid"); + return MCO_INVALID_ARGUMENTS; + } + if(desc->stack_size < MCO_MIN_STACK_SIZE) { + MCO_LOG("coroutine stack size is too small"); + return MCO_INVALID_ARGUMENTS; + } + if(desc->coro_size < sizeof(mco_coro)) { + MCO_LOG("coroutine size is invalid"); + return MCO_INVALID_ARGUMENTS; + } + return MCO_SUCCESS; +} + +mco_result mco_init(mco_coro* co, mco_desc* desc) { + if(!co) { + MCO_LOG("attempt to initialize an invalid coroutine"); + return MCO_INVALID_COROUTINE; + } + memset(co, 0, sizeof(mco_coro)); + /* Validate coroutine description. */ + mco_result res = _mco_validate_desc(desc); + if(res != MCO_SUCCESS) + return res; + /* Create the coroutine. */ + res = _mco_create_context(co, desc); + if(res != MCO_SUCCESS) + return res; + co->state = MCO_SUSPENDED; /* We initialize in suspended state. */ + co->free_cb = desc->free_cb; + co->allocator_data = desc->allocator_data; + co->func = desc->func; + co->user_data = desc->user_data; +#ifdef _MCO_USE_TSAN + co->tsan_fiber = __tsan_create_fiber(0); +#endif + co->magic_number = MCO_MAGIC_NUMBER; + return MCO_SUCCESS; +} + +mco_result mco_uninit(mco_coro* co) { + if(!co) { + MCO_LOG("attempt to uninitialize an invalid coroutine"); + return MCO_INVALID_COROUTINE; + } + /* Cannot uninitialize while running. */ + if(!(co->state == MCO_SUSPENDED || co->state == MCO_DEAD)) { + MCO_LOG("attempt to uninitialize a coroutine that is not dead or suspended"); + return MCO_INVALID_OPERATION; + } + /* The coroutine is now dead and cannot be used anymore. */ + co->state = MCO_DEAD; +#ifdef _MCO_USE_TSAN + if(co->tsan_fiber != NULL) { + __tsan_destroy_fiber(co->tsan_fiber); + co->tsan_fiber = NULL; + } +#endif + _mco_destroy_context(co); + return MCO_SUCCESS; +} + +mco_result mco_create(mco_coro** out_co, mco_desc* desc) { + /* Validate input. */ + if(!out_co) { + MCO_LOG("coroutine output pointer is NULL"); + return MCO_INVALID_POINTER; + } + if(!desc || !desc->malloc_cb || !desc->free_cb) { + *out_co = NULL; + MCO_LOG("coroutine allocator description is not set"); + return MCO_INVALID_ARGUMENTS; + } + /* Allocate the coroutine. */ + mco_coro* co = (mco_coro*)desc->malloc_cb(desc->coro_size, desc->allocator_data); + if(!co) { + MCO_LOG("coroutine allocation failed"); + *out_co = NULL; + return MCO_OUT_OF_MEMORY; + } + /* Initialize the coroutine. */ + mco_result res = mco_init(co, desc); + if(res != MCO_SUCCESS) { + desc->free_cb(co, desc->allocator_data); + *out_co = NULL; + return res; + } + *out_co = co; + return MCO_SUCCESS; +} + +mco_result mco_destroy(mco_coro* co) { + if(!co) { + MCO_LOG("attempt to destroy an invalid coroutine"); + return MCO_INVALID_COROUTINE; + } + /* Uninitialize the coroutine first. */ + mco_result res = mco_uninit(co); + if(res != MCO_SUCCESS) + return res; + /* Free the coroutine. */ + if(!co->free_cb) { + MCO_LOG("attempt destroy a coroutine that has no free callback"); + return MCO_INVALID_POINTER; + } + co->free_cb(co, co->allocator_data); + return MCO_SUCCESS; +} + +mco_result mco_resume(mco_coro* co) { + if(!co) { + MCO_LOG("attempt to resume an invalid coroutine"); + return MCO_INVALID_COROUTINE; + } + if(co->state != MCO_SUSPENDED) { /* Can only resume coroutines that are suspended. */ + MCO_LOG("attempt to resume a coroutine that is not suspended"); + return MCO_NOT_SUSPENDED; + } + co->state = MCO_RUNNING; /* The coroutine is now running. */ + _mco_jumpin(co); + return MCO_SUCCESS; +} + +mco_result mco_yield(mco_coro* co) { + if(!co) { + MCO_LOG("attempt to yield an invalid coroutine"); + return MCO_INVALID_COROUTINE; + } +#ifdef MCO_USE_ASYNCIFY + /* Asyncify already checks for stack overflow. */ +#else + /* This check happens when the stack overflow already happened, but better later than never. */ + volatile size_t dummy; + size_t stack_addr = (size_t)&dummy; + size_t stack_min = (size_t)co->stack_base; + size_t stack_max = stack_min + co->stack_size; + if(co->magic_number != MCO_MAGIC_NUMBER || stack_addr < stack_min || stack_addr > stack_max) { /* Stack overflow. */ + MCO_LOG("coroutine stack overflow, try increasing the stack size"); + return MCO_STACK_OVERFLOW; + } +#endif + if(co->state != MCO_RUNNING) { /* Can only yield coroutines that are running. */ + MCO_LOG("attempt to yield a coroutine that is not running"); + return MCO_NOT_RUNNING; + } + co->state = MCO_SUSPENDED; /* The coroutine is now suspended. */ + _mco_jumpout(co); + return MCO_SUCCESS; +} + +mco_state mco_status(mco_coro* co) { + if(co != NULL) { + return co->state; + } + return MCO_DEAD; +} + +void* mco_get_user_data(mco_coro* co) { + if(co != NULL) { + return co->user_data; + } + return NULL; +} + +mco_result mco_push(mco_coro* co, const void* src, size_t len) { + if(!co) { + MCO_LOG("attempt to use an invalid coroutine"); + return MCO_INVALID_COROUTINE; + } else if(len > 0) { + size_t bytes_stored = co->bytes_stored + len; + if(bytes_stored > co->storage_size) { + MCO_LOG("attempt to push too many bytes into coroutine storage"); + return MCO_NOT_ENOUGH_SPACE; + } + if(!src) { + MCO_LOG("attempt push a null pointer into coroutine storage"); + return MCO_INVALID_POINTER; + } + memcpy(&co->storage[co->bytes_stored], src, len); + co->bytes_stored = bytes_stored; + } + return MCO_SUCCESS; +} + +mco_result mco_pop(mco_coro* co, void* dest, size_t len) { + if(!co) { + MCO_LOG("attempt to use an invalid coroutine"); + return MCO_INVALID_COROUTINE; + } else if(len > 0) { + if(len > co->bytes_stored) { + MCO_LOG("attempt to pop too many bytes from coroutine storage"); + return MCO_NOT_ENOUGH_SPACE; + } + size_t bytes_stored = co->bytes_stored - len; + if(dest) { + memcpy(dest, &co->storage[bytes_stored], len); + } + co->bytes_stored = bytes_stored; +#ifdef MCO_ZERO_MEMORY + /* Clear garbage in the discarded storage. */ + memset(&co->storage[bytes_stored], 0, len); +#endif + } + return MCO_SUCCESS; +} + +mco_result mco_peek(mco_coro* co, void* dest, size_t len) { + if(!co) { + MCO_LOG("attempt to use an invalid coroutine"); + return MCO_INVALID_COROUTINE; + } else if(len > 0) { + if(len > co->bytes_stored) { + MCO_LOG("attempt to peek too many bytes from coroutine storage"); + return MCO_NOT_ENOUGH_SPACE; + } + if(!dest) { + MCO_LOG("attempt peek into a null pointer"); + return MCO_INVALID_POINTER; + } + memcpy(dest, &co->storage[co->bytes_stored - len], len); + } + return MCO_SUCCESS; +} + +size_t mco_get_bytes_stored(mco_coro* co) { + if(co == NULL) { + return 0; + } + return co->bytes_stored; +} + +size_t mco_get_storage_size(mco_coro* co) { + if(co == NULL) { + return 0; + } + return co->storage_size; +} + +#ifdef MCO_NO_MULTITHREAD +mco_coro* mco_running(void) { + return mco_current_co; +} +#else +static MCO_NO_INLINE mco_coro* _mco_running(void) { + return mco_current_co; +} +mco_coro* mco_running(void) { + /* + Compilers aggressively optimize the use of TLS by caching loads. + Since fiber code can migrate between threads it’s possible for the load to be stale. + To prevent this from happening we avoid inline functions. + */ + mco_coro* (*volatile func)(void) = _mco_running; + return func(); +} +#endif + +const char* mco_result_description(mco_result res) { + switch(res) { + case MCO_SUCCESS: + return "No error"; + case MCO_GENERIC_ERROR: + return "Generic error"; + case MCO_INVALID_POINTER: + return "Invalid pointer"; + case MCO_INVALID_COROUTINE: + return "Invalid coroutine"; + case MCO_NOT_SUSPENDED: + return "Coroutine not suspended"; + case MCO_NOT_RUNNING: + return "Coroutine not running"; + case MCO_MAKE_CONTEXT_ERROR: + return "Make context error"; + case MCO_SWITCH_CONTEXT_ERROR: + return "Switch context error"; + case MCO_NOT_ENOUGH_SPACE: + return "Not enough space"; + case MCO_OUT_OF_MEMORY: + return "Out of memory"; + case MCO_INVALID_ARGUMENTS: + return "Invalid arguments"; + case MCO_INVALID_OPERATION: + return "Invalid operation"; + case MCO_STACK_OVERFLOW: + return "Stack overflow"; + } + return "Unknown error"; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MINICORO_IMPL */ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright (c) 2021-2022 Eduardo Bart (https://github.com/edubart/minicoro) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ diff --git a/3rdparty/minitrace/README.md b/3rdparty/minitrace/README.md index 3a3063bd0..75cdcbf05 100644 --- a/3rdparty/minitrace/README.md +++ b/3rdparty/minitrace/README.md @@ -18,35 +18,43 @@ Remember to be careful when interpreting the output. This is not a sampling prof How to use ---------- - 1. Include minitrace.c and minitrace.h in your project. #include minitrace.h in some common header. + 1. Include `minitrace.c` and `minitrace.h` in your project. `#include minitrace.h` in some common header. 2. In your initialization code: - mtr_init("trace.json"); + ```c + mtr_init("trace.json"); + ``` 3. In your exit code: - mtr_shutdown(); + ```c + mtr_shutdown(); + ``` - 4. In all functions you want to profile: + 4. Make sure `MTR_ENABLED` is defined globally when you want to profile, for example `-DMTR_ENABLED` - // C - MTR_BEGIN("GFX", "RasterizeTriangle") - ... - MTR_END("GFX", "RasterizeTriangle") + 5. In all functions you want to profile: - // C++ - MTR_SCOPE("GFX", "RasterizeTriangle") + ```c + // C + MTR_BEGIN("GFX", "RasterizeTriangle") + ... + MTR_END("GFX", "RasterizeTriangle") + ``` - 5. In Google Chrome open "about:tracing" + ```c++ + // C++ + MTR_SCOPE("GFX", "RasterizeTriangle") + ``` - 6. Click Open, and choose your trace.json + 6. In Google Chrome open "about:tracing" - 7. Navigate the trace view using the WASD keys, and Look for bottlenecks and optimize your application. + 7. Click Open, and choose your `trace.json` - 8. In your final release build, build with + 8. Navigate the trace view using the WASD keys, and Look for bottlenecks and optimize your application. - -DMTR_DISABLE + 9. In your final release build, don't forget to remove `-DMTR_ENABLED` or however you set the define. By default, it will collect 1 million tracepoints and then stop. You can change this behaviour, see the @@ -57,41 +65,43 @@ Note: Please only use string literals in MTR statements. Example code ------------ - int main(int argc, const char *argv[]) { - int i; - mtr_init("trace.json"); - - MTR_META_PROCESS_NAME("minitrace_test"); - MTR_META_THREAD_NAME("main thread"); - - int long_running_thing_1; - int long_running_thing_2; - - MTR_START("background", "long_running", &long_running_thing_1); - MTR_START("background", "long_running", &long_running_thing_2); - - MTR_BEGIN("main", "outer"); - usleep(80000); - for (i = 0; i < 3; i++) { - MTR_BEGIN("main", "inner"); - usleep(40000); - MTR_END("main", "inner"); - usleep(10000); - } - MTR_STEP("background", "long_running", &long_running_thing_1, "middle step"); - usleep(80000); - MTR_END("main", "outer"); - - usleep(50000); - MTR_INSTANT("main", "the end"); - usleep(10000); - MTR_FINISH("background", "long_running", &long_running_thing_1); - MTR_FINISH("background", "long_running", &long_running_thing_2); - - mtr_flush(); - mtr_shutdown(); - return 0; - } +```c +int main(int argc, const char *argv[]) { + int i; + mtr_init("trace.json"); + + MTR_META_PROCESS_NAME("minitrace_test"); + MTR_META_THREAD_NAME("main thread"); + + int long_running_thing_1; + int long_running_thing_2; + + MTR_START("background", "long_running", &long_running_thing_1); + MTR_START("background", "long_running", &long_running_thing_2); + + MTR_BEGIN("main", "outer"); + usleep(80000); + for (i = 0; i < 3; i++) { + MTR_BEGIN("main", "inner"); + usleep(40000); + MTR_END("main", "inner"); + usleep(10000); + } + MTR_STEP("background", "long_running", &long_running_thing_1, "middle step"); + usleep(80000); + MTR_END("main", "outer"); + + usleep(50000); + MTR_INSTANT("main", "the end"); + usleep(10000); + MTR_FINISH("background", "long_running", &long_running_thing_1); + MTR_FINISH("background", "long_running", &long_running_thing_2); + + mtr_flush(); + mtr_shutdown(); + return 0; +} +``` The output will result in something looking a little like the picture at the top of this readme. diff --git a/3rdparty/minitrace/minitrace.cpp b/3rdparty/minitrace/minitrace.cpp index b6daa5466..a3ac5f829 100644 --- a/3rdparty/minitrace/minitrace.cpp +++ b/3rdparty/minitrace/minitrace.cpp @@ -8,10 +8,13 @@ #include #include #include +#include #ifdef _WIN32 #pragma warning (disable:4996) +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif #include #define __thread __declspec(thread) #define pthread_mutex_t CRITICAL_SECTION @@ -26,11 +29,20 @@ #include #endif +#define __STDC_FORMAT_MACROS +#include + #include "minitrace.h" -#define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0]) +#ifdef __GNUC__ +#define ATTR_NORETURN __attribute__((noreturn)) +#else +#define ATTR_NORETURN +#endif -namespace minitrace { +#define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0]) +#define TRUE 1 +#define FALSE 0 // Ugh, this struct is already pretty heavy. // Will probably need to move arguments to a second buffer to support more than one. @@ -51,43 +63,50 @@ typedef struct raw_event { }; } raw_event_t; -static raw_event_t *buffer; -static volatile int count; -static int is_tracing = 0; +static raw_event_t *event_buffer; +static raw_event_t *flush_buffer; +static std::atomic_int event_count; +static int is_tracing = FALSE; +static int is_flushing = FALSE; +static int events_in_progress = 0; static int64_t time_offset; static int first_line = 1; -static FILE *file; +static FILE *f; static __thread int cur_thread_id; // Thread local storage +static int cur_process_id; static pthread_mutex_t mutex; +static pthread_mutex_t event_mutex; #define STRING_POOL_SIZE 100 static char *str_pool[100]; +// forward declaration +void mtr_flush_with_state(int); + // Tiny portability layer. // Exposes: // get_cur_thread_id() +// get_cur_process_id() // mtr_time_s() // pthread basics #ifdef _WIN32 static int get_cur_thread_id() { return (int)GetCurrentThreadId(); } +static int get_cur_process_id() { + return (int)GetCurrentProcessId(); +} static uint64_t _frequency = 0; static uint64_t _starttime = 0; - -inline int64_t mtr_time_usec(){ - static int64_t prev = 0; +double mtr_time_s() { if (_frequency == 0) { QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency); QueryPerformanceCounter((LARGE_INTEGER*)&_starttime); } __int64 time; QueryPerformanceCounter((LARGE_INTEGER*)&time); - int64_t now = static_cast( 1.0e6 * ((double)(time - _starttime) / (double)_frequency)); - if( now <= prev) now = prev + 1; - prev = now; - return now; + return ((double) (time - _starttime) / (double) _frequency); } // Ctrl+C handling for Windows console apps @@ -110,36 +129,37 @@ void mtr_register_sigint_handler() { static inline int get_cur_thread_id() { return (int)(intptr_t)pthread_self(); } +static inline int get_cur_process_id() { + return (int)getpid(); +} #if defined(BLACKBERRY) -inline int64_t mtr_time_usec(){ - static int64_t prev = 0; +double mtr_time_s() { struct timespec time; clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps - int64_t now = time.tv_sec*1000000 + time.tv_nsec / 1000; - if( now <= prev) now = prev + 1; - prev = now; - return now; + return time.tv_sec + time.tv_nsec / 1.0e9; } #else -int64_t mtr_time_usec() -{ - static int64_t prev = 0; +double mtr_time_s() { + static time_t start; struct timeval tv; - gettimeofday(&tv, nullptr); - int64_t now = 1000000*tv.tv_sec + tv.tv_usec; - if( now <= prev) now = prev + 1; - prev = now; - return now; + gettimeofday(&tv, NULL); + if (start == 0) { + start = tv.tv_sec; + } + tv.tv_sec -= start; + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; } #endif // !BLACKBERRY -static void termination_handler(int ) { +static void termination_handler(int signum) ATTR_NORETURN; +static void termination_handler(int signum) { + (void) signum; if (is_tracing) { printf("Ctrl-C detected! Flushing trace and shutting down.\n\n"); mtr_flush(); - fwrite("\n]}\n", 1, 4, file); - fclose(file); + fwrite("\n]}\n", 1, 4, f); + fclose(f); } exit(1); } @@ -155,19 +175,28 @@ void mtr_register_sigint_handler() { #endif -void mtr_init(const char *json_file) { +void mtr_init_from_stream(void *stream) { #ifndef MTR_ENABLED return; #endif - buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t)); + event_buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t)); + flush_buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t)); is_tracing = 1; - count = 0; - file = fopen(json_file, "wb"); + event_count = 0; + f = (FILE *)stream; const char *header = "{\"traceEvents\":[\n"; - fwrite(header, 1, strlen(header), file); - time_offset = mtr_time_usec(); + fwrite(header, 1, strlen(header), f); + time_offset = (uint64_t)(mtr_time_s() * 1000000); first_line = 1; pthread_mutex_init(&mutex, 0); + pthread_mutex_init(&event_mutex, 0); +} + +void mtr_init(const char *json_file) { +#ifndef MTR_ENABLED + return; +#endif + mtr_init_from_stream(fopen(json_file, "wb")); } void mtr_shutdown() { @@ -175,14 +204,18 @@ void mtr_shutdown() { #ifndef MTR_ENABLED return; #endif - is_tracing = 0; - mtr_flush(); - fwrite("\n]}\n", 1, 4, file); - fclose(file); + pthread_mutex_lock(&mutex); + is_tracing = FALSE; + pthread_mutex_unlock(&mutex); + mtr_flush_with_state(TRUE); + + fwrite("\n]}\n", 1, 4, f); + fclose(f); pthread_mutex_destroy(&mutex); - file = 0; - free(buffer); - buffer = 0; + pthread_mutex_destroy(&event_mutex); + f = 0; + free(event_buffer); + event_buffer = 0; for (i = 0; i < STRING_POOL_SIZE; i++) { if (str_pool[i]) { free(str_pool[i]); @@ -195,7 +228,7 @@ const char *mtr_pool_string(const char *str) { int i; for (i = 0; i < STRING_POOL_SIZE; i++) { if (!str_pool[i]) { - str_pool[i] = (char *)malloc(strlen(str) + 1); + str_pool[i] = (char*)malloc(strlen(str) + 1); strcpy(str_pool[i], str); return str_pool[i]; } else { @@ -210,33 +243,63 @@ void mtr_start() { #ifndef MTR_ENABLED return; #endif - is_tracing = 1; + pthread_mutex_lock(&mutex); + is_tracing = TRUE; + pthread_mutex_unlock(&mutex); } void mtr_stop() { #ifndef MTR_ENABLED return; #endif - is_tracing = 0; + pthread_mutex_lock(&mutex); + is_tracing = FALSE; + pthread_mutex_unlock(&mutex); } // TODO: fwrite more than one line at a time. -void mtr_flush() { +// Flushing is thread safe and process async +// using double-buffering mechanism. +// Aware: only one flushing process may be +// running at any point of time +void mtr_flush_with_state(int is_last) { #ifndef MTR_ENABLED return; #endif + int i = 0; char linebuf[1024]; - char arg_buf[256]; + char arg_buf[1024]; char id_buf[256]; - // We have to lock while flushing. So we really should avoid flushing as much as possible. - - + int event_count_copy = 0; + int events_in_progress_copy = 1; + raw_event_t *event_buffer_tmp = NULL; + + // small critical section to swap buffers + // - no any new events can be spawn while + // swapping since they tied to the same mutex + // - checks for any flushing in process pthread_mutex_lock(&mutex); - int old_tracing = is_tracing; - is_tracing = 0; // Stop logging even if using interlocked increments instead of the mutex. Can cause data loss. + // if not flushing already + if (is_flushing) { + pthread_mutex_unlock(&mutex); + return; + } + is_flushing = TRUE; + event_count_copy = event_count; + event_buffer_tmp = flush_buffer; + flush_buffer = event_buffer; + event_buffer = event_buffer_tmp; + event_count = 0; + // waiting for any unfinished events before swap + while (events_in_progress_copy != 0) { + pthread_mutex_lock(&event_mutex); + events_in_progress_copy = events_in_progress; + pthread_mutex_unlock(&event_mutex); + } + pthread_mutex_unlock(&mutex); - for (int i = 0; i < count; i++) { - raw_event_t *raw = &buffer[i]; + for (i = 0; i < event_count_copy; i++) { + raw_event_t *raw = &flush_buffer[i]; int len; switch (raw->arg_type) { case MTR_ARG_TYPE_INT: @@ -247,12 +310,12 @@ void mtr_flush() { break; case MTR_ARG_TYPE_STRING_COPY: if (strlen(raw->a_str) > 700) { - ((char*)raw->a_str)[700] = 0; + snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%.*s\"", raw->arg_name, 700, raw->a_str); + } else { + snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str); } - snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str); break; case MTR_ARG_TYPE_NONE: - default: arg_buf[0] = '\0'; break; } @@ -274,16 +337,15 @@ void mtr_flush() { const char *cat = raw->cat; #ifdef _WIN32 // On Windows, we often end up with backslashes in category. + char temp[256]; { - char temp[256]; - int cat_len = (int)strlen(cat); - if (cat_len > 255) - cat_len = 255; - for (int a = 0; a < cat_len; a++) - { - temp[a] = cat[a] == '\\' ? '/' : cat[a]; + int len = (int)strlen(cat); + int i; + if (len > 255) len = 255; + for (i = 0; i < len; i++) { + temp[i] = cat[i] == '\\' ? '/' : cat[i]; } - temp[cat_len] = 0; + temp[len] = 0; cat = temp; } #endif @@ -291,89 +353,138 @@ void mtr_flush() { len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}", first_line ? "" : ",\n", cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf); - fwrite(linebuf, 1, len, file); - fflush(file); + fwrite(linebuf, 1, len, f); first_line = 0; + + if (raw->arg_type == MTR_ARG_TYPE_STRING_COPY) { + free((void*)raw->a_str); + } + #ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME + free(raw->name); + free(raw->cat); + #endif } - count = 0; - is_tracing = old_tracing; + + pthread_mutex_lock(&mutex); + is_flushing = is_last; pthread_mutex_unlock(&mutex); } +void mtr_flush() { + mtr_flush_with_state(FALSE); +} + void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) { #ifndef MTR_ENABLED return; #endif - if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE) + pthread_mutex_lock(&mutex); + if (!is_tracing || event_count >= INTERNAL_MINITRACE_BUFFER_SIZE) { + pthread_mutex_unlock(&mutex); return; - int64_t ts = mtr_time_usec(); + } + raw_event_t *ev = &event_buffer[event_count]; + ++event_count; + pthread_mutex_lock(&event_mutex); + ++events_in_progress; + pthread_mutex_unlock(&event_mutex); + pthread_mutex_unlock(&mutex); + + double ts = mtr_time_s(); if (!cur_thread_id) { cur_thread_id = get_cur_thread_id(); } + if (!cur_process_id) { + cur_process_id = get_cur_process_id(); + } -#if 0 && _WIN32 // TODO: This needs testing - int bufPos = InterlockedIncrement(&count); - raw_event_t *ev = &buffer[count - 1]; -#else - pthread_mutex_lock(&mutex); - raw_event_t *ev = &buffer[count]; - count++; - pthread_mutex_unlock(&mutex); -#endif +#ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME + const size_t category_len = strlen(category); + ev->cat = malloc(category_len + 1); + strcpy(ev->cat, category); + const size_t name_len = strlen(name); + ev->name = malloc(name_len + 1); + strcpy(ev->name, name); + +#else ev->cat = category; ev->name = name; +#endif + ev->id = id; ev->ph = ph; - if (ev->ph == 'X') { - int64_t x; - memcpy(&x, id, sizeof(int64_t)); - ev->ts = x; - ev->a_double = static_cast(ts - x); - } else { - ev->ts = ts; - } - ev->tid = cur_thread_id; - ev->pid = 0; + if (ev->ph == 'X') { + double x; + memcpy(&x, id, sizeof(double)); + ev->ts = (int64_t)(x * 1000000); + ev->a_double = (ts - x) * 1000000; + } else { + ev->ts = (int64_t)(ts * 1000000); + } + ev->tid = cur_thread_id; + ev->pid = cur_process_id; + ev->arg_type = MTR_ARG_TYPE_NONE; + + pthread_mutex_lock(&event_mutex); + --events_in_progress; + pthread_mutex_unlock(&event_mutex); } void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value) { #ifndef MTR_ENABLED return; #endif - if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE) + pthread_mutex_lock(&mutex); + if (!is_tracing || event_count >= INTERNAL_MINITRACE_BUFFER_SIZE) { + pthread_mutex_unlock(&mutex); return; + } + raw_event_t *ev = &event_buffer[event_count]; + ++event_count; + pthread_mutex_lock(&event_mutex); + ++events_in_progress; + pthread_mutex_unlock(&event_mutex); + pthread_mutex_unlock(&mutex); + if (!cur_thread_id) { cur_thread_id = get_cur_thread_id(); } - int64_t ts = mtr_time_usec(); + if (!cur_process_id) { + cur_process_id = get_cur_process_id(); + } + double ts = mtr_time_s(); -#if 0 && _WIN32 // TODO: This needs testing - int bufPos = InterlockedIncrement(&count); - raw_event_t *ev = &buffer[count - 1]; -#else - pthread_mutex_lock(&mutex); - raw_event_t *ev = &buffer[count]; - count++; - pthread_mutex_unlock(&mutex); -#endif +#ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME + const size_t category_len = strlen(category); + ev->cat = malloc(category_len + 1); + strcpy(ev->cat, category); + const size_t name_len = strlen(name); + ev->name = malloc(name_len + 1); + strcpy(ev->name, name); + +#else ev->cat = category; ev->name = name; +#endif + ev->id = id; - ev->ts = ts; + ev->ts = (int64_t)(ts * 1000000); ev->ph = ph; ev->tid = cur_thread_id; - ev->pid = 0; + ev->pid = cur_process_id; ev->arg_type = arg_type; ev->arg_name = arg_name; switch (arg_type) { case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break; case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break; case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break; - default: - break; + case MTR_ARG_TYPE_NONE: break; } -} + pthread_mutex_lock(&event_mutex); + --events_in_progress; + pthread_mutex_unlock(&event_mutex); } + diff --git a/3rdparty/minitrace/minitrace.h b/3rdparty/minitrace/minitrace.h index c7d5b3126..465c1aff5 100644 --- a/3rdparty/minitrace/minitrace.h +++ b/3rdparty/minitrace/minitrace.h @@ -1,6 +1,3 @@ -#ifndef MINITRACE_H -#define MINITRACE_H - // Minitrace // // Copyright 2014 by Henrik Rydgård @@ -21,9 +18,16 @@ // More: // http://www.altdevblogaday.com/2012/08/21/using-chrometracing-to-view-your-inline-profiling-data/ +#ifndef MINITRACE_H +#define MINITRACE_H + #include -#define MTR_ENABLED +#ifdef MTR_BUILDING_WITH_CMAKE +#include "minitrace_export.h" +#else +#define MINITRACE_EXPORT +#endif // If MTR_ENABLED is not defined, Minitrace does nothing and has near zero overhead. // Preferably, set this flag in your build system. If you can't just uncomment this line. @@ -34,38 +38,42 @@ // occasionally. It's safe...ish. #define INTERNAL_MINITRACE_BUFFER_SIZE 1000000 -//#define MTR_ENABLED - -namespace minitrace { +#ifdef __cplusplus +extern "C" { +#endif // Initializes Minitrace. Must be called very early during startup of your executable, -// before any MTR_ statements.. -void mtr_init(const char *json_file); +// before any MTR_ statements. +MINITRACE_EXPORT void mtr_init(const char *json_file); +// Same as above, but allows passing in a custom stream (FILE *), as returned by +// fopen(). It should be opened for writing, preferably in binary mode to avoid +// processing of line endings (i.e. the "wb" mode). +MINITRACE_EXPORT void mtr_init_from_stream(void *stream); // Shuts down minitrace cleanly, flushing the trace buffer. -void mtr_shutdown(); +MINITRACE_EXPORT void mtr_shutdown(void); // Lets you enable and disable Minitrace at runtime. // May cause strange discontinuities in the output. // Minitrace is enabled on startup by default. -void mtr_start(); -void mtr_stop(); +MINITRACE_EXPORT void mtr_start(void); +MINITRACE_EXPORT void mtr_stop(void); // Flushes the collected data to disk, clearing the buffer for new data. -void mtr_flush(); +MINITRACE_EXPORT void mtr_flush(void); // Returns the current time in seconds. Used internally by Minitrace. No caching. -int64_t mtr_time_usec(); +MINITRACE_EXPORT double mtr_time_s(void); // Registers a handler that will flush the trace on Ctrl+C. // Works on Linux and MacOSX, and in Win32 console applications. -void mtr_register_sigint_handler(); +MINITRACE_EXPORT void mtr_register_sigint_handler(void); // Utility function that should rarely be used. // If str is semi dynamic, store it permanently in a small pool so we don't need to malloc it. // The pool fills up fast though and performance isn't great. // Returns a fixed string if the pool is full. -const char *mtr_pool_string(const char *str); +MINITRACE_EXPORT const char *mtr_pool_string(const char *str); // Commented-out types will be supported in the future. typedef enum { @@ -83,8 +91,8 @@ typedef enum { #define MTR_MAX_ARGS 1 // Only use the macros to call these. -void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id); -void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value); +MINITRACE_EXPORT void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id); +MINITRACE_EXPORT void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value); #ifdef MTR_ENABLED @@ -129,8 +137,8 @@ void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, // Instant events. For things with no duration. #define MTR_INSTANT(c, n) internal_mtr_raw_event(c, n, 'I', nullptr) -#define MTR_INSTANT_C(c, n, aname, astrval) internal_mtr_raw_event(c, n, 'I', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval)) -#define MTR_INSTANT_I(c, n, aname, aintval) internal_mtr_raw_event(c, n, 'I', 0, MTR_ARG_TYPE_INT, aname, (void *)(aintval)) +#define MTR_INSTANT_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval)) +#define MTR_INSTANT_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_INT, aname, (void *)(aintval)) // Counters (can't do multi-value counters yet) #define MTR_COUNTER(c, n, val) internal_mtr_raw_event_arg(c, n, 'C', 0, MTR_ARG_TYPE_INT, n, (void *)(intptr_t)(val)) @@ -189,7 +197,7 @@ void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, #define MTR_SCOPE_FUNC() MTR_SCOPE(__FILE__, __FUNCTION__) #define MTR_INSTANT_FUNC() MTR_INSTANT(__FILE__, __FUNCTION__) #define MTR_SCOPE_FUNC_LIMIT_S(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, l) -#define MTR_SCOPE_FUNC_LIMIT_MS(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, 1) +#define MTR_SCOPE_FUNC_LIMIT_MS(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, (double)l * 0.000001) // Same, but with a single argument of the usual types. #define MTR_BEGIN_FUNC_S(aname, arg) MTR_BEGIN_S(__FILE__, __FUNCTION__, aname, arg) @@ -204,6 +212,8 @@ void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, #define MTR_END_FUNC_I(aname, arg) MTR_END_I(__FILE__, __FUNCTION__, aname, arg) #define MTR_SCOPE_FUNC_I(aname, arg) MTR_SCOPE_I(__FILE__, __FUNCTION__, aname, arg) +#ifdef __cplusplus +} #ifdef MTR_ENABLED // These are optimized to use X events (combined B and E). Much easier to do in C++ than in C. @@ -211,7 +221,7 @@ class MTRScopedTrace { public: MTRScopedTrace(const char *category, const char *name) : category_(category), name_(name) { - start_time_ = mtr_time_usec(); + start_time_ = mtr_time_s(); } ~MTRScopedTrace() { internal_mtr_raw_event(category_, name_, 'X', &start_time_); @@ -220,19 +230,19 @@ class MTRScopedTrace { private: const char *category_; const char *name_; - int64_t start_time_; + double start_time_; }; // Only outputs a block if execution time exceeded the limit. -// TODO: This will effectively call mtr_time_usec twice at the end, which is bad. +// TODO: This will effectively call mtr_time_s twice at the end, which is bad. class MTRScopedTraceLimit { public: - MTRScopedTraceLimit(const char* category, const char* name, int64_t limit_s) + MTRScopedTraceLimit(const char *category, const char *name, double limit_s) : category_(category), name_(name), limit_(limit_s) { - start_time_ = mtr_time_usec(); + start_time_ = mtr_time_s(); } ~MTRScopedTraceLimit() { - int64_t end_time = mtr_time_usec(); + double end_time = mtr_time_s(); if (end_time - start_time_ >= limit_) { internal_mtr_raw_event(category_, name_, 'X', &start_time_); } @@ -241,8 +251,8 @@ class MTRScopedTraceLimit { private: const char *category_; const char *name_; - int64_t start_time_; - int64_t limit_; + double start_time_; + double limit_; }; class MTRScopedTraceArg { @@ -259,9 +269,8 @@ class MTRScopedTraceArg { const char *category_; const char *name_; }; - #endif -} //end namespace +#endif #endif diff --git a/3rdparty/tinyxml2/LICENSE.txt b/3rdparty/tinyxml2/LICENSE.txt new file mode 100644 index 000000000..85a6a36f0 --- /dev/null +++ b/3rdparty/tinyxml2/LICENSE.txt @@ -0,0 +1,18 @@ +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. diff --git a/3rdparty/tinyxml2/tinyxml2.cpp b/3rdparty/tinyxml2/tinyxml2.cpp index 31925d964..c5c487010 100755 --- a/3rdparty/tinyxml2/tinyxml2.cpp +++ b/3rdparty/tinyxml2/tinyxml2.cpp @@ -103,12 +103,17 @@ distribution. #if defined(_WIN64) #define TIXML_FSEEK _fseeki64 #define TIXML_FTELL _ftelli64 -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__ANDROID__) +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__CYGWIN__) #define TIXML_FSEEK fseeko #define TIXML_FTELL ftello -#elif defined(__unix__) && defined(__x86_64__) - #define TIXML_FSEEK fseeko64 - #define TIXML_FTELL ftello64 +#elif defined(__ANDROID__) + #if __ANDROID_API__ > 24 + #define TIXML_FSEEK fseeko64 + #define TIXML_FTELL ftello64 + #else + #define TIXML_FSEEK fseeko + #define TIXML_FTELL ftello + #endif #else #define TIXML_FSEEK fseek #define TIXML_FTELL ftell @@ -707,7 +712,7 @@ bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) { } -char* XMLDocument::Identify( char* p, XMLNode** node ) +char* XMLDocument::Identify( char* p, XMLNode** node, bool first ) { TIXMLASSERT( node ); TIXMLASSERT( p ); @@ -759,9 +764,19 @@ char* XMLDocument::Identify( char* p, XMLNode** node ) p += dtdHeaderLen; } else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { - returnNode = CreateUnlinkedNode( _elementPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += elementHeaderLen; + + // Preserve whitespace pedantically before closing tag, when it's immediately after opening tag + if (WhitespaceMode() == PEDANTIC_WHITESPACE && first && p != start && *(p + elementHeaderLen) == '/') { + returnNode = CreateUnlinkedNode(_textPool); + returnNode->_parseLineNum = startLine; + p = start; // Back it up, all the text counts. + _parseCurLineNum = startLine; + } + else { + returnNode = CreateUnlinkedNode(_elementPool); + returnNode->_parseLineNum = _parseCurLineNum; + p += elementHeaderLen; + } } else { returnNode = CreateUnlinkedNode( _textPool ); @@ -814,6 +829,34 @@ XMLNode::~XMLNode() } } +// ChildElementCount was originally suggested by msteiger on the sourceforge page for TinyXML and modified by KB1SPH for TinyXML-2. + +int XMLNode::ChildElementCount(const char *value) const { + int count = 0; + + const XMLElement *e = FirstChildElement(value); + + while (e) { + e = e->NextSiblingElement(value); + count++; + } + + return count; +} + +int XMLNode::ChildElementCount() const { + int count = 0; + + const XMLElement *e = FirstChildElement(); + + while (e) { + e = e->NextSiblingElement(); + count++; + } + + return count; +} + const char* XMLNode::Value() const { // Edge case: XMLDocuments don't have a Value. Return null. @@ -1062,21 +1105,23 @@ char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) if (_document->Error()) return 0; + bool first = true; while( p && *p ) { XMLNode* node = 0; - p = _document->Identify( p, &node ); + p = _document->Identify( p, &node, first ); TIXMLASSERT( p ); if ( node == 0 ) { break; } + first = false; const int initialLineNum = node->_parseLineNum; StrPair endTag; p = node->ParseDeep( p, &endTag, curLineNumPtr ); if ( !p ) { - DeleteNode( node ); + _document->DeleteNode( node ); if ( !_document->Error() ) { _document->SetError( XML_ERROR_PARSING, initialLineNum, 0); } @@ -1109,7 +1154,7 @@ char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) } if ( !wellLocated ) { _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value()); - DeleteNode( node ); + _document->DeleteNode( node ); break; } } @@ -1144,7 +1189,7 @@ char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) } if ( mismatch ) { _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name()); - DeleteNode( node ); + _document->DeleteNode( node ); break; } } @@ -1776,11 +1821,11 @@ XMLError XMLElement::QueryInt64Text(int64_t* ival) const } -XMLError XMLElement::QueryUnsigned64Text(uint64_t* ival) const +XMLError XMLElement::QueryUnsigned64Text(uint64_t* uval) const { if(FirstChild() && FirstChild()->ToText()) { const char* t = FirstChild()->Value(); - if(XMLUtil::ToUnsigned64(t, ival)) { + if(XMLUtil::ToUnsigned64(t, uval)) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; @@ -2412,21 +2457,21 @@ XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) } -XMLError XMLDocument::Parse( const char* p, size_t len ) +XMLError XMLDocument::Parse( const char* xml, size_t nBytes ) { Clear(); - if ( len == 0 || !p || !*p ) { + if ( nBytes == 0 || !xml || !*xml ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } - if ( len == static_cast(-1) ) { - len = strlen( p ); + if ( nBytes == static_cast(-1) ) { + nBytes = strlen( xml ); } TIXMLASSERT( _charBuffer == 0 ); - _charBuffer = new char[ len+1 ]; - memcpy( _charBuffer, p, len ); - _charBuffer[len] = 0; + _charBuffer = new char[ nBytes+1 ]; + memcpy( _charBuffer, xml, nBytes ); + _charBuffer[nBytes] = 0; Parse(); if ( Error() ) { diff --git a/3rdparty/tinyxml2/tinyxml2.h b/3rdparty/tinyxml2/tinyxml2.h index 452ae95bb..7586f7b8d 100755 --- a/3rdparty/tinyxml2/tinyxml2.h +++ b/3rdparty/tinyxml2/tinyxml2.h @@ -42,9 +42,6 @@ distribution. #endif #include -/* - TODO: intern strings instead of allocation. -*/ /* gcc: g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe @@ -64,7 +61,7 @@ distribution. # pragma warning(disable: 4251) #endif -#ifdef _WIN32 +#ifdef _MSC_VER # ifdef TINYXML2_EXPORT # define TINYXML2_LIB __declspec(dllexport) # elif defined(TINYXML2_IMPORT) @@ -83,27 +80,27 @@ distribution. #if defined(TINYXML2_DEBUG) # if defined(_MSC_VER) # // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like -# define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } +# define TIXMLASSERT( x ) do { if ( !((void)0,(x))) { __debugbreak(); } } while(false) # elif defined (ANDROID_NDK) # include -# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } +# define TIXMLASSERT( x ) do { if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } } while(false) # else # include # define TIXMLASSERT assert # endif #else -# define TIXMLASSERT( x ) {} +# define TIXMLASSERT( x ) do {} while(false) #endif #endif /* Versioning, past 1.0.14: http://semver.org/ */ -static const int TIXML2_MAJOR_VERSION = 9; +static const int TIXML2_MAJOR_VERSION = 10; static const int TIXML2_MINOR_VERSION = 0; static const int TIXML2_PATCH_VERSION = 0; -#define TINYXML2_MAJOR_VERSION 9 +#define TINYXML2_MAJOR_VERSION 10 #define TINYXML2_MINOR_VERSION 0 #define TINYXML2_PATCH_VERSION 0 @@ -112,7 +109,7 @@ static const int TIXML2_PATCH_VERSION = 0; // system, and the capacity of the stack. On the other hand, it's a trivial // attack that can result from ill, malicious, or even correctly formed XML, // so there needs to be a limit in place. -static const int TINYXML2_MAX_ELEMENT_DEPTH = 100; +static const int TINYXML2_MAX_ELEMENT_DEPTH = 500; namespace tinyxml2 { @@ -305,9 +302,9 @@ class DynArray if ( cap > _allocated ) { TIXMLASSERT( cap <= INT_MAX / 2 ); const int newAllocated = cap * 2; - T* newMem = new T[newAllocated]; + T* newMem = new T[static_cast(newAllocated)]; TIXMLASSERT( newAllocated >= _size ); - memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs + memcpy( newMem, _mem, sizeof(T)*static_cast(_size) ); // warning: not using constructors, only works for PODs if ( _mem != _pool ) { delete [] _mem; } @@ -317,7 +314,7 @@ class DynArray } T* _mem; - T _pool[INITIAL_SIZE]; + T _pool[static_cast(INITIAL_SIZE)]; int _allocated; // objects allocated int _size; // number objects in use }; @@ -365,17 +362,17 @@ class MemPoolT : public MemPool _nUntracked = 0; } - virtual int ItemSize() const { + virtual int ItemSize() const override{ return ITEM_SIZE; } int CurrentAllocs() const { return _currentAllocs; } - virtual void* Alloc() { + virtual void* Alloc() override{ if ( !_root ) { // Need a new block. - Block* block = new Block(); + Block* block = new Block; _blockPtrs.Push( block ); Item* blockItems = block->items; @@ -398,7 +395,7 @@ class MemPoolT : public MemPool return result; } - virtual void Free( void* mem ) { + virtual void Free( void* mem ) override { if ( !mem ) { return; } @@ -416,7 +413,7 @@ class MemPoolT : public MemPool ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); } - void SetTracked() { + void SetTracked() override { --_nUntracked; } @@ -443,7 +440,7 @@ class MemPoolT : public MemPool union Item { Item* next; - char itemData[ITEM_SIZE]; + char itemData[static_cast(ITEM_SIZE)]; }; struct Block { Item items[ITEMS_PER_BLOCK]; @@ -603,7 +600,7 @@ class TINYXML2_LIB XMLUtil TIXMLASSERT( p ); TIXMLASSERT( q ); TIXMLASSERT( nChar >= 0 ); - return strncmp( p, q, nChar ) == 0; + return strncmp( p, q, static_cast(nChar) ) == 0; } inline static bool IsUTF8Continuation( const char p ) { @@ -732,6 +729,12 @@ class TINYXML2_LIB XMLNode return 0; } + // ChildElementCount was originally suggested by msteiger on the sourceforge page for TinyXML and modified by KB1SPH for TinyXML-2. + + int ChildElementCount(const char *value) const; + + int ChildElementCount() const; + /** The meaning of 'value' changes for the specific type. @verbatim Document: empty (NULL is returned, not an empty string) @@ -992,12 +995,12 @@ class TINYXML2_LIB XMLText : public XMLNode { friend class XMLDocument; public: - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; - virtual XMLText* ToText() { + virtual XMLText* ToText() override { return this; } - virtual const XMLText* ToText() const { + virtual const XMLText* ToText() const override { return this; } @@ -1010,14 +1013,14 @@ class TINYXML2_LIB XMLText : public XMLNode return _isCData; } - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; protected: explicit XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} virtual ~XMLText() {} - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; private: bool _isCData; @@ -1032,23 +1035,23 @@ class TINYXML2_LIB XMLComment : public XMLNode { friend class XMLDocument; public: - virtual XMLComment* ToComment() { + virtual XMLComment* ToComment() override { return this; } - virtual const XMLComment* ToComment() const { + virtual const XMLComment* ToComment() const override { return this; } - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; protected: explicit XMLComment( XMLDocument* doc ); virtual ~XMLComment(); - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr) override; private: XMLComment( const XMLComment& ); // not supported @@ -1071,23 +1074,23 @@ class TINYXML2_LIB XMLDeclaration : public XMLNode { friend class XMLDocument; public: - virtual XMLDeclaration* ToDeclaration() { + virtual XMLDeclaration* ToDeclaration() override { return this; } - virtual const XMLDeclaration* ToDeclaration() const { + virtual const XMLDeclaration* ToDeclaration() const override { return this; } - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; protected: explicit XMLDeclaration( XMLDocument* doc ); virtual ~XMLDeclaration(); - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; private: XMLDeclaration( const XMLDeclaration& ); // not supported @@ -1106,23 +1109,23 @@ class TINYXML2_LIB XMLUnknown : public XMLNode { friend class XMLDocument; public: - virtual XMLUnknown* ToUnknown() { + virtual XMLUnknown* ToUnknown() override { return this; } - virtual const XMLUnknown* ToUnknown() const { + virtual const XMLUnknown* ToUnknown() const override { return this; } - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; protected: explicit XMLUnknown( XMLDocument* doc ); virtual ~XMLUnknown(); - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; private: XMLUnknown( const XMLUnknown& ); // not supported @@ -1274,13 +1277,13 @@ class TINYXML2_LIB XMLElement : public XMLNode SetValue( str, staticMem ); } - virtual XMLElement* ToElement() { + virtual XMLElement* ToElement() override { return this; } - virtual const XMLElement* ToElement() const { + virtual const XMLElement* ToElement() const override { return this; } - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none @@ -1676,11 +1679,11 @@ class TINYXML2_LIB XMLElement : public XMLNode ElementClosingType ClosingType() const { return _closingType; } - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; protected: - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; private: XMLElement( XMLDocument* doc ); @@ -1704,7 +1707,8 @@ class TINYXML2_LIB XMLElement : public XMLNode enum Whitespace { PRESERVE_WHITESPACE, - COLLAPSE_WHITESPACE + COLLAPSE_WHITESPACE, + PEDANTIC_WHITESPACE }; @@ -1728,11 +1732,11 @@ class TINYXML2_LIB XMLDocument : public XMLNode XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE ); ~XMLDocument(); - virtual XMLDocument* ToDocument() { + virtual XMLDocument* ToDocument() override { TIXMLASSERT( this == _document ); return this; } - virtual const XMLDocument* ToDocument() const { + virtual const XMLDocument* ToDocument() const override { TIXMLASSERT( this == _document ); return this; } @@ -1829,7 +1833,7 @@ class TINYXML2_LIB XMLDocument : public XMLNode @endverbatim */ void Print( XMLPrinter* streamer=0 ) const; - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; /** Create a new Element associated with @@ -1915,15 +1919,15 @@ class TINYXML2_LIB XMLDocument : public XMLNode void DeepCopy(XMLDocument* target) const; // internal - char* Identify( char* p, XMLNode** node ); + char* Identify( char* p, XMLNode** node, bool first ); // internal void MarkInUse(const XMLNode* const); - virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const override{ return 0; } - virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { + virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const override{ return false; } @@ -2286,18 +2290,18 @@ class TINYXML2_LIB XMLPrinter : public XMLVisitor void PushDeclaration( const char* value ); void PushUnknown( const char* value ); - virtual bool VisitEnter( const XMLDocument& /*doc*/ ); - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) override; + virtual bool VisitExit( const XMLDocument& /*doc*/ ) override { return true; } - virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); - virtual bool VisitExit( const XMLElement& element ); + virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) override; + virtual bool VisitExit( const XMLElement& element ) override; - virtual bool Visit( const XMLText& text ); - virtual bool Visit( const XMLComment& comment ); - virtual bool Visit( const XMLDeclaration& declaration ); - virtual bool Visit( const XMLUnknown& unknown ); + virtual bool Visit( const XMLText& text ) override; + virtual bool Visit( const XMLComment& comment ) override; + virtual bool Visit( const XMLDeclaration& declaration ) override; + virtual bool Visit( const XMLUnknown& unknown ) override; /** If in print to memory mode, return a pointer to diff --git a/3rdparty/wildcards/LICENSE_1_0.txt b/3rdparty/wildcards/LICENSE_1_0.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/3rdparty/wildcards/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/3rdparty/wildcards/README.md b/3rdparty/wildcards/README.md new file mode 100644 index 000000000..72b4a6207 --- /dev/null +++ b/3rdparty/wildcards/README.md @@ -0,0 +1,215 @@ +[![language.badge]][language.url] [![standard.badge]][standard.url] [![license.badge]][license.url] [![travis.badge]][travis.url] [![appveyor.badge]][appveyor.url] [![release.badge]][release.url] [![godbolt.badge]][godbolt.url] [![wandbox.badge]][wandbox.url] + +# Wildcards + +*Wildcards* is a simple C++ header-only template library which implements +a general purpose algorithm for matching using wildcards. It supports both +runtime and compile time execution. + +## Basic usage + +The following examples of the basic usage are functionaly equivalent. + +```C++ +#include + +int main() +{ + { + using wildcards::match; + + static_assert(match("Hello, World!", "H*World?"), ""); + } + + { + using wildcards::make_matcher; + + static_assert(make_matcher("H*World?").matches("Hello, World!"), ""); + } + + { + using namespace wildcards::literals; + + static_assert("H*World?"_wc.matches("Hello, World!"), ""); + } + + return 0; +} +``` + +## Advanced usage + +The following examples of the advanced usage are functionaly equivalent. + +```C++ +#include + +int main() +{ + { + using wildcards::match; + + static_assert(match("Hello, World!", "H%World_", {'%', '_', '\\'}), ""); + } + + { + using wildcards::make_matcher; + + static_assert(make_matcher("H%World_", {'%', '_', '\\'}).matches("Hello, World!"), ""); + } + + return 0; +} +``` + +See more useful and complex [examples](example) and try them online! See also +[the tests](test/src/wildcards) to learn more. + +## Demonstration on Compiler Explorer + +Check compilers output of the following example on [Compiler Explorer][godbolt.url]. + +```C++ +#include + +using namespace wildcards::literals; + +constexpr auto pattern = "*.[hc](pp|)"_wc; + +// returns true +bool test1() +{ + constexpr auto res = pattern.matches("source.c"); + + static_assert(res, "must be true"); + + return res; +} + +// returns false +bool test2() +{ + constexpr auto res = pattern.matches("source.cc"); + + static_assert(!res, "must be false"); + + return res; +} +``` + +## Integration + +1. Single-header approach + * Copy [`wildcards.hpp`](single_include/wildcards.hpp) from + [`single_include`](single_include) directory to your project's header + search path. + * Add `#include ` to your source file. + * Use `wildcards::match()` or `wildcards::make_matcher()`. You can also use + operator `""_wc` from `wildcards::literals` namespace. + +2. Multi-header approach + * Add [`include`](include) directory to your project's header search path. + * Add `#include ` to your source file. + * Use `wildcards::match()` or `wildcards::make_matcher()`. You can also use + operator `""_wc` from `wildcards::literals` namespace. + +## Portability + +The library requires at least a C++11 compiler to build. It has no external +dependencies. + +The following compilers are continuously tested at [Travis CI][travis.url] +and [Appveyor CI][appveyor.url]. + +| Compiler | Version | Operating System | Notes | +|---------------------|---------|---------------------|-------------------------| +| Xcode | 9.0 | OS X 10.12 | C++11/14/17 | +| Clang (with libcxx) | 3.9 | Ubuntu 14.04 LTS | C++14/17 | +| Clang (with libcxx) | 4.0 | Ubuntu 14.04 LTS | C++11/14/17 | +| Clang (with libcxx) | 5.0 | Ubuntu 14.04 LTS | C++11/14/17 | +| Clang (with libcxx) | 6.0 | Ubuntu 14.04 LTS | C++11/14/17 | +| GCC | 5.5 | Ubuntu 14.04 LTS | C++11/14/17 | +| GCC | 6.4 | Ubuntu 14.04 LTS | C++11/14/17 | +| GCC | 7.3 | Ubuntu 14.04 LTS | C++11/14/17 | +| GCC | 8.1 | Ubuntu 14.04 LTS | C++11/14/17 | +| DJGPP | 7.2 | Ubuntu 14.04 LTS | C++11/14/17, build only | +| Visual Studio | 14 2015 | Windows Server 2016 | C++11/14/17, limited | +| Visual Studio | 15 2017 | Windows Server 2016 | C++11/14/17 | +| MinGW | 6.3 | Windows Server 2016 | C++11/14/17 | +| MinGW | 7.2 | Windows Server 2016 | C++11/14/17 | +| MinGW | 7.3 | Windows Server 2016 | C++11/14/17 | + +## License + +This project is licensed under the [Boost 1.0][license.url]. + +## Details + +### Syntax + +| Pattern | Meaning | +| --------- | ---------------------------------------------- | +| `*` | Matches everything. | +| `?` | Matches any single character. | +| `\` | Escape character. | +| `[abc]` | Matches any character in *Set*. | +| `[!abc]` | Matches any character not in *Set*. | +| `(ab\|c)` | Matches one of the sequences in *Alternative*. | + +* *Set* cannot be empty. Any special character loses its special meaning in it. +* *Alternative* can contain more than two or just one sequence. +* The use of *Sets* and *Alternatives* can be switched off. +* Special characters are predefined for `char`, `char16_t`, `char32_t` + and `wchar_t`, but can be redefined. + +### Technical Notes + +* *Wildcards* depends on two components which originate from external sources + and were made part of the repository: + * [`cpp_feature.hpp`](include/cpp_feature.hpp) taken from + [here](https://github.com/ned14/quickcpplib/blob/master/include/cpp_feature.h), + * [`catch.hpp`](test/include/catch.hpp) taken from + [here](https://github.com/catchorg/Catch2/releases/download/v2.4.2/catch.hpp). + +* *Wildcards* uses a recursive approach. Hence you can simply run out of stack + (during runtime execution) or you can exceed the maximum depth of constexpr + evaluation (during compile time execution). If so, try making the input + sequence shorter or the pattern less complex. You can also try to build using + the C++14 standard since the C++14 implementation of the library is more + effective and consumes less resources. + +* Place more specific sequences in *Alternatives* first. This becomes important + when *Alternatives* are nested. E.g. `match("source.cpp", "(*.[hc](pp|))")` + will work as expected but `match("source.cpp", "(*.[hc](|pp))")` will not. + Fixing that would make *Wildcards* unreasonably complex. + +* The `cx` library is a byproduct created during the development of *Wildcards* + which uses some pieces from its functionality internally. More of the `cx` is + used in tests and examples. You can use this library in exactly the same way + as you use *Wildcards* (single-header / multi-header approach) but if you are + interested only in *Wildcards*, you don't need to care about the `cx` at all. + This library might become a separate project in the future. + +[language.url]: https://isocpp.org/ +[language.badge]: https://img.shields.io/badge/language-C++-blue.svg + +[standard.url]: https://en.wikipedia.org/wiki/C%2B%2B#Standardization +[standard.badge]: https://img.shields.io/badge/C%2B%2B-11%2F14%2F17-blue.svg + +[license.url]: http://www.boost.org/LICENSE_1_0.txt +[license.badge]: https://img.shields.io/badge/license-Boost%201.0-blue.svg + +[travis.url]: https://travis-ci.org/zemasoft/wildcards +[travis.badge]: https://travis-ci.org/zemasoft/wildcards.svg?branch=master + +[appveyor.url]: https://ci.appveyor.com/project/zemasoft/wildcards +[appveyor.badge]: https://ci.appveyor.com/api/projects/status/github/zemasoft/wildcards?svg=true&branch=master + +[release.url]: https://github.com/zemasoft/wildcards/releases +[release.badge]: https://img.shields.io/github/release/zemasoft/wildcards.svg + +[godbolt.url]: https://godbolt.org/z/nPr4h7 +[godbolt.badge]: https://img.shields.io/badge/try%20it-on%20godbolt-blue.svg + +[wandbox.url]: https://github.com/zemasoft/wildcards/tree/master/example +[wandbox.badge]: https://img.shields.io/badge/try%20it-on%20wandbox-blue.svg diff --git a/3rdparty/wildcards/wildcards.hpp b/3rdparty/wildcards/wildcards.hpp new file mode 100644 index 000000000..df4f236c9 --- /dev/null +++ b/3rdparty/wildcards/wildcards.hpp @@ -0,0 +1,1830 @@ +// THIS FILE HAS BEEN GENERATED AUTOMATICALLY. DO NOT EDIT DIRECTLY. +// Generated: 2019-03-08 09:59:35.958950200 +// Copyright Tomas Zeman 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#ifndef WILDCARDS_HPP +#define WILDCARDS_HPP +#define WILDCARDS_VERSION_MAJOR 1 +#define WILDCARDS_VERSION_MINOR 5 +#define WILDCARDS_VERSION_PATCH 0 +#ifndef WILDCARDS_CARDS_HPP +#define WILDCARDS_CARDS_HPP +#include +namespace wildcards +{ +template +struct cards +{ +constexpr cards(T a, T s, T e) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{false}, +alt_enabled{false} +{ +} +constexpr cards(T a, T s, T e, T so, T sc, T sn, T ao, T ac, T ar) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{true}, +set_open{std::move(so)}, +set_close{std::move(sc)}, +set_not{std::move(sn)}, +alt_enabled{true}, +alt_open{std::move(ao)}, +alt_close{std::move(ac)}, +alt_or{std::move(ar)} +{ +} +T anything; +T single; +T escape; +bool set_enabled; +T set_open; +T set_close; +T set_not; +bool alt_enabled; +T alt_open; +T alt_close; +T alt_or; +}; +enum class cards_type +{ +standard, +extended +}; +template <> +struct cards +{ +constexpr cards(cards_type type = cards_type::extended) +: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} +{ +} +constexpr cards(char a, char s, char e) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{false}, +alt_enabled{false} +{ +} +constexpr cards(char a, char s, char e, char so, char sc, char sn, char ao, char ac, char ar) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{true}, +set_open{std::move(so)}, +set_close{std::move(sc)}, +set_not{std::move(sn)}, +alt_enabled{true}, +alt_open{std::move(ao)}, +alt_close{std::move(ac)}, +alt_or{std::move(ar)} +{ +} +char anything{'*'}; +char single{'?'}; +char escape{'\\'}; +bool set_enabled{true}; +char set_open{'['}; +char set_close{']'}; +char set_not{'!'}; +bool alt_enabled{true}; +char alt_open{'('}; +char alt_close{')'}; +char alt_or{'|'}; +}; +template <> +struct cards +{ +constexpr cards(cards_type type = cards_type::extended) +: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} +{ +} +constexpr cards(char16_t a, char16_t s, char16_t e) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{false}, +alt_enabled{false} +{ +} +constexpr cards(char16_t a, char16_t s, char16_t e, char16_t so, char16_t sc, char16_t sn, +char16_t ao, char16_t ac, char16_t ar) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{true}, +set_open{std::move(so)}, +set_close{std::move(sc)}, +set_not{std::move(sn)}, +alt_enabled{true}, +alt_open{std::move(ao)}, +alt_close{std::move(ac)}, +alt_or{std::move(ar)} +{ +} +char16_t anything{u'*'}; +char16_t single{u'?'}; +char16_t escape{u'\\'}; +bool set_enabled{true}; +char16_t set_open{u'['}; +char16_t set_close{u']'}; +char16_t set_not{u'!'}; +bool alt_enabled{true}; +char16_t alt_open{u'('}; +char16_t alt_close{u')'}; +char16_t alt_or{u'|'}; +}; +template <> +struct cards +{ +constexpr cards(cards_type type = cards_type::extended) +: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} +{ +} +constexpr cards(char32_t a, char32_t s, char32_t e) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{false}, +alt_enabled{false} +{ +} +constexpr cards(char32_t a, char32_t s, char32_t e, char32_t so, char32_t sc, char32_t sn, +char32_t ao, char32_t ac, char32_t ar) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{true}, +set_open{std::move(so)}, +set_close{std::move(sc)}, +set_not{std::move(sn)}, +alt_enabled{true}, +alt_open{std::move(ao)}, +alt_close{std::move(ac)}, +alt_or{std::move(ar)} +{ +} +char32_t anything{U'*'}; +char32_t single{U'?'}; +char32_t escape{U'\\'}; +bool set_enabled{true}; +char32_t set_open{U'['}; +char32_t set_close{U']'}; +char32_t set_not{U'!'}; +bool alt_enabled{true}; +char32_t alt_open{U'('}; +char32_t alt_close{U')'}; +char32_t alt_or{U'|'}; +}; +template <> +struct cards +{ +constexpr cards(cards_type type = cards_type::extended) +: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} +{ +} +constexpr cards(wchar_t a, wchar_t s, wchar_t e) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{false}, +alt_enabled{false} +{ +} +constexpr cards(wchar_t a, wchar_t s, wchar_t e, wchar_t so, wchar_t sc, wchar_t sn, wchar_t ao, +wchar_t ac, wchar_t ar) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{true}, +set_open{std::move(so)}, +set_close{std::move(sc)}, +set_not{std::move(sn)}, +alt_enabled{true}, +alt_open{std::move(ao)}, +alt_close{std::move(ac)}, +alt_or{std::move(ar)} +{ +} +wchar_t anything{L'*'}; +wchar_t single{L'?'}; +wchar_t escape{L'\\'}; +bool set_enabled{true}; +wchar_t set_open{L'['}; +wchar_t set_close{L']'}; +wchar_t set_not{L'!'}; +bool alt_enabled{true}; +wchar_t alt_open{L'('}; +wchar_t alt_close{L')'}; +wchar_t alt_or{L'|'}; +}; +template +constexpr cards make_cards(T&& a, T&& s, T&& e) +{ +return {std::forward(a), std::forward(s), std::forward(e)}; +} +template +constexpr cards make_cards(T&& a, T&& s, T&& e, T&& so, T&& sc, T&& sn, T&& ao, T&& ac, T&& ar) +{ +return {std::forward(a), std::forward(s), std::forward(e), +std::forward(so), std::forward(sc), std::forward(sn), +std::forward(ao), std::forward(ac), std::forward(ar)}; +} +} +#endif +#ifndef WILDCARDS_MATCH_HPP +#define WILDCARDS_MATCH_HPP +#include +#include +#include +#ifndef CONFIG_HPP +#define CONFIG_HPP +#ifndef QUICKCPPLIB_HAS_FEATURE_H +#define QUICKCPPLIB_HAS_FEATURE_H +#if __cplusplus >= 201103L +#if !defined(__cpp_alias_templates) +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) +#if __cplusplus >= 201402L +#define __cpp_constexpr 201304 +#else +#define __cpp_constexpr 190000 +#endif +#endif +#if !defined(__cpp_decltype) +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) +#define __cpp_ref_qualifiers 190000 +#endif +#if !defined(__cpp_rvalue_references) +#define __cpp_rvalue_references 190000 +#endif +#if !defined(__cpp_static_assert) +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_unicode_characters) +#define __cpp_unicode_characters 190000 +#endif +#if !defined(__cpp_unicode_literals) +#define __cpp_unicode_literals 190000 +#endif +#if !defined(__cpp_user_defined_literals) +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) +#define __cpp_variadic_templates 190000 +#endif +#endif +#if __cplusplus >= 201402L +#if !defined(__cpp_aggregate_nsdmi) +#define __cpp_aggregate_nsdmi 190000 +#endif +#if !defined(__cpp_binary_literals) +#define __cpp_binary_literals 190000 +#endif +#if !defined(__cpp_decltype_auto) +#define __cpp_decltype_auto 190000 +#endif +#if !defined(__cpp_generic_lambdas) +#define __cpp_generic_lambdas 190000 +#endif +#if !defined(__cpp_init_captures) +#define __cpp_init_captures 190000 +#endif +#if !defined(__cpp_return_type_deduction) +#define __cpp_return_type_deduction 190000 +#endif +#if !defined(__cpp_sized_deallocation) +#define __cpp_sized_deallocation 190000 +#endif +#if !defined(__cpp_variable_templates) +#define __cpp_variable_templates 190000 +#endif +#endif +#if defined(_MSC_VER) && !defined(__clang__) +#if !defined(__cpp_exceptions) && defined(_CPPUNWIND) +#define __cpp_exceptions 190000 +#endif +#if !defined(__cpp_rtti) && defined(_CPPRTTI) +#define __cpp_rtti 190000 +#endif +#if !defined(__cpp_alias_templates) && _MSC_VER >= 1800 +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) && _MSC_FULL_VER >= 190023506 +#define __cpp_constexpr 190000 +#endif +#if !defined(__cpp_decltype) && _MSC_VER >= 1600 +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) && _MSC_VER >= 1800 +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) && _MSC_VER >= 1800 +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) && _MSC_VER >= 1900 +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) && _MSC_VER >= 1900 +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) && _MSC_VER >= 1600 +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) && _MSC_VER >= 1900 +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) && _MSC_VER >= 1700 +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) && _MSC_VER >= 1800 +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) && _MSC_VER >= 1900 +#define __cpp_ref_qualifiers 190000 +#endif +#if !defined(__cpp_rvalue_references) && _MSC_VER >= 1600 +#define __cpp_rvalue_references 190000 +#endif +#if !defined(__cpp_static_assert) && _MSC_VER >= 1600 +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_user_defined_literals) && _MSC_VER >= 1900 +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) && _MSC_VER >= 1800 +#define __cpp_variadic_templates 190000 +#endif +#if !defined(__cpp_binary_literals) && _MSC_VER >= 1900 +#define __cpp_binary_literals 190000 +#endif +#if !defined(__cpp_decltype_auto) && _MSC_VER >= 1900 +#define __cpp_decltype_auto 190000 +#endif +#if !defined(__cpp_generic_lambdas) && _MSC_VER >= 1900 +#define __cpp_generic_lambdas 190000 +#endif +#if !defined(__cpp_init_captures) && _MSC_VER >= 1900 +#define __cpp_init_captures 190000 +#endif +#if !defined(__cpp_return_type_deduction) && _MSC_VER >= 1900 +#define __cpp_return_type_deduction 190000 +#endif +#if !defined(__cpp_sized_deallocation) && _MSC_VER >= 1900 +#define __cpp_sized_deallocation 190000 +#endif +#if !defined(__cpp_variable_templates) && _MSC_FULL_VER >= 190023506 +#define __cpp_variable_templates 190000 +#endif +#endif +#if(defined(__GNUC__) && !defined(__clang__)) +#define QUICKCPPLIB_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if !defined(__cpp_exceptions) && defined(__EXCEPTIONS) +#define __cpp_exceptions 190000 +#endif +#if !defined(__cpp_rtti) && defined(__GXX_RTTI) +#define __cpp_rtti 190000 +#endif +#if defined(__GXX_EXPERIMENTAL_CXX0X__) +#if !defined(__cpp_alias_templates) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) && (QUICKCPPLIB_GCC >= 40800) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) && (QUICKCPPLIB_GCC >= 40600) +#define __cpp_constexpr 190000 +#endif +#if !defined(__cpp_decltype) && (QUICKCPPLIB_GCC >= 40300) +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) && (QUICKCPPLIB_GCC >= 40800) +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) && (QUICKCPPLIB_GCC >= 40800) +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) && (QUICKCPPLIB_GCC >= 40600) +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) && (QUICKCPPLIB_GCC >= 40801) +#define __cpp_ref_qualifiers 190000 +#endif +#if !defined(__cpp_rvalue_references) && defined(__cpp_rvalue_reference) +#define __cpp_rvalue_references __cpp_rvalue_reference +#endif +#if !defined(__cpp_static_assert) && (QUICKCPPLIB_GCC >= 40300) +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_unicode_characters) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_unicode_characters 190000 +#endif +#if !defined(__cpp_unicode_literals) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_unicode_literals 190000 +#endif +#if !defined(__cpp_user_defined_literals) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) && (QUICKCPPLIB_GCC >= 40400) +#define __cpp_variadic_templates 190000 +#endif +#endif +#endif +#if defined(__clang__) +#define QUICKCPPLIB_CLANG (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) +#if !defined(__cpp_exceptions) && (defined(__EXCEPTIONS) || defined(_CPPUNWIND)) +#define __cpp_exceptions 190000 +#endif +#if !defined(__cpp_rtti) && (defined(__GXX_RTTI) || defined(_CPPRTTI)) +#define __cpp_rtti 190000 +#endif +#if defined(__GXX_EXPERIMENTAL_CXX0X__) +#if !defined(__cpp_alias_templates) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) && (QUICKCPPLIB_CLANG >= 30300) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_constexpr 190000 +#endif +#if !defined(__cpp_decltype) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) && (QUICKCPPLIB_CLANG >= 30300) +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) && defined(__cpp_raw_string_literals) +#define __cpp_raw_strings __cpp_raw_string_literals +#endif +#if !defined(__cpp_raw_strings) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_ref_qualifiers 190000 +#endif +#if !defined(__cpp_rvalue_references) && defined(__cpp_rvalue_reference) +#define __cpp_rvalue_references __cpp_rvalue_reference +#endif +#if !defined(__cpp_rvalue_references) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_rvalue_references 190000 +#endif +#if !defined(__cpp_static_assert) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_unicode_characters) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_unicode_characters 190000 +#endif +#if !defined(__cpp_unicode_literals) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_unicode_literals 190000 +#endif +#if !defined(__cpp_user_defined_literals) && defined(__cpp_user_literals) +#define __cpp_user_defined_literals __cpp_user_literals +#endif +#if !defined(__cpp_user_defined_literals) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_variadic_templates 190000 +#endif +#endif +#endif +#endif +#define cfg_HAS_CONSTEXPR14 (__cpp_constexpr >= 201304) +#if cfg_HAS_CONSTEXPR14 +#define cfg_constexpr14 constexpr +#else +#define cfg_constexpr14 +#endif +#if cfg_HAS_CONSTEXPR14 && defined(__clang__) +#define cfg_HAS_FULL_FEATURED_CONSTEXPR14 1 +#else +#define cfg_HAS_FULL_FEATURED_CONSTEXPR14 0 +#endif +#endif +#ifndef CX_FUNCTIONAL_HPP +#define CX_FUNCTIONAL_HPP +#include +namespace cx +{ +template +struct less +{ +constexpr auto operator()(const T& lhs, const T& rhs) const -> decltype(lhs < rhs) +{ +return lhs < rhs; +} +}; +template <> +struct less +{ +template +constexpr auto operator()(T&& lhs, U&& rhs) const +-> decltype(std::forward(lhs) < std::forward(rhs)) +{ +return std::forward(lhs) < std::forward(rhs); +} +}; +template +struct equal_to +{ +constexpr auto operator()(const T& lhs, const T& rhs) const -> decltype(lhs == rhs) +{ +return lhs == rhs; +} +}; +template <> +struct equal_to +{ +template +constexpr auto operator()(T&& lhs, U&& rhs) const +-> decltype(std::forward(lhs) == std::forward(rhs)) +{ +return std::forward(lhs) == std::forward(rhs); +} +}; +} +#endif +#ifndef CX_ITERATOR_HPP +#define CX_ITERATOR_HPP +#include +#include +namespace cx +{ +template +constexpr It next(It it) +{ +return it + 1; +} +template +constexpr It prev(It it) +{ +return it - 1; +} +template +constexpr auto size(const C& c) -> decltype(c.size()) +{ +return c.size(); +} +template +constexpr std::size_t size(const T (&)[N]) +{ +return N; +} +template +constexpr auto empty(const C& c) -> decltype(c.empty()) +{ +return c.empty(); +} +template +constexpr bool empty(const T (&)[N]) +{ +return false; +} +template +constexpr bool empty(std::initializer_list il) +{ +return il.size() == 0; +} +template +constexpr auto begin(const C& c) -> decltype(c.begin()) +{ +return c.begin(); +} +template +constexpr auto begin(C& c) -> decltype(c.begin()) +{ +return c.begin(); +} +template +constexpr T* begin(T (&array)[N]) +{ +return &array[0]; +} +template +constexpr const E* begin(std::initializer_list il) +{ +return il.begin(); +} +template +constexpr auto cbegin(const C& c) -> decltype(cx::begin(c)) +{ +return cx::begin(c); +} +template +constexpr auto end(const C& c) -> decltype(c.end()) +{ +return c.end(); +} +template +constexpr auto end(C& c) -> decltype(c.end()) +{ +return c.end(); +} +template +constexpr T* end(T (&array)[N]) +{ +return &array[N]; +} +template +constexpr const E* end(std::initializer_list il) +{ +return il.end(); +} +template +constexpr auto cend(const C& c) -> decltype(cx::end(c)) +{ +return cx::end(c); +} +} +#endif +#ifndef WILDCARDS_UTILITY_HPP +#define WILDCARDS_UTILITY_HPP +#include +#include +namespace wildcards +{ +template +struct const_iterator +{ +using type = typename std::remove_cv< +typename std::remove_reference()))>::type>::type; +}; +template +using const_iterator_t = typename const_iterator::type; +template +struct iterator +{ +using type = typename std::remove_cv< +typename std::remove_reference()))>::type>::type; +}; +template +using iterator_t = typename iterator::type; +template +struct iterated_item +{ +using type = typename std::remove_cv< +typename std::remove_reference())>::type>::type; +}; +template +using iterated_item_t = typename iterated_item::type; +template +struct container_item +{ +using type = typename std::remove_cv< +typename std::remove_reference()))>::type>::type; +}; +template +using container_item_t = typename container_item::type; +} +#endif +namespace wildcards +{ +template +struct full_match_result +{ +bool res; +SequenceIterator s, send, s1; +PatternIterator p, pend, p1; +constexpr operator bool() const +{ +return res; +} +}; +namespace detail +{ +template +struct match_result +{ +bool res; +SequenceIterator s; +PatternIterator p; +constexpr operator bool() const +{ +return res; +} +}; +template +constexpr match_result make_match_result(bool res, +SequenceIterator s, +PatternIterator p) +{ +return {std::move(res), std::move(s), std::move(p)}; +} +template +constexpr full_match_result make_full_match_result( +SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, +match_result mr) +{ +return {std::move(mr.res), std::move(s), std::move(send), std::move(mr.s), +std::move(p), std::move(pend), std::move(mr.p)}; +} +#if !cfg_HAS_FULL_FEATURED_CONSTEXPR14 +constexpr bool throw_invalid_argument(const char* what_arg) +{ +return what_arg == nullptr ? false : throw std::invalid_argument(what_arg); +} +template +constexpr T throw_invalid_argument(T t, const char* what_arg) +{ +return what_arg == nullptr ? t : throw std::invalid_argument(what_arg); +} +constexpr bool throw_logic_error(const char* what_arg) +{ +return what_arg == nullptr ? false : throw std::logic_error(what_arg); +} +template +constexpr T throw_logic_error(T t, const char* what_arg) +{ +return what_arg == nullptr ? t : throw std::logic_error(what_arg); +} +#endif +enum class is_set_state +{ +open, +not_or_first, +first, +next +}; +template +constexpr bool is_set( +PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +is_set_state state = is_set_state::open) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.set_enabled) +{ +return false; +} +while (p != pend) +{ +switch (state) +{ +case is_set_state::open: +if (*p != c.set_open) +{ +return false; +} +state = is_set_state::not_or_first; +break; +case is_set_state::not_or_first: +if (*p == c.set_not) +{ +state = is_set_state::first; +} +else +{ +state = is_set_state::next; +} +break; +case is_set_state::first: +state = is_set_state::next; +break; +case is_set_state::next: +if (*p == c.set_close) +{ +return true; +} +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +"The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +return false; +#else +return c.set_enabled && p != pend && +(state == is_set_state::open +? *p == c.set_open && is_set(cx::next(p), pend, c, is_set_state::not_or_first) +: +state == is_set_state::not_or_first +? *p == c.set_not ? is_set(cx::next(p), pend, c, is_set_state::first) +: is_set(cx::next(p), pend, c, is_set_state::next) +: state == is_set_state::first +? is_set(cx::next(p), pend, c, is_set_state::next) +: state == is_set_state::next +? *p == c.set_close || +is_set(cx::next(p), pend, c, is_set_state::next) +: throw std::logic_error("The program execution should never end up " +"here throwing this exception")); +#endif +} +enum class set_end_state +{ +open, +not_or_first, +first, +next +}; +template +constexpr PatternIterator set_end( +PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +set_end_state state = set_end_state::open) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.set_enabled) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The use of sets is disabled"); +#else +return throw_invalid_argument(p, "The use of sets is disabled"); +#endif +} +while (p != pend) +{ +switch (state) +{ +case set_end_state::open: +if (*p != c.set_open) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid set"); +#else +return throw_invalid_argument(p, "The given pattern is not a valid set"); +#endif +} +state = set_end_state::not_or_first; +break; +case set_end_state::not_or_first: +if (*p == c.set_not) +{ +state = set_end_state::first; +} +else +{ +state = set_end_state::next; +} +break; +case set_end_state::first: +state = set_end_state::next; +break; +case set_end_state::next: +if (*p == c.set_close) +{ +return cx::next(p); +} +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +p, "The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid set"); +#else +return throw_invalid_argument(p, "The given pattern is not a valid set"); +#endif +#else +return !c.set_enabled +? throw std::invalid_argument("The use of sets is disabled") +: p == pend +? throw std::invalid_argument("The given pattern is not a valid set") +: +state == set_end_state::open +? *p == c.set_open +? set_end(cx::next(p), pend, c, set_end_state::not_or_first) +: throw std::invalid_argument("The given pattern is not a valid set") +: +state == set_end_state::not_or_first +? *p == c.set_not ? set_end(cx::next(p), pend, c, set_end_state::first) +: set_end(cx::next(p), pend, c, set_end_state::next) +: state == set_end_state::first +? set_end(cx::next(p), pend, c, set_end_state::next) +: state == set_end_state::next +? *p == c.set_close +? cx::next(p) +: set_end(cx::next(p), pend, c, set_end_state::next) +: throw std::logic_error( +"The program execution should never end up " +"here throwing this exception"); +#endif +} +enum class match_set_state +{ +open, +not_or_first_in, +first_out, +next_in, +next_out +}; +template > +constexpr match_result match_set( +SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +const EqualTo& equal_to = EqualTo(), match_set_state state = match_set_state::open) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.set_enabled) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The use of sets is disabled"); +#else +return throw_invalid_argument(make_match_result(false, s, p), "The use of sets is disabled"); +#endif +} +while (p != pend) +{ +switch (state) +{ +case match_set_state::open: +if (*p != c.set_open) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid set"); +#else +return throw_invalid_argument(make_match_result(false, s, p), +"The given pattern is not a valid set"); +#endif +} +state = match_set_state::not_or_first_in; +break; +case match_set_state::not_or_first_in: +if (*p == c.set_not) +{ +state = match_set_state::first_out; +} +else +{ +if (s == send) +{ +return make_match_result(false, s, p); +} +if (equal_to(*s, *p)) +{ +return make_match_result(true, s, p); +} +state = match_set_state::next_in; +} +break; +case match_set_state::first_out: +if (s == send || equal_to(*s, *p)) +{ +return make_match_result(false, s, p); +} +state = match_set_state::next_out; +break; +case match_set_state::next_in: +if (*p == c.set_close || s == send) +{ +return make_match_result(false, s, p); +} +if (equal_to(*s, *p)) +{ +return make_match_result(true, s, p); +} +break; +case match_set_state::next_out: +if (*p == c.set_close) +{ +return make_match_result(true, s, p); +} +if (s == send || equal_to(*s, *p)) +{ +return make_match_result(false, s, p); +} +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +make_match_result(false, s, p), +"The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid set"); +#else +return throw_invalid_argument(make_match_result(false, s, p), +"The given pattern is not a valid set"); +#endif +#else +return !c.set_enabled +? throw std::invalid_argument("The use of sets is disabled") +: p == pend +? throw std::invalid_argument("The given pattern is not a valid set") +: state == match_set_state::open +? *p == c.set_open +? match_set(s, send, cx::next(p), pend, c, equal_to, +match_set_state::not_or_first_in) +: +throw std::invalid_argument("The given pattern is not a valid set") +: +state == match_set_state::not_or_first_in +? *p == c.set_not +? match_set(s, send, cx::next(p), pend, c, equal_to, +match_set_state::first_out) +: +s == send ? make_match_result(false, s, p) +: equal_to(*s, *p) +? make_match_result(true, s, p) +: match_set(s, send, cx::next(p), pend, c, +equal_to, match_set_state::next_in) +: +state == match_set_state::first_out +? s == send || equal_to(*s, *p) +? make_match_result(false, s, p) +: match_set(s, send, cx::next(p), pend, c, equal_to, +match_set_state::next_out) +: +state == match_set_state::next_in +? *p == c.set_close || s == send +? make_match_result(false, s, p) +: equal_to(*s, *p) ? make_match_result(true, s, p) +: match_set(s, send, cx::next(p), +pend, c, equal_to, state) +: +state == match_set_state::next_out +? *p == c.set_close +? make_match_result(true, s, p) +: s == send || equal_to(*s, *p) +? make_match_result(false, s, p) +: match_set(s, send, cx::next(p), pend, c, +equal_to, state) +: throw std::logic_error( +"The program execution should never end up " +"here " +"throwing this exception"); +#endif +} +enum class is_alt_state +{ +open, +next, +escape +}; +template +constexpr bool is_alt( +PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +is_alt_state state = is_alt_state::open, int depth = 0) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.alt_enabled) +{ +return false; +} +while (p != pend) +{ +switch (state) +{ +case is_alt_state::open: +if (*p != c.alt_open) +{ +return false; +} +state = is_alt_state::next; +++depth; +break; +case is_alt_state::next: +if (*p == c.escape) +{ +state = is_alt_state::escape; +} +else if (c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, is_set_state::not_or_first)) +{ +p = cx::prev(set_end(cx::next(p), pend, c, set_end_state::not_or_first)); +} +else if (*p == c.alt_open) +{ +++depth; +} +else if (*p == c.alt_close) +{ +--depth; +if (depth == 0) +{ +return true; +} +} +break; +case is_alt_state::escape: +state = is_alt_state::next; +break; +default: + +throw std::logic_error( +"The program execution should never end up here throwing this exception"); + +} +p = cx::next(p); +} +return false; +#else +return c.alt_enabled && p != pend && +(state == is_alt_state::open +? *p == c.alt_open && is_alt(cx::next(p), pend, c, is_alt_state::next, depth + 1) +: state == is_alt_state::next +? *p == c.escape +? is_alt(cx::next(p), pend, c, is_alt_state::escape, depth) +: c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, is_set_state::not_or_first) +? is_alt(set_end(cx::next(p), pend, c, set_end_state::not_or_first), +pend, c, state, depth) +: *p == c.alt_open +? is_alt(cx::next(p), pend, c, state, depth + 1) +: *p == c.alt_close +? depth == 1 || +is_alt(cx::next(p), pend, c, state, depth - 1) +: is_alt(cx::next(p), pend, c, state, depth) +: +state == is_alt_state::escape +? is_alt(cx::next(p), pend, c, is_alt_state::next, depth) +: throw std::logic_error( +"The program execution should never end up here throwing this " +"exception")); +#endif +} +enum class alt_end_state +{ +open, +next, +escape +}; +template +constexpr PatternIterator alt_end( +PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +alt_end_state state = alt_end_state::open, int depth = 0) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.alt_enabled) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The use of alternatives is disabled"); +#else +return throw_invalid_argument(p, "The use of alternatives is disabled"); +#endif +} +while (p != pend) +{ +switch (state) +{ +case alt_end_state::open: +if (*p != c.alt_open) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid alternative"); +#else +return throw_invalid_argument(p, "The given pattern is not a valid alternative"); +#endif +} +state = alt_end_state::next; +++depth; +break; +case alt_end_state::next: +if (*p == c.escape) +{ +state = alt_end_state::escape; +} +else if (c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, is_set_state::not_or_first)) +{ +p = cx::prev(set_end(cx::next(p), pend, c, set_end_state::not_or_first)); +} +else if (*p == c.alt_open) +{ +++depth; +} +else if (*p == c.alt_close) +{ +--depth; +if (depth == 0) +{ +return cx::next(p); +} +} +break; +case alt_end_state::escape: +state = alt_end_state::next; +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +p, "The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid alternative"); +#else +return throw_invalid_argument(p, "The given pattern is not a valid alternative"); +#endif +#else +return !c.alt_enabled +? throw std::invalid_argument("The use of alternatives is disabled") +: p == pend +? throw std::invalid_argument("The given pattern is not a valid alternative") +: state == alt_end_state::open +? *p == c.alt_open +? alt_end(cx::next(p), pend, c, alt_end_state::next, depth + 1) +: throw std::invalid_argument( +"The given pattern is not a valid alternative") +: state == alt_end_state::next +? *p == c.escape +? alt_end(cx::next(p), pend, c, alt_end_state::escape, depth) +: c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, +is_set_state::not_or_first) +? alt_end(set_end(cx::next(p), pend, c, +set_end_state::not_or_first), +pend, c, state, depth) +: *p == c.alt_open +? alt_end(cx::next(p), pend, c, state, depth + 1) +: *p == c.alt_close +? depth == 1 ? cx::next(p) +: alt_end(cx::next(p), pend, c, +state, depth - 1) +: alt_end(cx::next(p), pend, c, state, depth) +: +state == alt_end_state::escape +? alt_end(cx::next(p), pend, c, alt_end_state::next, depth) +: throw std::logic_error( +"The program execution should never end up here throwing " +"this " +"exception"); +#endif +} +enum class alt_sub_end_state +{ +next, +escape +}; +template +constexpr PatternIterator alt_sub_end( +PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +alt_sub_end_state state = alt_sub_end_state::next, int depth = 1) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.alt_enabled) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The use of alternatives is disabled"); +#else +return throw_invalid_argument(p, "The use of alternatives is disabled"); +#endif +} +while (p != pend) +{ +switch (state) +{ +case alt_sub_end_state::next: +if (*p == c.escape) +{ +state = alt_sub_end_state::escape; +} +else if (c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, is_set_state::not_or_first)) +{ +p = cx::prev(set_end(cx::next(p), pend, c, set_end_state::not_or_first)); +} +else if (*p == c.alt_open) +{ +++depth; +} +else if (*p == c.alt_close) +{ +--depth; +if (depth == 0) +{ +return p; +} +} +else if (*p == c.alt_or) +{ +if (depth == 1) +{ +return p; +} +} +break; +case alt_sub_end_state::escape: +state = alt_sub_end_state::next; +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +p, "The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid alternative"); +#else +return throw_invalid_argument(p, "The given pattern is not a valid alternative"); +#endif +#else +return !c.alt_enabled +? throw std::invalid_argument("The use of alternatives is disabled") +: p == pend +? throw std::invalid_argument("The given pattern is not a valid alternative") +: state == alt_sub_end_state::next +? *p == c.escape +? alt_sub_end(cx::next(p), pend, c, alt_sub_end_state::escape, depth) +: c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, is_set_state::not_or_first) +? alt_sub_end(set_end(cx::next(p), pend, c, +set_end_state::not_or_first), +pend, c, state, depth) +: *p == c.alt_open +? alt_sub_end(cx::next(p), pend, c, state, depth + 1) +: *p == c.alt_close +? depth == 1 ? p : alt_sub_end(cx::next(p), pend, +c, state, depth - 1) +: *p == c.alt_or +? depth == 1 ? p +: alt_sub_end(cx::next(p), pend, +c, state, depth) +: alt_sub_end(cx::next(p), pend, c, state, +depth) +: +state == alt_sub_end_state::escape +? alt_sub_end(cx::next(p), pend, c, alt_sub_end_state::next, depth) +: throw std::logic_error( +"The program execution should never end up here throwing " +"this " +"exception"); +#endif +} +template > +constexpr match_result match( +SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +const EqualTo& equal_to = EqualTo(), bool partial = false, bool escape = false); +template > +constexpr match_result match_alt( +SequenceIterator s, SequenceIterator send, PatternIterator p1, PatternIterator p1end, +PatternIterator p2, PatternIterator p2end, +const cards>& c = cards>(), +const EqualTo& equal_to = EqualTo(), bool partial = false) +{ +#if cfg_HAS_CONSTEXPR14 +auto result1 = match(s, send, p1, p1end, c, equal_to, true); +if (result1) +{ +auto result2 = match(result1.s, send, p2, p2end, c, equal_to, partial); +if (result2) +{ +return result2; +} +} +p1 = cx::next(p1end); +if (p1 == p2) +{ +return make_match_result(false, s, p1end); +} +return match_alt(s, send, p1, alt_sub_end(p1, p2, c), p2, p2end, c, equal_to, partial); +#else +return match(s, send, p1, p1end, c, equal_to, true) && +match(match(s, send, p1, p1end, c, equal_to, true).s, send, p2, p2end, c, equal_to, +partial) +? match(match(s, send, p1, p1end, c, equal_to, true).s, send, p2, p2end, c, equal_to, +partial) +: cx::next(p1end) == p2 +? make_match_result(false, s, p1end) +: match_alt(s, send, cx::next(p1end), alt_sub_end(cx::next(p1end), p2, c), p2, +p2end, c, equal_to, partial); +#endif +} +template +constexpr match_result match( +SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, +const cards>& c, const EqualTo& equal_to, bool partial, +bool escape) +{ +#if cfg_HAS_CONSTEXPR14 +if (p == pend) +{ +return make_match_result(partial || s == send, s, p); +} +if (escape) +{ +if (s == send || !equal_to(*s, *p)) +{ +return make_match_result(false, s, p); +} +return match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial); +} +if (*p == c.anything) +{ +auto result = match(s, send, cx::next(p), pend, c, equal_to, partial); +if (result) +{ +return result; +} +if (s == send) +{ +return make_match_result(false, s, p); +} +return match(cx::next(s), send, p, pend, c, equal_to, partial); +} +if (*p == c.single) +{ +if (s == send) +{ +return make_match_result(false, s, p); +} +return match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial); +} +if (*p == c.escape) +{ +return match(s, send, cx::next(p), pend, c, equal_to, partial, true); +} +if (c.set_enabled && *p == c.set_open && is_set(cx::next(p), pend, c, is_set_state::not_or_first)) +{ +auto result = +match_set(s, send, cx::next(p), pend, c, equal_to, match_set_state::not_or_first_in); +if (!result) +{ +return result; +} +return match(cx::next(s), send, set_end(cx::next(p), pend, c, set_end_state::not_or_first), +pend, c, equal_to, partial); +} +if (c.alt_enabled && *p == c.alt_open && is_alt(cx::next(p), pend, c, is_alt_state::next, 1)) +{ +auto p_alt_end = alt_end(cx::next(p), pend, c, alt_end_state::next, 1); +return match_alt(s, send, cx::next(p), alt_sub_end(cx::next(p), p_alt_end, c), p_alt_end, pend, +c, equal_to, partial); +} +if (s == send || !equal_to(*s, *p)) +{ +return make_match_result(false, s, p); +} +return match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial); +#else +return p == pend +? make_match_result(partial || s == send, s, p) +: escape +? s == send || !equal_to(*s, *p) +? make_match_result(false, s, p) +: match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial) +: *p == c.anything +? match(s, send, cx::next(p), pend, c, equal_to, partial) +? match(s, send, cx::next(p), pend, c, equal_to, partial) +: s == send ? make_match_result(false, s, p) +: match(cx::next(s), send, p, pend, c, equal_to, partial) +: *p == c.single +? s == send ? make_match_result(false, s, p) +: match(cx::next(s), send, cx::next(p), pend, c, +equal_to, partial) +: *p == c.escape +? match(s, send, cx::next(p), pend, c, equal_to, partial, true) +: c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, +is_set_state::not_or_first) +? !match_set(s, send, cx::next(p), pend, c, equal_to, +match_set_state::not_or_first_in) +? match_set(s, send, cx::next(p), pend, c, +equal_to, +match_set_state::not_or_first_in) +: match(cx::next(s), send, +set_end(cx::next(p), pend, c, +set_end_state::not_or_first), +pend, c, equal_to, partial) +: c.alt_enabled && *p == c.alt_open && +is_alt(cx::next(p), pend, c, +is_alt_state::next, 1) +? match_alt( +s, send, cx::next(p), +alt_sub_end(cx::next(p), +alt_end(cx::next(p), pend, c, +alt_end_state::next, 1), +c), +alt_end(cx::next(p), pend, c, +alt_end_state::next, 1), +pend, c, equal_to, partial) +: s == send || !equal_to(*s, *p) +? make_match_result(false, s, p) +: match(cx::next(s), send, cx::next(p), pend, +c, equal_to, partial); +#endif +} +} +template > +constexpr full_match_result, const_iterator_t> match( +Sequence&& sequence, Pattern&& pattern, +const cards>& c = cards>(), +const EqualTo& equal_to = EqualTo()) +{ +return detail::make_full_match_result( +cx::cbegin(sequence), cx::cend(sequence), cx::cbegin(pattern), cx::cend(pattern), +detail::match(cx::cbegin(sequence), cx::cend(std::forward(sequence)), +cx::cbegin(pattern), cx::cend(std::forward(pattern)), c, equal_to)); +} +template , +typename = typename std::enable_if::value>::type> +constexpr full_match_result, const_iterator_t> match( +Sequence&& sequence, Pattern&& pattern, const EqualTo& equal_to) +{ +return match(std::forward(sequence), std::forward(pattern), +cards>(), equal_to); +} +} +#endif +#ifndef WILDCARDS_MATCHER_HPP +#define WILDCARDS_MATCHER_HPP +#include +#include +#include +#ifndef CX_STRING_VIEW_HPP +#define CX_STRING_VIEW_HPP +#include +#include +#ifndef CX_ALGORITHM_HPP +#define CX_ALGORITHM_HPP +namespace cx +{ +template +constexpr bool equal(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) +{ +#if cfg_HAS_CONSTEXPR14 +while (first1 != last1 && first2 != last2 && *first1 == *first2) +{ +++first1, ++first2; +} +return first1 == last1 && first2 == last2; +#else +return first1 != last1 && first2 != last2 && *first1 == *first2 +? equal(first1 + 1, last1, first2 + 1, last2) +: first1 == last1 && first2 == last2; +#endif +} +} +#endif +namespace cx +{ +template +class basic_string_view +{ +public: +using value_type = T; +constexpr basic_string_view() = default; +template +constexpr basic_string_view(const T (&str)[N]) : data_{&str[0]}, size_{N - 1} +{ +} +constexpr basic_string_view(const T* str, std::size_t s) : data_{str}, size_{s} +{ +} +constexpr const T* data() const +{ +return data_; +} +constexpr std::size_t size() const +{ +return size_; +} +constexpr bool empty() const +{ +return size() == 0; +} +constexpr const T* begin() const +{ +return data_; +} +constexpr const T* cbegin() const +{ +return begin(); +} +constexpr const T* end() const +{ +return data_ + size_; +} +constexpr const T* cend() const +{ +return end(); +} +private: +const T* data_{nullptr}; +std::size_t size_{0}; +}; +template +constexpr bool operator==(const basic_string_view& lhs, const basic_string_view& rhs) +{ +return equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} +template +constexpr bool operator!=(const basic_string_view& lhs, const basic_string_view& rhs) +{ +return !(lhs == rhs); +} +template +std::basic_ostream& operator<<(std::basic_ostream& o, const basic_string_view& s) +{ +o << s.data(); +return o; +} +template +constexpr basic_string_view make_string_view(const T (&str)[N]) +{ +return {str, N - 1}; +} +template +constexpr basic_string_view make_string_view(const T* str, std::size_t s) +{ +return {str, s}; +} +using string_view = basic_string_view; +using u16string_view = basic_string_view; +using u32string_view = basic_string_view; +using wstring_view = basic_string_view; +namespace literals +{ +constexpr string_view operator"" _sv(const char* str, std::size_t s) +{ +return {str, s}; +} +constexpr u16string_view operator"" _sv(const char16_t* str, std::size_t s) +{ +return {str, s}; +} +constexpr u32string_view operator"" _sv(const char32_t* str, std::size_t s) +{ +return {str, s}; +} +constexpr wstring_view operator"" _sv(const wchar_t* str, std::size_t s) +{ +return {str, s}; +} +} +} +#endif +namespace wildcards +{ +template > +class matcher +{ +public: +constexpr explicit matcher(Pattern&& pattern, const cards>& c = +cards>(), +const EqualTo& equal_to = EqualTo()) +: p_{cx::cbegin(pattern)}, +pend_{cx::cend(std::forward(pattern))}, +c_{c}, +equal_to_{equal_to} +{ +} +constexpr matcher(Pattern&& pattern, const EqualTo& equal_to) +: p_{cx::cbegin(pattern)}, +pend_{cx::cend(std::forward(pattern))}, +c_{cards>()}, +equal_to_{equal_to} +{ +} +template +constexpr full_match_result, const_iterator_t> matches( +Sequence&& sequence) const +{ +return detail::make_full_match_result( +cx::cbegin(sequence), cx::cend(sequence), p_, pend_, +detail::match(cx::cbegin(sequence), cx::cend(std::forward(sequence)), p_, pend_, +c_, equal_to_)); +} +private: +const_iterator_t p_; +const_iterator_t pend_; +cards> c_; +EqualTo equal_to_; +}; +template > +constexpr matcher make_matcher( +Pattern&& pattern, +const cards>& c = cards>(), +const EqualTo& equal_to = EqualTo()) +{ +return matcher{std::forward(pattern), c, equal_to}; +} +template , +typename = typename std::enable_if::value>::type> +constexpr matcher make_matcher(Pattern&& pattern, const EqualTo& equal_to) +{ +return make_matcher(std::forward(pattern), cards>(), equal_to); +} +namespace literals +{ +constexpr auto operator"" _wc(const char* str, std::size_t s) +-> decltype(make_matcher(cx::make_string_view(str, s + 1))) +{ +return make_matcher(cx::make_string_view(str, s + 1)); +} +constexpr auto operator"" _wc(const char16_t* str, std::size_t s) +-> decltype(make_matcher(cx::make_string_view(str, s + 1))) +{ +return make_matcher(cx::make_string_view(str, s + 1)); +} +constexpr auto operator"" _wc(const char32_t* str, std::size_t s) +-> decltype(make_matcher(cx::make_string_view(str, s + 1))) +{ +return make_matcher(cx::make_string_view(str, s + 1)); +} +constexpr auto operator"" _wc(const wchar_t* str, std::size_t s) +-> decltype(make_matcher(cx::make_string_view(str, s + 1))) +{ +return make_matcher(cx::make_string_view(str, s + 1)); +} +} +} +#endif +#endif diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5d69c290d..600abbaa3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,472 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.7.2 (2025-05-29) +------------------ +* Fix issue `#978 `_ : skipped was not working properly +* Added codespell as a pre-commit hook. (`#977 `_) +* fix: Make impossible to accidentally copy JsonExporter singleton (`#975 `_) +* Contributors: Davide Faconti, Leander Stephen D'Souza, tony-p + +4.7.1 (2025-05-13) +------------------ +* fix ROS CI +* Add action to publish Doxygen documentation as GH Page (`#972 `_) +* Update Doxyfile +* Make BT::Any::copyInto const (`#970 `_) +* more changes related to TestNode +* Contributors: David Sobek, Davide Faconti, Marcus Ebner von Eschenbach + +4.7.0 (2025-04-24) +------------------ +* change TestNodeConfig preferred constructor +* Fix dangling‐capture in TestNodeConfig +* Fix Precondition to only check condition once (`#904 `_) +* fix issue 945 +* extend JSON conversion to include vectors (`#965 `_) +* Fix CI, add BUILD_TESTS and remove catkin support +* Fix testing CMake issue to resolve Rolling regression (`#961 `_) +* Bug fix/set blackboard (`#955 `_) +* feat: add fuzzing harnesses (`#925 `_) +* fix warnings +* Add const to applyVisitor (`#935 `_) +* try fix (`#941 `_) +* add workflow for sonarcube (`#936 `_) +* Fix issue `#909 `_: static queue in Loop +* apply changes suggested in `#893 `_ +* apply fix mentioned in `#916 `_ +* apply fixes suggested in `#919 `_ +* fix issue `#918 `_ (introduced in `#885 `_) +* add fix suggested in `#920 `_ +* add unit test related to `#931 `_ +* Fix compilation error when targeting C++23 (`#926 `_) ^~~~~~~~~~~~~ +* Fixes issue # `#929 `_ and `#921 `_ +* apply check suggested in `#924 `_ +* Fix ROS 2 build when ZeroMQ or SQlite3 include are not in the default include path (`#911 `_) + * Fix ROS 2 build when ZeroMQ or SQlite3 include are not in the default include path + * Update ament_build.cmake +* Fix/use correct compiler pixi/conda (`#914 `_) + * fix: Use the cxx-compiler package which will set the correct compiler for the platform, and setup the required environment for it to work as expected + * misc: update pixi versions in pipeline +* Add "other ports" to NodeConfig (`#910 `_) +* [retry_node] Refresh max_attempts\_ in case it changed (`#905 `_) + Co-authored-by: Guillaume Doisy +* use relative path in .Doxyfile (`#882 `_) +* Additional XML verification for ReactiveSequence nodes (`#885 `_) + Co-authored-by: AndyZe +* fix script parse error while 'A==-1' (`#896 `_) + Co-authored-by: wangzheng +* Expose return value of wait_for (`#887 `_) +* fix(examples): update t11_groot_howto log filename (`#886 `_) +* put minitrace in the build_interface link library (`#874 `_) + fixes the cmake export set when building behavior tree on standard cmake: CMake Error: install(EXPORT "behaviortree_cppTargets" ...) includes target "behaviortree_cpp" which requires target "minitrace" that is not in any export set. +* Improved XML parsing error message to say where in the XML the offending port is found. (`#876 `_) + Example output: + a port with name [ball_pose] is found in the XML (, line 7) but not in the providedPorts() of its registered node type. +* Refactored the TreeNode::executeTick() function to use a scoped timer for performance monitoring. (`#861 `_) (`#863 `_) + Update src/tree_node.cpp + Co-authored-by: wangzheng + Co-authored-by: Davide Faconti +* fix issue `#852 `_: thread safety in Loggers +* Lexy updated +* tinyXML updated to version 10.0 +* cppzmq updated to version 4.10 +* fix the "all_skipped" logic +* fixed: support utf-8 path xml-file (`#845 `_) + * fixed: 1. added compile version check to support Chinese path xml-file parsing 2. cmake add msvc /utf-8 options + * change cmake /utf-8 option add mode +* Export plugins to share directory & register CrossDoor plugin (`#804 `_) +* Contributors: Aglargil, AndyZe, Antoine Hoarau, David Sobek, Davide Faconti, Guillaume Doisy, Isar Meijer, Jake Keller, Marq Rasmussen, Michele Tartari, Silvio Traversaro, Tony Najjar, b-adkins, ckrah, devis12, kinly, tony-p, vincent-hui + +4.6.2 (2024-06-26) +------------------ +* Initialize template variable `T out` (`#839 `_) +* Building with a recent compiler fails due incompatible expected library (`#833 `_) + * nonstd::expected updated to 0.8 +* fix issue `#829 `_: support again custom JSON converters +* fix issue `#834 `_: enable minitrace +* allow multiple instances of the loggers +* fix issue `#827 `_ : verify name +* add TickMonitorCallback +* Fix typo in FallbackNode constructor parameter name (`#830 `_) +* fix segfault and throw instead when manifest is nullptr +* Add in call to ament_export_targets. (`#826 `_) +* Contributors: Davide Faconti, S. Messerschmidt, Sharmin Ramli, avikus-seonghyeon.kwon + +4.6.1 (2024-05-20) +------------------ +* remove flatbuffers from public API and old file_logger +* fix issue `#824 `_: use global in Blackboard::set +* Add test for setting a global blackboard entry using a node's output port `#823 `_ +* examples renamed +* Contributors: Davide Faconti, Robin Müller + +4.6.0 (2024-04-28) +------------------ +* add tutorial 19 about the global blackboard +* renamed examples to match website +* Update TestNode and the corresponding tutorial +* bug fixes related to sequence_id and unit tests added +* Add string concatenation operator to scripting (`#802 `_) +* Add library alias for BT::behaviortree_cpp (`#808 `_) +* add Time Stamped blackboard (`#805 `_) +* add additional information and functionality to SQLiteLogger +* add syntax for entries in the root blackboard ("@" prefix) +* Fix/pixi build (`#791 `_) +* fix unit tests in Windows +* fix windows compilation +* Update cmake_windows.yml +* Deprecate Balckboard::clear(). Issue `#794 `_ +* Support string vector conversion for ports (`#790 `_) +* add more convertToString for integers +* warn about overwritten enums +* fix ambiguous to_json +* Extend unit test for blackboard backup to run the second tree (`#789 `_) +* json conversion changed and +* issue `#755 `_ : add backchaining test and change reactive nodes checks (`#770 `_) +* Update switch_node.h +* test moved and port remapping fixed +* Create pull_request_template.md + +* adding pre-commit +* handle enums conversions is assignment +* Contributors: Davide Faconti, Sean Geles, Sebastian Castro, Victor Massagué Respall, avikus-seonghyeon.kwon, tony-p + +4.5.2 (2024-03-07) +------------------ +* bugfix: string to enum/integer/boolean in scripts +* bug fix in scripting comparison +* added more pretty-prints to demangler +* fixes and checks in default values, based on PR `#773 `_ +* Initialize std::atomic_bool (`#772 `_) +* Fix issue `#767 `_ and `#768 `_ +* updated default port syntax: "{=}" +* new default port capability: blackbard entries +* fix issue `#757 `_ : skipped nodes should not call post-condition ALWAYS +* Merge pull request `#756 `_ from imere/imere-patch-1 +* fix(test): Typo in gtest_blackboard.cpp +* Contributors: Davide Faconti, Lu Z, Marq Rasmussen + +4.5.1 (2024-01-23) +------------------ +* Support enums and real numbers in Node Switch +* improve Any::castPtr and add example +* fix issue `#748 `_ : static error messages +* Merge pull request `#746 `_ from galou/snprintf + Use snprintf instead of sprintf +* Use snprintf instead of sprintf + - Augment the buffer size on doc error. + - Let sprintf in switch_node.h since the max. string length is known. +* Contributors: Davide Faconti, Gaël Écorchard + +4.5.0 (2024-01-10) +------------------ +* fix typo in unit test `#733 `_ +* allow Input/Output ports with type Any +* Merge pull request `#703 `_ from galou/export_xsd + Implement writeTreeXSD() to generate an XSD +* Any::isType() will return the original type. Cherry picking from `#708 `_ +* fix `#734 `_ +* remove unneeded includes +* add Any::castPtr +* add alias KeyValueVector +* Merge pull request `#730 `_ from adlarkin/add_metadata + Add optional metadata to TreeNodeManifest +* Contributors: Ashton Larkin, Davide Faconti, Gaël Écorchard + +4.4.3 (2023-12-19) +------------------ +* Merge pull request #709 from galou/unset_blackboard +* fix issue `#725 `_ : SetBlackboard can copy entries +* add more unit tests +* fix typos `#721 `_ +* fix: guard macro declaration to prevent redefinition warning +* fix: Rename scoped lock so it doesn't hide the outer lock triggering a compiler warning +* add private ports to exclude from autoremapping `#706 `_ +* fix issue `#713 `_: getNodesByPath should be const +* Contributors: Davide Faconti, Nestor Gonzalez, Tony Paulussen + +4.4.2 (2023-11-28) +------------------ +* fix issue `#702 `_ : output ports require {} +* Merge pull request `#691 `_ from galou/small_refactor_and_doc + Small code refactor, log- and doc changes +* Merge pull request `#701 `_ from tony-p/fix/file-loggers-protected + fix: ensure public get config overload is used +* ci: use pixi github action +* fix: ensure public get config overload is used +* Small code refactor, log- and doc changes +* Contributors: Davide Faconti, Gaël Écorchard, Tony Paulussen + +4.4.1 (2023-11-12) +------------------ +* erase server_port+1 +* add reset by default in base classes (fix `#694 `_) +* fix issue `#696 `_ (wrong autoremapping) +* Remove traces of SequenceStar +* fix `#685 `_ (timeout in ZMP publisher) +* clang: fix warning + fix warning: lambda capture 'this' is not used +* Use feature test macro to check availability of `std::from_chars` +* fix warning in older compilers +* Contributors: Christoph Hertzberg, Davide Faconti, Gaël Écorchard, Shen Xingjian, Sid + +4.4.0 (2023-10-16) +------------------ +* Update ex05_subtree_model.cpp +* added any::stringToNumber +* added SubTree model example +* unit test for issue 660 +* adding SubTree model +* minor changes +* change blackboard entry +* Update simple_string.hpp +* SimpleString: fix warning by checking upper size limit (`#666 `_) +* Contributors: Adam Boseley, Davide Faconti + +4.3.8 (2023-10-09) +------------------ +* ReactiveSequence and ReactiveFallback will behave more similarly to 3.8 +* bug fix in wakeUpSignal +* ignore newlines in script +* stop ordering ports in TreeNodesModel +* add a specific tutorial for plugins +* Contributors: Davide Faconti + +4.3.7 (2023-09-12) +------------------ +* Test and fix issue `#653 `_: AnyTypeAllowed by default +* more time margin for Windows tests +* Add support for successful conda builds (`#650 `_) +* fix: Update how unit tests are executed in the github workflow so they are actually run on windows (`#647 `_) +* Add unit test related to SequenceWithMemory `#636 `_ +* Contributors: Davide Faconti, tony-p + +4.3.6 (2023-08-31) +------------------ +* Simplify the visualization of custom type in Groot2 and improved tutorial 12 +* fix compilation warnings +* Apply changes in ReactiveSequence to ReactiveFallback too +* test that logging works correctly with ReactiveSequence `#643 `_ +* reduce the number of times preconditions scripts are executed +* PauseWithRetry test added +* Contributors: Davide Faconti + +4.3.5 (2023-08-14) +------------------ +* fix issue `#621 `_: ConsumeQueue +* feat: add template specialization for convertFromString deque (`#628 `_) +* unit test added +* Update groot2_publisher.h (`#630 `_) +* unit test issue `#629 `_ +* WhileDoElseNode can have 2 or 3 children (`#625 `_) +* fix issue `#624 `_ : add TimeoutNode::halt() +* fix recording_fist_time issue on windows (`#618 `_) +* Contributors: Aglargil, Davide Faconti, Michael Terzer, benyamin saedi, muritane + +4.3.4 (2023-07-25) +------------------ +* Fix error #617 in TestNode +* minitrace updated +* fix issue #615 : don't execute preconditions if state is RUNNING +* README.md +* fix issue `#605 `_: strip whitespaces and better error message +* Export cxx-standard with target. (`#604 `_) +* feature `#603 `_: add static method [std::string description()] to manifest +* fix issue with move semantic +* Contributors: Davide Faconti, Sebastian Kasperski + +4.3.3 (2023-07-05) +------------------ +* bug fix `#601 `_: onHalted not called correctly in Control Nodes +* Groot recording (`#598 `_) + * add recording to groot publisher + * fixed + * protocols compatibility + * reply with first timestamp + * remove prints +* Fix error when building static library (`#599 `_) +* fix warnings +* 4.3.2 +* prepare release +* fix `#595 `_ : improvement in blackboard/scripting types (`#597 `_) +* Merge branch 'master' of github.com:BehaviorTree/BehaviorTree.CPP +* Merge branch 'parallel_all' +* Fix Issue 593 (`#594 `_): support skipping in Parallel node +* fix ParallelAll +* adding ParallelAll, WIP +* Contributors: Davide Faconti, Oleksandr Perepadia + +4.3.2 (2023-06-27) +------------------ +* fix `#595 `_ : improvement in blackboard/scripting types (`#597 `_) +* Fix Issue 593 (`#594 `_): support skipping in Parallel node +* adding ParallelAll +* Contributors: Davide Faconti + +4.3.1 (2023-06-21) +------------------ +* fix issue `#592 `_ +* use lambda in tutorial +* add script condition +* "fix" issue `#587 `_: ReactiveSequence should set conditions to IDLE +* better error message +* Fix issue `#585 `_ +* Contributors: Davide Faconti + +4.3.0 (2023-06-13) +------------------ +* use PImpl in multiple classes +* updated FileLogger2 +* better error messages +* blackboard refactoring to fix buggy _autoremap +* improved support for default values +* fix error and add nodiscard +* Fix `#580 `_ : more informative error when not specializing BT::toStr +* add builtin models to WriteTreeToXML +* add simple example to generate logs +* add Sleep Node +* Fix `#271 `_: better error message +* remove EOL ros2 from CI +* Contributors: Davide Faconti + +4.2.1 (2023-06-07) +------------------ +* Fix `#570 `_: string_view set in blackboard +* Fix missing attribute in generated XML (writeTreeNodesModelXML) +* Allow registration of TestNode +* Contributors: Davide Faconti, Oleksandr Perepadia + +4.2.0 (2023-05-23) +------------------ +* add more informative IDLE status +* more informative error message when trying to register virtual classes +* fixes and simpler getAnyLocked +* add Tree::getNodesByPath +* add FileLogger2 +* change getPortAny name and fic loop_node +* Lexy updated to release 2022.12.1 +* do not skip pre-post condition in substituted tick +* added Loop node +* deprecating getAny +* revert new behavior of Sequence and Fallback +* add resetChild to all the decorators that missed it +* Add test related to issue `#539 `_ +* related to `#555 `_ +* Critical bug fix in XML exporting +* Fix writeTreeNodesModelXML +* fix ament not registering executables as tests +* fix std::system_error in TimeoutNode +* minor changes, mostly comments +* add version string +* old ZMQ publisher removed +* Add RunOnce, based on `#472 `_ +* Contributors: Alberto Soragna, Davide Faconti, Gaël Écorchard, Mithun Kinarullathil, Sergei Molchanov + +4.1.1 (2023-03-29) +------------------ +* adding sqlite logger +* fix warning +* better cmake +* ManualSelector removed +* magic_enum updated +* fix issue `#530 `_: use convertFromString in scripting assignments +* added unit test +* files moved +* fix groot2 publisher +* minor fixes in blackboard +* fix XML: Subtree should remember the remapped ports +* add the ability to load substitution rules from JSON +* Update README.md +* Contributors: Davide Faconti + +4.1.0 (2023-03-18) +------------------ +* temporary disable codeql +* Groot2 interface (`#528 `_) + * refactored groot2 interface + * protocol updated +* merging groot2 publisher +* add observer +* prepare 4.1 +* Update README.md +* fix issue `#525 `_ when ReactiveSequence contains skipped children +* fix reactive sequence (issue `#526 `_ and `#525 `_) +* better test +* add cast to ENUMS in ports +* changes ported from 4.1 +* fix samples +* better include paths +* Control node and Decorators RUNNING before first child +* blackboard: update getKeys and add mutex to scripting +* add [[nodiscard]] and some other minor changes +* add screenshot +* change the behavior of tickOnce to actually loop is wake up signal is… (`#522 `_) + * change the behavior of tickOnce to actually loop is wake up signal is received + * fix warning +* Cmake conan (`#521 `_) + * boost coroutine substituted with minicoro. 3rd party updates + * cmake refactoring + conan + * fix cmake + * fix build with conan and change CI +* fix CI in ROS1 (`#519 `_) +* fix alloc-dealloc-mismatch for _storage.str.data (`#518 `_) +* Fix issue `#515 `_: reactive sequence not skipped correctly +* Fix issue `#517 `_ +* Merge branch 'master' of github.com:BehaviorTree/BehaviorTree.CPP +* fix issue `#492 `_ (Threads::Threads) +* Fix boost dependency in package.xml (`#512 `_) + `libboost-coroutine-dev` has been merged into rosdistro on February 21st + 2023. Link to merge request: https://github.com/ros/rosdistro/pull/35789/. +* fix compilation +* revert breaking change +* Merge branch 'master' of github.com:BehaviorTree/BehaviorTree.CPP +* make default value of port optional, to allow empty strings +* Contributors: Alberto Soragna, Bart Keulen, Davide Faconti + +4.0.2 (2023-02-17) +------------------ +* fix issue `#501 `_ +* fix issue `#505 `_ +* solve issue `#506 `_ +* prevent useless exception catcking +* fix issue `#507 `_ +* adding the uid to the log to uniquely identify the nodes (`#502 `_) +* fix in SharedLibrary and cosmetic changes to the code +* using tinyxml ErrorStr() instead of ErrorName() to get more info about missing file (`#497 `_) +* Fixed use of ros_pkg for ROS1 applications (`#483 `_) +* Fix error message StdCoutLogger -> MinitraceLogger (`#495 `_) +* Fix boost dependency in package.xml (`#493 `_) + Co-authored-by: Bart Keulen +* support Enums in string conversion +* fix issue 489 +* updated example. Demonstrate pass by reference +* lexy updated +* rename haltChildren to resetChildren +* revert `#329 `_ +* Merge branch 'master' of github.com:BehaviorTree/BehaviorTree.CPP +* Small improvements (`#479 `_) + * Make message for allowed port names more explicit + Also throw an exception for unknown port direction rather than using + `PortDirection::INOUT`. + * Small code improvements + * Remove code without effect +* Fix some renaming for V4 (`#480 `_) +* Define NodeConfiguration for BT3 compatibility (`#477 `_) +* Implement `#404 `_ to solve `#435 `_ (gtest not found) +* fix issue `#474 `_ Make libraries dependencies private +* fix issue `#413 `_ (Delay logic) +* change suggested in `#444 `_ +* add XML converter +* Add CodeQL workflow (`#471 `_) +* Update README.md +* Contributors: Ana, Bart Keulen, Christian Henkel, Davide Faconti, Gaël Écorchard, Jorge, Mahmoud Farshbafdoustar, Norawit Nangsue + +4.0.1 (2022-11-19) +------------------ +* version 4.X +* Contributors: Adam Aposhian, Adam Sasine, Alberto Soragna, Ali Aydın KÜÇÜKÇÖLLÜ, AndyZe, Davide Faconti, Dennis, Gaël Écorchard, Jafar, Joseph Schornak, Luca Bonamini, Paul Bovbel, SubaruArai, Tim Clephas, Will + 3.7.0 (2022-05-23) ----------- * add netlify stuff @@ -33,7 +499,7 @@ Changelog for package behaviortree_cpp dependency explicitly. * Change order of lock to prevent deadlock. (`#368 `_) Resolves `#367 `_. -* Fix `#320 `_ : forbit refrences in Any +* Fix `#320 `_ : forbid references in Any * Update action_node.h * Contributors: Adam Sasine, Davide Faconti, Fabian Schurig, Griswald Brooks, Hyeongsik Min, Robodrome, imgbot[bot], panwauu @@ -380,9 +846,9 @@ Changelog for package behaviortree_cpp * Conan package distribution (#39) * Non-functional refactoring of xml_parsing to clean up the code * cosmetic changes in the code of BehaviorTreeFactory -* XML schema. Related to enchancement #40 +* XML schema. Related to enhancement #40 * call setRegistrationName() for built-in Nodes - The methos is called by BehaviorTreefactory, therefore it + The method is called by BehaviorTreefactory, therefore it registrationName is empty if trees are created programmatically. * Reset reference count when destroying logger (issue #38) * Contributors: Davide Facont, Davide Faconti, Uilian Ries @@ -398,7 +864,7 @@ Changelog for package behaviortree_cpp ------------------ * adding virtual TreeNode::onInit() [issue #33] * fix issue #34 : if you don't implement convertFromString, it will compile but it may throw -* Pretty demangled names and obsolate comments removed +* Pretty demangled names and obsolete comments removed * bug fixes * more comments * [enhancement #32]: add CoroActionNode and rename ActionNode as "AsynActionNode" @@ -465,7 +931,7 @@ Changelog for package behaviortree_cpp * Fix: registerBuilder did not register the manifest. It was "broken" as public API method * Use the Pimpl idiom to hide zmq from the header file * move header of minitrace in the cpp file -* Fixed a crash occuring when you didn't initialized a Tree object (#20) +* Fixed a crash occurring when you didn't initialized a Tree object (#20) * Fix issue #16 * add ParallelNode to pre-registered entries in factory (issue #13) * removed M_PI diff --git a/CMakeLists.txt b/CMakeLists.txt index 31cbd30d4..e69c9e96c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,59 +1,60 @@ -cmake_minimum_required(VERSION 3.10.2) # version on Ubuntu Bionic -project(behaviortree_cpp) +cmake_minimum_required(VERSION 3.16.3) # version on Ubuntu Focal -#---- Add the subdirectory cmake ---- -set(CMAKE_CONFIG_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake") -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CONFIG_PATH}") +project(behaviortree_cpp VERSION 4.7.2 LANGUAGES C CXX) -#---- Enable C++17 ---- -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) +# create compile_commands.json +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -if(MSVC) - add_definitions(-D_CRT_SECURE_NO_WARNINGS -DWIN32_LEAN_AND_MEAN) -else() - add_definitions(-Wpedantic) +#---- project configuration ---- +option(BTCPP_SHARED_LIBS "Build shared libraries" ON) +option(BTCPP_BUILD_TOOLS "Build commandline tools" ON) +option(BTCPP_EXAMPLES "Build tutorials and examples" ON) +option(BUILD_TESTING "Build the unit tests" ON) +option(BTCPP_GROOT_INTERFACE "Add Groot2 connection. Requires ZeroMQ" ON) +option(BTCPP_SQLITE_LOGGING "Add SQLite logging." ON) + +option(USE_V3_COMPATIBLE_NAMES "Use some alias to compile more easily old 3.x code" OFF) +option(ENABLE_FUZZING "Enable fuzzing builds" OFF) +option(USE_AFLPLUSPLUS "Use AFL++ instead of libFuzzer" OFF) +option(ENABLE_DEBUG "Enable debug build with full symbols" OFF) +option(FORCE_STATIC_LINKING "Force static linking of all dependencies" OFF) + +set(BASE_FLAGS "") + +if(ENABLE_DEBUG) + list(APPEND BASE_FLAGS + -g3 + -ggdb3 + -O0 + -fno-omit-frame-pointer + ) endif() -set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# Include fuzzing configuration if enabled +if(ENABLE_FUZZING) + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/fuzzing_build.cmake) +else() + # Apply base flags for non-fuzzing builds + add_compile_options(${BASE_FLAGS}) + add_link_options(${BASE_FLAGS}) +endif() -#---- project configuration ---- -option(BUILD_EXAMPLES "Build tutorials and examples" ON) -option(BUILD_SAMPLES "Build sample nodes" ON) -option(BUILD_UNIT_TESTS "Build the unit tests" ON) -option(BUILD_TOOLS "Build commandline tools" ON) -option(BUILD_SHARED_LIBS "Build shared libraries" ON) -option(BUILD_MANUAL_SELECTOR "Build manual selector node" ON) -option(ENABLE_COROUTINES "Enable boost coroutines" ON) -option(USE_V3_COMPATIBLE_NAMES "Use some alias to compile more easily old 3.x code" OFF) +set(CMAKE_CONFIG_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CONFIG_PATH}") -#---- Include boost to add coroutines ---- -if(ENABLE_COROUTINES) - find_package(Boost COMPONENTS coroutine QUIET) - - if(Boost_FOUND) - string(REPLACE "." "0" Boost_VERSION_NODOT ${Boost_VERSION}) - if(NOT Boost_VERSION_NODOT VERSION_LESS 105900) - message(STATUS "Found boost::coroutine2.") - add_definitions(-DBT_BOOST_COROUTINE2) - set(BT_COROUTINES true) - elseif(NOT Boost_VERSION_NODOT VERSION_LESS 105300) - message(STATUS "Found boost::coroutine.") - add_definitions(-DBT_BOOST_COROUTINE) - set(BT_COROUTINES true) - endif() - include_directories(${Boost_INCLUDE_DIRS}) - endif() +set(BTCPP_LIBRARY ${PROJECT_NAME}) - if(NOT DEFINED BT_COROUTINES) - message(STATUS "Boost coroutines disabled. Install Boost (version 1.59+ recommended).") - endif() -else() - message(STATUS "Boost coroutines disabled by CMake option.") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'Release' as none was specified.") + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() -if(NOT DEFINED BT_COROUTINES) - add_definitions(-DBT_NO_COROUTINES) +if(MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS -DWIN32_LEAN_AND_MEAN) +else() + add_definitions(-Wpedantic -fno-omit-frame-pointer) endif() if(USE_V3_COMPATIBLE_NAMES) @@ -61,21 +62,8 @@ if(USE_V3_COMPATIBLE_NAMES) endif() #---- Find other packages ---- -find_package(Threads) -find_package(ZMQ) +find_package(Threads REQUIRED) -list(APPEND BEHAVIOR_TREE_PUBLIC_LIBRARIES - ${CMAKE_THREAD_LIBS_INIT} - ${CMAKE_DL_LIBS} -) - -if( ZMQ_FOUND ) - message(STATUS "ZeroMQ found.") - add_definitions( -DZMQ_FOUND ) - list(APPEND BT_SOURCE src/loggers/bt_zmq_publisher.cpp) -else() - message(WARNING "ZeroMQ NOT found. Skipping the build of [PublisherZMQ] and [bt_recorder].") -endif() set(BEHAVIOR_TREE_LIBRARY ${PROJECT_NAME}) @@ -91,52 +79,25 @@ if ( ament_cmake_FOUND ) add_definitions( -DUSING_ROS2 ) message(STATUS "------------------------------------------") - message(STATUS "BehaviourTree is being built using AMENT.") + message(STATUS "BehaviorTree is being built using AMENT.") message(STATUS "------------------------------------------") - - set(BUILD_TOOL_INCLUDE_DIRS ${ament_INCLUDE_DIRS}) - -elseif( CATKIN_DEVEL_PREFIX OR CATKIN_BUILD_BINARY_PACKAGE) - - set(catkin_FOUND 1) - add_definitions( -DUSING_ROS ) - find_package(catkin REQUIRED COMPONENTS roslib) - find_package(GTest) - + include(cmake/ament_build.cmake) +else() message(STATUS "------------------------------------------") - message(STATUS "BehaviourTree is being built using CATKIN.") + message(STATUS "BehaviorTree is being built with conan.") message(STATUS "------------------------------------------") - - catkin_package( - INCLUDE_DIRS include # do not include "3rdparty" here - LIBRARIES ${BEHAVIOR_TREE_LIBRARY} - CATKIN_DEPENDS roslib - ) - - list(APPEND BEHAVIOR_TREE_PUBLIC_LIBRARIES ${catkin_LIBRARIES}) - set(BUILD_TOOL_INCLUDE_DIRS ${catkin_INCLUDE_DIRS}) - -elseif(BUILD_UNIT_TESTS) - if(${CMAKE_VERSION} VERSION_LESS "3.11.0") - find_package(GTest REQUIRED) - else() - include(FetchContent) - FetchContent_Declare( - googletest - URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip - ) - # For Windows: Prevent overriding the parent project's compiler/linker settings - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(googletest) - endif() + include(cmake/conan_build.cmake) endif() - ############################################################# # LIBRARY add_subdirectory(3rdparty/lexy) +add_library(minitrace STATIC 3rdparty/minitrace/minitrace.cpp) +target_compile_definitions(minitrace PRIVATE MTR_ENABLED=True) +set_property(TARGET minitrace PROPERTY POSITION_INDEPENDENT_CODE ON) + list(APPEND BT_SOURCE src/action_node.cpp src/basic_types.cpp @@ -149,45 +110,49 @@ list(APPEND BT_SOURCE src/shared_library.cpp src/tree_node.cpp src/script_parser.cpp + src/json_export.cpp src/xml_parsing.cpp + src/actions/test_node.cpp + src/actions/sleep_node.cpp + src/actions/updated_action.cpp + + src/decorators/delay_node.cpp src/decorators/inverter_node.cpp src/decorators/repeat_node.cpp src/decorators/retry_node.cpp src/decorators/subtree_node.cpp - src/decorators/delay_node.cpp + src/decorators/timeout_node.cpp + src/decorators/updated_decorator.cpp src/controls/if_then_else_node.cpp src/controls/fallback_node.cpp src/controls/parallel_node.cpp + src/controls/parallel_all_node.cpp src/controls/reactive_sequence.cpp src/controls/reactive_fallback.cpp src/controls/sequence_node.cpp - src/controls/sequence_star_node.cpp + src/controls/sequence_with_memory_node.cpp src/controls/switch_node.cpp src/controls/while_do_else_node.cpp src/loggers/bt_cout_logger.cpp - src/loggers/bt_file_logger.cpp + src/loggers/bt_file_logger_v2.cpp src/loggers/bt_minitrace_logger.cpp + src/loggers/bt_observer.cpp 3rdparty/tinyxml2/tinyxml2.cpp - 3rdparty/minitrace/minitrace.cpp ) -if(BUILD_MANUAL_SELECTOR) - find_package(Curses QUIET) - if(CURSES_FOUND) - list(APPEND BT_SOURCE - src/controls/manual_node.cpp - ) - list(APPEND BEHAVIOR_TREE_PUBLIC_LIBRARIES ${CURSES_LIBRARIES}) - add_definitions(-DNCURSES_FOUND) - else() - message(WARNING "NCurses NOT found. Skipping the build of manual selector node.") - endif() + +if(BTCPP_GROOT_INTERFACE) + # should be found already, at this stage + list(APPEND BT_SOURCE src/loggers/groot2_publisher.cpp ) endif() +if(BTCPP_SQLITE_LOGGING) + list(APPEND BT_SOURCE src/loggers/bt_sqlite_logger.cpp ) +endif() ###################################################### @@ -200,143 +165,95 @@ if (WIN32) list(APPEND BT_SOURCE src/shared_library_WIN.cpp ) endif() -if (BUILD_SHARED_LIBS) +if (BTCPP_SHARED_LIBS) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) - add_library(${BEHAVIOR_TREE_LIBRARY} SHARED ${BT_SOURCE}) + add_library(${BTCPP_LIBRARY} SHARED ${BT_SOURCE}) else() - add_library(${BEHAVIOR_TREE_LIBRARY} STATIC ${BT_SOURCE}) -endif() - -if( ZMQ_FOUND ) - list(APPEND BUILD_TOOL_INCLUDE_DIRS ${ZMQ_INCLUDE_DIRS}) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + add_library(${BTCPP_LIBRARY} STATIC ${BT_SOURCE}) endif() -target_link_libraries(${BEHAVIOR_TREE_LIBRARY} PUBLIC - ${BEHAVIOR_TREE_PUBLIC_LIBRARIES}) +message(STATUS "BTCPP_EXTRA_LIBRARIES: ${BTCPP_EXTRA_LIBRARIES}") -target_link_libraries(${BEHAVIOR_TREE_LIBRARY} PRIVATE - ${Boost_LIBRARIES} - ${ZMQ_LIBRARIES} - foonathan::lexy +target_link_libraries(${BTCPP_LIBRARY} + PRIVATE + Threads::Threads + ${CMAKE_DL_LIBS} + $ + $ + PUBLIC + ${BTCPP_EXTRA_LIBRARIES} ) -#get_target_property(my_libs ${BEHAVIOR_TREE_LIBRARY} INTERFACE_LINK_LIBRARIES) -#list(REMOVE_ITEM _libs X) -#message("my_libs: ${my_libs}") - -#set_target_properties(${BEHAVIOR_TREE_LIBRARY} PROPERTIES INTERFACE_LINK_LIBRARIES "") +target_include_directories(${BTCPP_LIBRARY} + PUBLIC + $ + $ + PRIVATE + $ + $ + ${BTCPP_EXTRA_INCLUDE_DIRS} + ) -target_compile_definitions(${BEHAVIOR_TREE_LIBRARY} PRIVATE $<$:TINYXML2_DEBUG>) +target_compile_definitions(${BTCPP_LIBRARY} PRIVATE $<$:TINYXML2_DEBUG>) +target_compile_definitions(${BTCPP_LIBRARY} PUBLIC BTCPP_LIBRARY_VERSION="${CMAKE_PROJECT_VERSION}") -target_include_directories(${BEHAVIOR_TREE_LIBRARY} PUBLIC - $ - $ - $ - $ - ${BUILD_TOOL_INCLUDE_DIRS}) - -if( ZMQ_FOUND ) - target_compile_definitions(${BEHAVIOR_TREE_LIBRARY} PUBLIC ZMQ_FOUND) -endif() +target_compile_features(${BTCPP_LIBRARY} PUBLIC cxx_std_17) if(MSVC) + target_compile_options(${BTCPP_LIBRARY} PRIVATE "/source-charset:utf-8") else() - target_compile_options(${BEHAVIOR_TREE_LIBRARY} PRIVATE - -Wall -Wextra -Werror=return-type) + if(ENABLE_DEBUG) + target_compile_options(${BTCPP_LIBRARY} PRIVATE -Wall -Wextra -g3 -ggdb3 -O0 -fno-omit-frame-pointer) + else() + target_compile_options(${BTCPP_LIBRARY} PRIVATE -Wall -Wextra) + endif() endif() -############################################################# -if(ament_cmake_FOUND) - find_package(ament_index_cpp REQUIRED) - ament_target_dependencies(${BEHAVIOR_TREE_LIBRARY} PUBLIC ament_index_cpp) - ament_export_dependencies(ament_index_cpp) - - set( BEHAVIOR_TREE_LIB_DESTINATION lib ) - set( BEHAVIOR_TREE_INC_DESTINATION include ) - set( BEHAVIOR_TREE_BIN_DESTINATION bin ) - - ament_export_include_directories(include) - ament_export_libraries(${BEHAVIOR_TREE_LIBRARY}) - ament_package() -elseif(catkin_FOUND) - set( BEHAVIOR_TREE_LIB_DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} ) - set( BEHAVIOR_TREE_INC_DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION} ) - set( BEHAVIOR_TREE_BIN_DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} ) -else() - set( BEHAVIOR_TREE_LIB_DESTINATION lib ) - set( BEHAVIOR_TREE_INC_DESTINATION include ) - set( BEHAVIOR_TREE_BIN_DESTINATION bin ) +add_library(BT::${BTCPP_LIBRARY} ALIAS ${BTCPP_LIBRARY}) +# Add fuzzing targets +if(ENABLE_FUZZING) + add_fuzzing_targets() endif() -message( STATUS "BEHAVIOR_TREE_LIB_DESTINATION: ${BEHAVIOR_TREE_LIB_DESTINATION} " ) -message( STATUS "BEHAVIOR_TREE_BIN_DESTINATION: ${BEHAVIOR_TREE_BIN_DESTINATION} " ) -message( STATUS "BUILD_UNIT_TESTS: ${BUILD_UNIT_TESTS} " ) - +############################################################# +message( STATUS "BTCPP_LIB_DESTINATION: ${BTCPP_LIB_DESTINATION} " ) +message( STATUS "BTCPP_INCLUDE_DESTINATION: ${BTCPP_INCLUDE_DESTINATION} " ) -###################################################### -# Samples -if (BUILD_SAMPLES) +if (BUILD_TESTING OR BTCPP_EXAMPLES) add_subdirectory(sample_nodes) endif() ###################################################### -# Test -if (BUILD_UNIT_TESTS AND BUILD_SAMPLES) + +include(CTest) +message( STATUS "BUILD_TESTING: ${BUILD_TESTING} " ) +if (BUILD_TESTING) add_subdirectory(tests) endif() +if(BTCPP_BUILD_TOOLS) + add_subdirectory(tools) +endif() + +if(BTCPP_EXAMPLES) + add_subdirectory(examples) +endif() + ###################################################### # INSTALL -INSTALL(TARGETS ${BEHAVIOR_TREE_LIBRARY} - EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION ${BEHAVIOR_TREE_LIB_DESTINATION} - LIBRARY DESTINATION ${BEHAVIOR_TREE_LIB_DESTINATION} - RUNTIME DESTINATION ${BEHAVIOR_TREE_BIN_DESTINATION} +INSTALL(TARGETS ${BTCPP_LIBRARY} + EXPORT ${BTCPP_LIBRARY}Targets + ARCHIVE DESTINATION ${BTCPP_LIB_DESTINATION} + LIBRARY DESTINATION ${BTCPP_LIB_DESTINATION} + RUNTIME DESTINATION ${BTCPP_BIN_DESTINATION} + INCLUDES DESTINATION ${BTCPP_INCLUDE_DESTINATION} ) INSTALL( DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ - DESTINATION ${BEHAVIOR_TREE_INC_DESTINATION} + DESTINATION ${BTCPP_INCLUDE_DESTINATION} FILES_MATCHING PATTERN "*.h*") -install(EXPORT ${PROJECT_NAME}Targets - FILE "${PROJECT_NAME}Targets.cmake" - DESTINATION "${BEHAVIOR_TREE_LIB_DESTINATION}/cmake/${PROJECT_NAME}" - NAMESPACE BT:: - ) - -export(PACKAGE ${PROJECT_NAME}) - -include(CMakePackageConfigHelpers) - -configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - INSTALL_DESTINATION "${BEHAVIOR_TREE_LIB_DESTINATION}/cmake/${PROJECT_NAME}" -) - -# This requires to declare to project version in the project() macro - -#write_basic_package_version_file( -# "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" -# VERSION ${PROJECT_VERSION} -# COMPATIBILITY AnyNewerVersion -#) - -install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - # "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" - DESTINATION "${BEHAVIOR_TREE_LIB_DESTINATION}/cmake/${PROJECT_NAME}" -) - -###################################################### -# EXAMPLES and TOOLS -if(BUILD_TOOLS) - add_subdirectory(tools) -endif() - -if(BUILD_EXAMPLES AND BUILD_SAMPLES) - add_subdirectory(examples) -endif() +export_btcpp_package() diff --git a/Doxyfile b/Doxyfile index 7c24f87b6..d06db140e 100644 --- a/Doxyfile +++ b/Doxyfile @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = /home/davide.faconti/ws_behavior_tree/src/Behavior-Tree/doc +OUTPUT_DIRECTORY = ./doc # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -781,7 +781,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = /home/davide.faconti/ws_behavior_tree/src/Behavior-Tree/include +INPUT = ./include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -863,8 +863,9 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = /home/davide.faconti/ws_behavior_tree/src/Behavior-Tree/3rdparty \ - /home/davide.faconti/ws_behavior_tree/src/Behavior-Tree/gtest +EXCLUDE = ./3rdparty \ + ./gtest \ + ./include/behaviortree_cpp/contrib # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/LICENSE b/LICENSE index f60806f21..e55a7557f 100644 --- a/LICENSE +++ b/LICENSE @@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/README.md b/README.md index af3d60f86..6fda200c7 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ ![License MIT](https://img.shields.io/github/license/BehaviorTree/BehaviorTree.CPP?color=blue) -![Version](https://img.shields.io/badge/version-4.0-blue.svg) -[![cmake](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/cmake.yml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/cmake.yml) -[![ros1](https://github.com/BehaviorTree/BehaviorTree.CPP/workflows/ros1/badge.svg?branch=master)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions?query=workflow%3Aros1) -[![ros2](https://github.com/BehaviorTree/BehaviorTree.CPP/workflows/ros2/badge.svg?branch=master)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions?query=workflow%3Aros2) -[![LGTM Grade](https://img.shields.io/lgtm/grade/cpp/github/BehaviorTree/BehaviorTree.CPP)](https://lgtm.com/projects/g/BehaviorTree/BehaviorTree.CPP/context:cpp) -![Discourse topics](https://img.shields.io/discourse/topics?server=https%3A%2F%2Fdiscourse.behaviortree.dev) +[![conan Ubuntu](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/cmake_ubuntu.yml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/cmake_ubuntu.yml) +[![conan Windows](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/cmake_windows.yml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/cmake_windows.yml) +[![ros2](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/ros2.yaml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/ros2.yaml) +[![pixi (Conda)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/pixi.yaml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/pixi.yaml) -# BehaviorTree.CPP 4.0 +# BehaviorTree.CPP 4.7

@@ -16,11 +14,11 @@ It was designed to be flexible, easy to use, reactive and fast. Even if our main use-case is __robotics__, you can use this library to build __AI for games__, or to replace Finite State Machines. -There are few features that make __BehaviorTree.CPP__ unique, when compared to other implementations: +There are few features which make __BehaviorTree.CPP__ unique, when compared to other implementations: - It makes __asynchronous Actions__, i.e. non-blocking, a first-class citizen. -- You can build __reactive__ behaviors that execute multiple Actions concurrently. +- You can build __reactive__ behaviors that execute multiple Actions concurrently (orthogonality). - Trees are defined using a Domain Specific __scripting language__ (based on XML), and can be loaded at run-time; in other words, even if written in C++, the morphology of the Trees is _not_ hard-coded. @@ -37,86 +35,84 @@ to visualize, record, replay and analyze state transitions. You can learn about the main concepts, the API and the tutorials here: https://www.behaviortree.dev/ -If the documentation doesn't answer your questions and/or you want to -connect with the other **BT.CPP** users, visit https://discourse.behaviortree.dev/ - -## Previous version - -Version 3.8 of the software can be found in the branch -[v3.8](https://github.com/BehaviorTree/BehaviorTree.CPP/tree/v3.8). +An automatically generated API documentation can be found here: https://BehaviorTree.github.io/BehaviorTree.CPP/ -That branch might receive bug fixes, but the new features will be implemented -only in the master branch. +If the documentation doesn't answer your questions and/or you want to +connect with the other **BT.CPP** users, visit [our forum](https://github.com/BehaviorTree/BehaviorTree.CPP/discussions) -## Commercial support +# GUI Editor -Are you using BT.CPP in your commercial product and you need technical support / consulting? -You can contact the main author dfaconti@aurynrobotics.com to discuss your use case and needs. +Editing a BehaviorTree is as simple as editing an XML file in your favorite text editor. -# Design principles +If you are looking for a more fancy graphical user interface (and I know you do) check +[Groot2](https://www.behaviortree.dev/groot) out. -The main goal of this project is to create a Behavior Tree implementation -that uses the principles of Model Driven Development to separate the role -of the __Component Developer__ from the __Behavior Designer__. +![Groot screenshot](docs/groot-screenshot.png) -In practice, this means that: +# How to compile -- Custom TreeNodes must be reusable building blocks. - You should be able to implement them once and reuse them to build many behaviors. +**BT.CPP** requires a compile that supports c++17. -- To build a Behavior Tree out of TreeNodes, the Behavior Designer must -not need to read nor to modify the C++ source code.. +Three build systems are supported: -- Complex Behaviours must be composable using Subtrees. +- **colcon (ament)**, if you use ROS2 +- **conan** otherwise (Linux/Windows). +- **straight cmake** if you want to be personally responsible for dependencies :) -# GUI Editor +Compiling with [conan](https://conan.io/): -Editing a BehaviorTree is as simple as editing a XML file in your favourite text editor. +Assuming that you are in the **parent** directory of `BehaviorTree.CPP`: -If you are looking for a more fancy graphical user interface (and I know you do) check -[Groot](https://github.com/BehaviorTree/Groot) out. +``` +mkdir build; cd build +conan install ../BehaviorTree.CPP --output-folder=. --build=missing +cmake ../BehaviorTree.CPP -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" +cmake --build . --parallel +``` -![Groot screenshot](docs/groot-screenshot.png) +If you have dependencies such as ZeroMQ and SQlite already installed and you don't want to +use conan, simply type: -# How to compile (Ubuntu) +``` +mkdir build; cd build +cmake ../BehaviorTree.CPP +cmake --build . --parallel +``` -Please note that **Ubuntu 18.04 is not supported anymore in version 4.X**. Ubuntu 20.04 or later is required. +If you want to build in a [pixi](https://pixi.sh/) project (conda virtual environment). +``` +pixi run build +``` -First, install the following dependencies (optional, but recommended): +If you want to use BT.CPP in your application, please refer to the +example here: https://github.com/BehaviorTree/btcpp_sample . - sudo apt-get install libzmq3-dev libboost-dev libncurses5-dev libncursesw5-dev +# Commercial support -To compile and install the library, from the BehaviorTree.CPP folder, execute: +Are you using BT.CPP in your commercial product and do you need technical support / consulting? +You can contact the primary author, **dfaconti@aurynrobotics.com**, to discuss your use case and needs. - mkdir build; cd build - cmake .. - make - sudo make install +# Star History -If you want to use BT.CPP in your application a typical **CMakeLists.txt** file -will look like this: +[![Star History Chart](https://api.star-history.com/svg?repos=BehaviorTree/BehaviorTree.CPP&type=Date)](https://star-history.com/#BehaviorTree/BehaviorTree.CPP&Date) -```cmake -cmake_minimum_required(VERSION 3.10.2) -project(hello_BT) +## Previous version -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(behaviortree_cpp) +Version 3.8 of the software can be found in the branch +[v3.8](https://github.com/BehaviorTree/BehaviorTree.CPP/tree/v3.8). -add_executable(${PROJECT_NAME} "hello_BT.cpp") -target_link_libraries(${PROJECT_NAME} BT::behaviortree_cpp) -``` +That branch might receive bug fixes, but the new features will be implemented +only in the master branch. # License The MIT License (MIT) -Copyright (c) 2014-2018 Michele Colledanchise +Copyright (c) 2019-2023 Davide Faconti Copyright (c) 2018-2019 Davide Faconti, Eurecat -Copyright (c) 2019-2022 Davide Faconti +Copyright (c) 2014-2018 Michele Colledanchise Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/btcpp_logo.png b/btcpp_logo.png new file mode 100644 index 000000000..a01e03d6b Binary files /dev/null and b/btcpp_logo.png differ diff --git a/btcpp_logo.svg b/btcpp_logo.svg new file mode 100644 index 000000000..fffa7d927 --- /dev/null +++ b/btcpp_logo.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in index 64f20beb5..eaed471b8 100644 --- a/cmake/Config.cmake.in +++ b/cmake/Config.cmake.in @@ -2,6 +2,6 @@ include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") -set(@PROJECT_NAME@_TARGETS "BT::@BEHAVIOR_TREE_LIBRARY@") +set(@PROJECT_NAME@_TARGETS "BT::@PROJECT_NAME@") check_required_components(@PROJECT_NAME@) diff --git a/cmake/FindZMQ.cmake b/cmake/FindZMQ.cmake deleted file mode 100644 index c4b8c23de..000000000 --- a/cmake/FindZMQ.cmake +++ /dev/null @@ -1,66 +0,0 @@ -# - Try to find ZMQ -# Once done this will define -# -# ZMQ_FOUND - system has ZMQ -# ZMQ_INCLUDE_DIRS - the ZMQ include directory -# ZMQ_LIBRARIES - Link these to use ZMQ -# ZMQ_DEFINITIONS - Compiler switches required for using ZMQ -# -# Copyright (c) 2011 Lee Hambley -# -# Redistribution and use is allowed according to the terms of the New -# BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - -if(ZeroMQ_FOUND) - set(ZMQ_FOUND ${ZeroMQ_FOUND}) - set(ZMQ_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIR}) - set(ZMQ_LIBRARIES ${ZeroMQ_LIBRARY}) -else() - - - -if (ZMQ_LIBRARIES AND ZMQ_INCLUDE_DIRS) - # in cache already - set(ZMQ_FOUND TRUE) -else (ZMQ_LIBRARIES AND ZMQ_INCLUDE_DIRS) - - find_path(ZMQ_INCLUDE_DIR - NAMES - zmq.h - PATHS - /usr/include - /usr/local/include - /opt/local/include - ) - - find_library(ZMQ_LIBRARY - NAMES - zmq - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - set(ZMQ_INCLUDE_DIRS - ${ZMQ_INCLUDE_DIR} - ) - - if (ZMQ_LIBRARY) - set(ZMQ_LIBRARIES - ${ZMQ_LIBRARIES} - ${ZMQ_LIBRARY} - ) - endif (ZMQ_LIBRARY) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(ZMQ DEFAULT_MSG ZMQ_LIBRARIES ZMQ_INCLUDE_DIRS) - - # show the ZMQ_INCLUDE_DIRS and ZMQ_LIBRARIES variables only in the advanced view - mark_as_advanced(ZMQ_INCLUDE_DIRS ZMQ_LIBRARIES) - -endif (ZMQ_LIBRARIES AND ZMQ_INCLUDE_DIRS) -endif(ZeroMQ_FOUND) \ No newline at end of file diff --git a/cmake/FindZeroMQ.cmake b/cmake/FindZeroMQ.cmake new file mode 100644 index 000000000..b11258812 --- /dev/null +++ b/cmake/FindZeroMQ.cmake @@ -0,0 +1,67 @@ +# - Try to find ZMQ +# Once done this will define +# +# ZeroMQ_FOUND - system has ZMQ +# ZeroMQ_INCLUDE_DIRS - the ZMQ include directory +# ZeroMQ_LIBRARIES - Link these to use ZMQ +# ZeroMQ_DEFINITIONS - Compiler switches required for using ZMQ +# +# Copyright (c) 2011 Lee Hambley +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +if(ZeroMQ_FOUND) + set(ZeroMQ_FOUND ${ZeroMQ_FOUND}) + set(ZeroMQ_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIR}) + set(ZeroMQ_LIBRARIES ${ZeroMQ_LIBRARY}) +else() + + + +if (ZeroMQ_LIBRARIES AND ZeroMQ_INCLUDE_DIRS) + # in cache already + set(ZeroMQ_FOUND TRUE) +else (ZeroMQ_LIBRARIES AND ZeroMQ_INCLUDE_DIRS) + + find_path(ZeroMQ_INCLUDE_DIR + NAMES + zmq.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + ) + + find_library(ZeroMQ_LIBRARY + NAMES + zmq + libzmq + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ) + + set(ZeroMQ_INCLUDE_DIRS + ${ZeroMQ_INCLUDE_DIR} + ) + + if (ZeroMQ_LIBRARY) + set(ZeroMQ_LIBRARIES + ${ZeroMQ_LIBRARIES} + ${ZeroMQ_LIBRARY} + ) + endif (ZeroMQ_LIBRARY) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(ZeroMQ DEFAULT_MSG ZeroMQ_LIBRARIES ZeroMQ_INCLUDE_DIRS) + + # show the ZeroMQ_INCLUDE_DIRS and ZeroMQ_LIBRARIES variables only in the advanced view + mark_as_advanced(ZeroMQ_INCLUDE_DIRS ZeroMQ_LIBRARIES) + +endif (ZeroMQ_LIBRARIES AND ZeroMQ_INCLUDE_DIRS) +endif(ZeroMQ_FOUND) diff --git a/cmake/ament_build.cmake b/cmake/ament_build.cmake new file mode 100644 index 000000000..ec1e0a66b --- /dev/null +++ b/cmake/ament_build.cmake @@ -0,0 +1,42 @@ +#---- Add the subdirectory cmake ---- +set(CMAKE_CONFIG_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CONFIG_PATH}") + +if(BTCPP_GROOT_INTERFACE) + find_package(ZeroMQ REQUIRED) +endif() + +if(BTCPP_SQLITE_LOGGING) + find_package(SQLite3 REQUIRED) +endif() + +find_package(ament_index_cpp REQUIRED) + +set(BTCPP_EXTRA_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIRS} + ${SQLite3_INCLUDE_DIRS}) + +set( BTCPP_EXTRA_LIBRARIES + $ + $ + $ +) + +ament_export_dependencies(ament_index_cpp) + +set( BTCPP_LIB_DESTINATION lib ) +set( BTCPP_INCLUDE_DESTINATION include ) +set( BTCPP_BIN_DESTINATION bin ) + +mark_as_advanced( + BTCPP_EXTRA_LIBRARIES + BTCPP_EXTRA_INCLUDE_DIRS + BTCPP_LIB_DESTINATION + BTCPP_INCLUDE_DESTINATION + BTCPP_BIN_DESTINATION ) + +macro(export_btcpp_package) + ament_export_include_directories(include) + ament_export_libraries(${BTCPP_LIBRARY}) + ament_export_targets(${BTCPP_LIBRARY}Targets) + ament_package() +endmacro() diff --git a/cmake/conan.cmake b/cmake/conan.cmake new file mode 100644 index 000000000..d36c5ed44 --- /dev/null +++ b/cmake/conan.cmake @@ -0,0 +1,1146 @@ +# The MIT License (MIT) + +# Copyright (c) 2018 JFrog + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + + +# This file comes from: https://github.com/conan-io/cmake-conan. Please refer +# to this repository for issues and documentation. + +# Its purpose is to wrap and launch Conan C/C++ Package Manager when cmake is called. +# It will take CMake current settings (os, compiler, compiler version, architecture) +# and translate them to conan settings for installing and retrieving dependencies. + +# It is intended to facilitate developers building projects that have conan dependencies, +# but it is only necessary on the end-user side. It is not necessary to create conan +# packages, in fact it shouldn't be use for that. Check the project documentation. + +# version: 0.19.0-dev + +include(CMakeParseArguments) + +function(_get_msvc_ide_version result) + set(${result} "" PARENT_SCOPE) + if(NOT MSVC_VERSION VERSION_LESS 1400 AND MSVC_VERSION VERSION_LESS 1500) + set(${result} 8 PARENT_SCOPE) + elseif(NOT MSVC_VERSION VERSION_LESS 1500 AND MSVC_VERSION VERSION_LESS 1600) + set(${result} 9 PARENT_SCOPE) + elseif(NOT MSVC_VERSION VERSION_LESS 1600 AND MSVC_VERSION VERSION_LESS 1700) + set(${result} 10 PARENT_SCOPE) + elseif(NOT MSVC_VERSION VERSION_LESS 1700 AND MSVC_VERSION VERSION_LESS 1800) + set(${result} 11 PARENT_SCOPE) + elseif(NOT MSVC_VERSION VERSION_LESS 1800 AND MSVC_VERSION VERSION_LESS 1900) + set(${result} 12 PARENT_SCOPE) + elseif(NOT MSVC_VERSION VERSION_LESS 1900 AND MSVC_VERSION VERSION_LESS 1910) + set(${result} 14 PARENT_SCOPE) + elseif(NOT MSVC_VERSION VERSION_LESS 1910 AND MSVC_VERSION VERSION_LESS 1920) + set(${result} 15 PARENT_SCOPE) + elseif(NOT MSVC_VERSION VERSION_LESS 1920 AND MSVC_VERSION VERSION_LESS 1930) + set(${result} 16 PARENT_SCOPE) + elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1940) + set(${result} 17 PARENT_SCOPE) + else() + message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]") + endif() +endfunction() + +macro(_conan_detect_build_type) + conan_parse_arguments(${ARGV}) + + if(ARGUMENTS_BUILD_TYPE) + set(_CONAN_SETTING_BUILD_TYPE ${ARGUMENTS_BUILD_TYPE}) + elseif(CMAKE_BUILD_TYPE) + set(_CONAN_SETTING_BUILD_TYPE ${CMAKE_BUILD_TYPE}) + else() + message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)") + endif() + + string(TOUPPER ${_CONAN_SETTING_BUILD_TYPE} _CONAN_SETTING_BUILD_TYPE_UPPER) + if (_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "DEBUG") + set(_CONAN_SETTING_BUILD_TYPE "Debug") + elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELEASE") + set(_CONAN_SETTING_BUILD_TYPE "Release") + elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELWITHDEBINFO") + set(_CONAN_SETTING_BUILD_TYPE "RelWithDebInfo") + elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "MINSIZEREL") + set(_CONAN_SETTING_BUILD_TYPE "MinSizeRel") + endif() +endmacro() + +macro(_conan_check_system_name) + #handle -s os setting + if(CMAKE_SYSTEM_NAME AND NOT CMAKE_SYSTEM_NAME STREQUAL "Generic") + #use default conan os setting if CMAKE_SYSTEM_NAME is not defined + set(CONAN_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}) + if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + set(CONAN_SYSTEM_NAME Macos) + endif() + if(${CMAKE_SYSTEM_NAME} STREQUAL "QNX") + set(CONAN_SYSTEM_NAME Neutrino) + endif() + set(CONAN_SUPPORTED_PLATFORMS Windows Linux Macos Android iOS FreeBSD WindowsStore WindowsCE watchOS tvOS FreeBSD SunOS AIX Arduino Emscripten Neutrino) + list (FIND CONAN_SUPPORTED_PLATFORMS "${CONAN_SYSTEM_NAME}" _index) + if (${_index} GREATER -1) + #check if the cmake system is a conan supported one + set(_CONAN_SETTING_OS ${CONAN_SYSTEM_NAME}) + else() + message(FATAL_ERROR "cmake system ${CONAN_SYSTEM_NAME} is not supported by conan. Use one of ${CONAN_SUPPORTED_PLATFORMS}") + endif() + endif() +endmacro() + +macro(_conan_check_language) + get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES) + if (";${_languages};" MATCHES ";CXX;") + set(LANGUAGE CXX) + set(USING_CXX 1) + elseif (";${_languages};" MATCHES ";C;") + set(LANGUAGE C) + set(USING_CXX 0) + else () + message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unable to detect compiler version.") + endif() +endmacro() + +macro(_conan_detect_compiler) + + conan_parse_arguments(${ARGV}) + + if(ARGUMENTS_ARCH) + set(_CONAN_SETTING_ARCH ${ARGUMENTS_ARCH}) + endif() + + if(USING_CXX) + set(_CONAN_SETTING_COMPILER_CPPSTD ${CMAKE_CXX_STANDARD}) + endif() + + if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU OR ${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL QCC) + # using GCC or QCC + # TODO: Handle other params + string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) + list(GET VERSION_LIST 0 MAJOR) + list(GET VERSION_LIST 1 MINOR) + + if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU) + set(_CONAN_SETTING_COMPILER gcc) + # mimic Conan client autodetection + if (${MAJOR} GREATER_EQUAL 5) + set(COMPILER_VERSION ${MAJOR}) + else() + set(COMPILER_VERSION ${MAJOR}.${MINOR}) + endif() + elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL QCC) + set(_CONAN_SETTING_COMPILER qcc) + set(COMPILER_VERSION ${MAJOR}.${MINOR}) + endif () + + set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) + + if (USING_CXX) + conan_cmake_detect_unix_libcxx(_LIBCXX) + set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) + endif () + elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Intel) + string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) + list(GET VERSION_LIST 0 MAJOR) + list(GET VERSION_LIST 1 MINOR) + set(COMPILER_VERSION ${MAJOR}) + set(_CONAN_SETTING_COMPILER intel) + set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) + if (USING_CXX) + conan_cmake_detect_unix_libcxx(_LIBCXX) + set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) + endif () + elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL AppleClang) + # using AppleClang + string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) + list(GET VERSION_LIST 0 MAJOR) + list(GET VERSION_LIST 1 MINOR) + + # mimic Conan client autodetection + if (${MAJOR} GREATER_EQUAL 13) + set(COMPILER_VERSION ${MAJOR}) + else() + set(COMPILER_VERSION ${MAJOR}.${MINOR}) + endif() + + set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) + + set(_CONAN_SETTING_COMPILER apple-clang) + if (USING_CXX) + conan_cmake_detect_unix_libcxx(_LIBCXX) + set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) + endif () + elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang + AND NOT "${CMAKE_${LANGUAGE}_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC" + AND NOT "${CMAKE_${LANGUAGE}_SIMULATE_ID}" STREQUAL "MSVC") + + string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) + list(GET VERSION_LIST 0 MAJOR) + list(GET VERSION_LIST 1 MINOR) + set(_CONAN_SETTING_COMPILER clang) + + # mimic Conan client autodetection + if (${MAJOR} GREATER_EQUAL 8) + set(COMPILER_VERSION ${MAJOR}) + else() + set(COMPILER_VERSION ${MAJOR}.${MINOR}) + endif() + + set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) + + if(APPLE) + cmake_policy(GET CMP0025 APPLE_CLANG_POLICY) + if(NOT APPLE_CLANG_POLICY STREQUAL NEW) + message(STATUS "Conan: APPLE and Clang detected. Assuming apple-clang compiler. Set CMP0025 to avoid it") + set(_CONAN_SETTING_COMPILER apple-clang) + endif() + endif() + if (USING_CXX) + conan_cmake_detect_unix_libcxx(_LIBCXX) + set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) + endif () + elseif(${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL MSVC + OR (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang + AND "${CMAKE_${LANGUAGE}_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC" + AND "${CMAKE_${LANGUAGE}_SIMULATE_ID}" STREQUAL "MSVC")) + + set(_VISUAL "Visual Studio") + _get_msvc_ide_version(_VISUAL_VERSION) + if("${_VISUAL_VERSION}" STREQUAL "") + message(FATAL_ERROR "Conan: Visual Studio not recognized") + else() + set(_CONAN_SETTING_COMPILER ${_VISUAL}) + set(_CONAN_SETTING_COMPILER_VERSION ${_VISUAL_VERSION}) + endif() + + if(NOT _CONAN_SETTING_ARCH) + if (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "64") + set(_CONAN_SETTING_ARCH x86_64) + elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "^ARM") + message(STATUS "Conan: Using default ARM architecture from MSVC") + set(_CONAN_SETTING_ARCH armv6) + elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "86") + set(_CONAN_SETTING_ARCH x86) + else () + message(FATAL_ERROR "Conan: Unknown MSVC architecture [${MSVC_${LANGUAGE}_ARCHITECTURE_ID}]") + endif() + endif() + + conan_cmake_detect_vs_runtime(_vs_runtime ${ARGV}) + message(STATUS "Conan: Detected VS runtime: ${_vs_runtime}") + set(_CONAN_SETTING_COMPILER_RUNTIME ${_vs_runtime}) + + if (CMAKE_GENERATOR_TOOLSET) + set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) + elseif(CMAKE_VS_PLATFORM_TOOLSET AND (CMAKE_GENERATOR STREQUAL "Ninja")) + set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) + endif() + else() + message(FATAL_ERROR "Conan: compiler setup not recognized") + endif() + +endmacro() + +function(conan_cmake_settings result) + #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER}) + #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER_ID}) + #message(STATUS "VERSION " ${CMAKE_CXX_COMPILER_VERSION}) + #message(STATUS "FLAGS " ${CMAKE_LANG_FLAGS}) + #message(STATUS "LIB ARCH " ${CMAKE_CXX_LIBRARY_ARCHITECTURE}) + #message(STATUS "BUILD TYPE " ${CMAKE_BUILD_TYPE}) + #message(STATUS "GENERATOR " ${CMAKE_GENERATOR}) + #message(STATUS "GENERATOR WIN64 " ${CMAKE_CL_64}) + + message(STATUS "Conan: Automatic detection of conan settings from cmake") + + conan_parse_arguments(${ARGV}) + + _conan_detect_build_type(${ARGV}) + + _conan_check_system_name() + + _conan_check_language() + + _conan_detect_compiler(${ARGV}) + + # If profile is defined it is used + if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND ARGUMENTS_DEBUG_PROFILE) + set(_APPLIED_PROFILES ${ARGUMENTS_DEBUG_PROFILE}) + elseif(CMAKE_BUILD_TYPE STREQUAL "Release" AND ARGUMENTS_RELEASE_PROFILE) + set(_APPLIED_PROFILES ${ARGUMENTS_RELEASE_PROFILE}) + elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND ARGUMENTS_RELWITHDEBINFO_PROFILE) + set(_APPLIED_PROFILES ${ARGUMENTS_RELWITHDEBINFO_PROFILE}) + elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" AND ARGUMENTS_MINSIZEREL_PROFILE) + set(_APPLIED_PROFILES ${ARGUMENTS_MINSIZEREL_PROFILE}) + elseif(ARGUMENTS_PROFILE) + set(_APPLIED_PROFILES ${ARGUMENTS_PROFILE}) + endif() + + foreach(ARG ${_APPLIED_PROFILES}) + set(_SETTINGS ${_SETTINGS} -pr=${ARG}) + endforeach() + foreach(ARG ${ARGUMENTS_PROFILE_BUILD}) + conan_check(VERSION 1.24.0 REQUIRED DETECT_QUIET) + set(_SETTINGS ${_SETTINGS} -pr:b=${ARG}) + endforeach() + + if(NOT _SETTINGS OR ARGUMENTS_PROFILE_AUTO STREQUAL "ALL") + set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version + compiler.runtime compiler.libcxx compiler.toolset) + endif() + + # remove any manually specified settings from the autodetected settings + foreach(ARG ${ARGUMENTS_SETTINGS}) + string(REGEX MATCH "[^=]*" MANUAL_SETTING "${ARG}") + message(STATUS "Conan: ${MANUAL_SETTING} was added as an argument. Not using the autodetected one.") + list(REMOVE_ITEM ARGUMENTS_PROFILE_AUTO "${MANUAL_SETTING}") + endforeach() + + # Automatic from CMake + foreach(ARG ${ARGUMENTS_PROFILE_AUTO}) + string(TOUPPER ${ARG} _arg_name) + string(REPLACE "." "_" _arg_name ${_arg_name}) + if(_CONAN_SETTING_${_arg_name}) + set(_SETTINGS ${_SETTINGS} -s ${ARG}=${_CONAN_SETTING_${_arg_name}}) + endif() + endforeach() + + foreach(ARG ${ARGUMENTS_SETTINGS}) + set(_SETTINGS ${_SETTINGS} -s ${ARG}) + endforeach() + + message(STATUS "Conan: Settings= ${_SETTINGS}") + + set(${result} ${_SETTINGS} PARENT_SCOPE) +endfunction() + + +function(conan_cmake_detect_unix_libcxx result) + # Take into account any -stdlib in compile options + get_directory_property(compile_options DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_OPTIONS) + string(GENEX_STRIP "${compile_options}" compile_options) + + # Take into account any _GLIBCXX_USE_CXX11_ABI in compile definitions + get_directory_property(defines DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS) + string(GENEX_STRIP "${defines}" defines) + + foreach(define ${defines}) + if(define MATCHES "_GLIBCXX_USE_CXX11_ABI") + if(define MATCHES "^-D") + set(compile_options ${compile_options} "${define}") + else() + set(compile_options ${compile_options} "-D${define}") + endif() + endif() + endforeach() + + # add additional compiler options ala cmRulePlaceholderExpander::ExpandRuleVariable + set(EXPAND_CXX_COMPILER ${CMAKE_CXX_COMPILER}) + if(CMAKE_CXX_COMPILER_ARG1) + # CMake splits CXX="foo bar baz" into CMAKE_CXX_COMPILER="foo", CMAKE_CXX_COMPILER_ARG1="bar baz" + # without this, ccache, winegcc, or other wrappers might lose all their arguments + separate_arguments(SPLIT_CXX_COMPILER_ARG1 NATIVE_COMMAND ${CMAKE_CXX_COMPILER_ARG1}) + list(APPEND EXPAND_CXX_COMPILER ${SPLIT_CXX_COMPILER_ARG1}) + endif() + + if(CMAKE_CXX_COMPILE_OPTIONS_TARGET AND CMAKE_CXX_COMPILER_TARGET) + # without --target= we may be calling the wrong underlying GCC + list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_TARGET}${CMAKE_CXX_COMPILER_TARGET}") + endif() + + if(CMAKE_CXX_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN AND CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN) + list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN}${CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN}") + endif() + + if(CMAKE_CXX_COMPILE_OPTIONS_SYSROOT) + # without --sysroot= we may find the wrong #include + if(CMAKE_SYSROOT_COMPILE) + list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT_COMPILE}") + elseif(CMAKE_SYSROOT) + list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT}") + endif() + endif() + + separate_arguments(SPLIT_CXX_FLAGS NATIVE_COMMAND ${CMAKE_CXX_FLAGS}) + + if(CMAKE_OSX_SYSROOT) + set(xcode_sysroot_option "--sysroot=${CMAKE_OSX_SYSROOT}") + endif() + + execute_process( + COMMAND ${CMAKE_COMMAND} -E echo "#include " + COMMAND ${EXPAND_CXX_COMPILER} ${SPLIT_CXX_FLAGS} -x c++ ${xcode_sysroot_option} ${compile_options} -E -dM - + OUTPUT_VARIABLE string_defines + ) + + if(string_defines MATCHES "#define __GLIBCXX__") + # Allow -D_GLIBCXX_USE_CXX11_ABI=ON/OFF as argument to cmake + if(DEFINED _GLIBCXX_USE_CXX11_ABI) + if(_GLIBCXX_USE_CXX11_ABI) + set(${result} libstdc++11 PARENT_SCOPE) + return() + else() + set(${result} libstdc++ PARENT_SCOPE) + return() + endif() + endif() + + if(string_defines MATCHES "#define _GLIBCXX_USE_CXX11_ABI 1\n") + set(${result} libstdc++11 PARENT_SCOPE) + else() + # Either the compiler is missing the define because it is old, and so + # it can't use the new abi, or the compiler was configured to use the + # old abi by the user or distro (e.g. devtoolset on RHEL/CentOS) + set(${result} libstdc++ PARENT_SCOPE) + endif() + else() + set(${result} libc++ PARENT_SCOPE) + endif() +endfunction() + +function(conan_cmake_detect_vs_runtime result) + + conan_parse_arguments(${ARGV}) + if(ARGUMENTS_BUILD_TYPE) + set(build_type "${ARGUMENTS_BUILD_TYPE}") + elseif(CMAKE_BUILD_TYPE) + set(build_type "${CMAKE_BUILD_TYPE}") + else() + message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)") + endif() + + if(build_type) + string(TOUPPER "${build_type}" build_type) + endif() + set(variables CMAKE_CXX_FLAGS_${build_type} CMAKE_C_FLAGS_${build_type} CMAKE_CXX_FLAGS CMAKE_C_FLAGS) + foreach(variable ${variables}) + if(NOT "${${variable}}" STREQUAL "") + string(REPLACE " " ";" flags "${${variable}}") + foreach (flag ${flags}) + if("${flag}" STREQUAL "/MD" OR "${flag}" STREQUAL "/MDd" OR "${flag}" STREQUAL "/MT" OR "${flag}" STREQUAL "/MTd") + string(SUBSTRING "${flag}" 1 -1 runtime) + set(${result} "${runtime}" PARENT_SCOPE) + return() + endif() + endforeach() + endif() + endforeach() + if("${build_type}" STREQUAL "DEBUG") + set(${result} "MDd" PARENT_SCOPE) + else() + set(${result} "MD" PARENT_SCOPE) + endif() +endfunction() + +function(_collect_settings result) + set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version + compiler.runtime compiler.libcxx compiler.toolset + compiler.cppstd) + foreach(ARG ${ARGUMENTS_PROFILE_AUTO}) + string(TOUPPER ${ARG} _arg_name) + string(REPLACE "." "_" _arg_name ${_arg_name}) + if(_CONAN_SETTING_${_arg_name}) + set(detected_setings ${detected_setings} ${ARG}=${_CONAN_SETTING_${_arg_name}}) + endif() + endforeach() + set(${result} ${detected_setings} PARENT_SCOPE) +endfunction() + +function(conan_cmake_autodetect detected_settings) + _conan_detect_build_type(${ARGV}) + _conan_check_system_name() + _conan_check_language() + _conan_detect_compiler(${ARGV}) + _collect_settings(collected_settings) + set(${detected_settings} ${collected_settings} PARENT_SCOPE) +endfunction() + +macro(conan_parse_arguments) + set(options BASIC_SETUP CMAKE_TARGETS UPDATE KEEP_RPATHS NO_LOAD NO_OUTPUT_DIRS + OUTPUT_QUIET NO_IMPORTS SKIP_STD) + set(oneValueArgs CONANFILE ARCH BUILD_TYPE INSTALL_FOLDER OUTPUT_FOLDER CONAN_COMMAND) + set(multiValueArgs DEBUG_PROFILE RELEASE_PROFILE RELWITHDEBINFO_PROFILE MINSIZEREL_PROFILE + PROFILE REQUIRES OPTIONS IMPORTS SETTINGS BUILD ENV GENERATORS PROFILE_AUTO + INSTALL_ARGS CONFIGURATION_TYPES PROFILE_BUILD BUILD_REQUIRES) + cmake_parse_arguments(ARGUMENTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) +endmacro() + +function(old_conan_cmake_install) + # Calls "conan install" + # Argument BUILD is equivalent to --build={missing, PkgName,...} or + # --build when argument is 'BUILD all' (which builds all packages from source) + # Argument CONAN_COMMAND, to specify the conan path, e.g. in case of running from source + # cmake does not identify conan as command, even if it is +x and it is in the path + conan_parse_arguments(${ARGV}) + + if(CONAN_CMAKE_MULTI) + set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake_multi) + else() + set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake) + endif() + + set(CONAN_BUILD_POLICY "") + foreach(ARG ${ARGUMENTS_BUILD}) + if(${ARG} STREQUAL "all") + set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build) + break() + else() + set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build=${ARG}) + endif() + endforeach() + if(ARGUMENTS_CONAN_COMMAND) + set(CONAN_CMD ${ARGUMENTS_CONAN_COMMAND}) + else() + conan_check(REQUIRED) + endif() + set(CONAN_OPTIONS "") + if(ARGUMENTS_CONANFILE) + if(IS_ABSOLUTE ${ARGUMENTS_CONANFILE}) + set(CONANFILE ${ARGUMENTS_CONANFILE}) + else() + set(CONANFILE ${CMAKE_CURRENT_SOURCE_DIR}/${ARGUMENTS_CONANFILE}) + endif() + else() + set(CONANFILE ".") + endif() + foreach(ARG ${ARGUMENTS_OPTIONS}) + set(CONAN_OPTIONS ${CONAN_OPTIONS} -o=${ARG}) + endforeach() + if(ARGUMENTS_UPDATE) + set(CONAN_INSTALL_UPDATE --update) + endif() + if(ARGUMENTS_NO_IMPORTS) + set(CONAN_INSTALL_NO_IMPORTS --no-imports) + endif() + set(CONAN_INSTALL_FOLDER "") + if(ARGUMENTS_INSTALL_FOLDER) + set(CONAN_INSTALL_FOLDER -if=${ARGUMENTS_INSTALL_FOLDER}) + endif() + set(CONAN_OUTPUT_FOLDER "") + if(ARGUMENTS_OUTPUT_FOLDER) + set(CONAN_OUTPUT_FOLDER -of=${ARGUMENTS_OUTPUT_FOLDER}) + endif() + foreach(ARG ${ARGUMENTS_GENERATORS}) + set(CONAN_GENERATORS ${CONAN_GENERATORS} -g=${ARG}) + endforeach() + foreach(ARG ${ARGUMENTS_ENV}) + set(CONAN_ENV_VARS ${CONAN_ENV_VARS} -e=${ARG}) + endforeach() + set(conan_args install ${CONANFILE} ${settings} ${CONAN_ENV_VARS} ${CONAN_GENERATORS} ${CONAN_BUILD_POLICY} ${CONAN_INSTALL_UPDATE} ${CONAN_INSTALL_NO_IMPORTS} ${CONAN_OPTIONS} ${CONAN_INSTALL_FOLDER} ${ARGUMENTS_INSTALL_ARGS}) + + string (REPLACE ";" " " _conan_args "${conan_args}") + message(STATUS "Conan executing: ${CONAN_CMD} ${_conan_args}") + + if(ARGUMENTS_OUTPUT_QUIET) + execute_process(COMMAND ${CONAN_CMD} ${conan_args} + RESULT_VARIABLE return_code + OUTPUT_VARIABLE conan_output + ERROR_VARIABLE conan_output + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + else() + execute_process(COMMAND ${CONAN_CMD} ${conan_args} + RESULT_VARIABLE return_code + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + endif() + + if(NOT "${return_code}" STREQUAL "0") + message(FATAL_ERROR "Conan install failed='${return_code}'") + endif() + +endfunction() + +function(conan_cmake_install) + if(DEFINED CONAN_COMMAND) + set(CONAN_CMD ${CONAN_COMMAND}) + else() + conan_check(REQUIRED) + endif() + + set(installOptions UPDATE NO_IMPORTS OUTPUT_QUIET ERROR_QUIET) + set(installOneValueArgs PATH_OR_REFERENCE REFERENCE REMOTE LOCKFILE LOCKFILE_OUT LOCKFILE_NODE_ID INSTALL_FOLDER OUTPUT_FOLDER) + set(installMultiValueArgs GENERATOR BUILD ENV ENV_HOST ENV_BUILD OPTIONS_HOST OPTIONS OPTIONS_BUILD PROFILE + PROFILE_HOST PROFILE_BUILD SETTINGS SETTINGS_HOST SETTINGS_BUILD CONF CONF_HOST CONF_BUILD) + cmake_parse_arguments(ARGS "${installOptions}" "${installOneValueArgs}" "${installMultiValueArgs}" ${ARGN}) + foreach(arg ${installOptions}) + if(ARGS_${arg}) + set(${arg} ${${arg}} ${ARGS_${arg}}) + endif() + endforeach() + foreach(arg ${installOneValueArgs}) + if(DEFINED ARGS_${arg}) + if("${arg}" STREQUAL "REMOTE") + set(flag "--remote") + elseif("${arg}" STREQUAL "LOCKFILE") + set(flag "--lockfile") + elseif("${arg}" STREQUAL "LOCKFILE_OUT") + set(flag "--lockfile-out") + elseif("${arg}" STREQUAL "LOCKFILE_NODE_ID") + set(flag "--lockfile-node-id") + elseif("${arg}" STREQUAL "INSTALL_FOLDER") + set(flag "--install-folder") + elseif("${arg}" STREQUAL "OUTPUT_FOLDER") + set(flag "--output-folder") + endif() + set(${arg} ${${arg}} ${flag} ${ARGS_${arg}}) + endif() + endforeach() + foreach(arg ${installMultiValueArgs}) + if(DEFINED ARGS_${arg}) + if("${arg}" STREQUAL "GENERATOR") + set(flag "--generator") + elseif("${arg}" STREQUAL "BUILD") + set(flag "--build") + elseif("${arg}" STREQUAL "ENV") + set(flag "--env") + elseif("${arg}" STREQUAL "ENV_HOST") + set(flag "--env:host") + elseif("${arg}" STREQUAL "ENV_BUILD") + set(flag "--env:build") + elseif("${arg}" STREQUAL "OPTIONS") + set(flag "--options") + elseif("${arg}" STREQUAL "OPTIONS_HOST") + set(flag "--options:host") + elseif("${arg}" STREQUAL "OPTIONS_BUILD") + set(flag "--options:build") + elseif("${arg}" STREQUAL "PROFILE") + set(flag "--profile") + elseif("${arg}" STREQUAL "PROFILE_HOST") + set(flag "--profile:host") + elseif("${arg}" STREQUAL "PROFILE_BUILD") + set(flag "--profile:build") + elseif("${arg}" STREQUAL "SETTINGS") + set(flag "--settings") + elseif("${arg}" STREQUAL "SETTINGS_HOST") + set(flag "--settings:host") + elseif("${arg}" STREQUAL "SETTINGS_BUILD") + set(flag "--settings:build") + elseif("${arg}" STREQUAL "CONF") + set(flag "--conf") + elseif("${arg}" STREQUAL "CONF_HOST") + set(flag "--conf:host") + elseif("${arg}" STREQUAL "CONF_BUILD") + set(flag "--conf:build") + endif() + list(LENGTH ARGS_${arg} numargs) + foreach(item ${ARGS_${arg}}) + if(${item} STREQUAL "all" AND ${arg} STREQUAL "BUILD") + set(${arg} "--build") + break() + endif() + set(${arg} ${${arg}} ${flag} ${item}) + endforeach() + endif() + endforeach() + if(DEFINED UPDATE) + set(UPDATE --update) + endif() + if(DEFINED NO_IMPORTS) + set(NO_IMPORTS --no-imports) + endif() + set(install_args install ${PATH_OR_REFERENCE} ${REFERENCE} ${UPDATE} ${NO_IMPORTS} ${REMOTE} + ${LOCKFILE} ${LOCKFILE_OUT} ${LOCKFILE_NODE_ID} ${INSTALL_FOLDER} + ${OUTPUT_FOLDER} ${GENERATOR} ${BUILD} ${ENV} ${ENV_HOST} ${ENV_BUILD} + ${OPTIONS} ${OPTIONS_HOST} ${OPTIONS_BUILD} ${PROFILE} ${PROFILE_HOST} + ${PROFILE_BUILD} ${SETTINGS} ${SETTINGS_HOST} ${SETTINGS_BUILD} + ${CONF} ${CONF_HOST} ${CONF_BUILD}) + + string(REPLACE ";" " " _install_args "${install_args}") + message(STATUS "Conan executing: ${CONAN_CMD} ${_install_args}") + + if(ARGS_OUTPUT_QUIET) + set(OUTPUT_OPT OUTPUT_QUIET) + endif() + if(ARGS_ERROR_QUIET) + set(ERROR_OPT ERROR_QUIET) + endif() + + execute_process(COMMAND ${CONAN_CMD} ${install_args} + RESULT_VARIABLE return_code + ${OUTPUT_OPT} + ${ERROR_OPT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + if(NOT "${return_code}" STREQUAL "0") + if (ARGS_ERROR_QUIET) + message(WARNING "Conan install failed='${return_code}'") + else() + message(FATAL_ERROR "Conan install failed='${return_code}'") + endif() + endif() + +endfunction() + +function(conan_cmake_lock_create) + if(DEFINED CONAN_COMMAND) + set(CONAN_CMD ${CONAN_COMMAND}) + else() + conan_check(REQUIRED) + endif() + + set(lockCreateOptions UPDATE BASE OUTPUT_QUIET ERROR_QUIET) + set(lockCreateOneValueArgs PATH REFERENCE REMOTE LOCKFILE LOCKFILE_OUT) + set(lockCreateMultiValueArgs BUILD ENV ENV_HOST ENV_BUILD OPTIONS_HOST OPTIONS OPTIONS_BUILD PROFILE + PROFILE_HOST PROFILE_BUILD SETTINGS SETTINGS_HOST SETTINGS_BUILD) + cmake_parse_arguments(ARGS "${lockCreateOptions}" "${lockCreateOneValueArgs}" "${lockCreateMultiValueArgs}" ${ARGN}) + foreach(arg ${lockCreateOptions}) + if(ARGS_${arg}) + set(${arg} ${${arg}} ${ARGS_${arg}}) + endif() + endforeach() + foreach(arg ${lockCreateOneValueArgs}) + if(DEFINED ARGS_${arg}) + if("${arg}" STREQUAL "REMOTE") + set(flag "--remote") + elseif("${arg}" STREQUAL "LOCKFILE") + set(flag "--lockfile") + elseif("${arg}" STREQUAL "LOCKFILE_OUT") + set(flag "--lockfile-out") + endif() + set(${arg} ${${arg}} ${flag} ${ARGS_${arg}}) + endif() + endforeach() + foreach(arg ${lockCreateMultiValueArgs}) + if(DEFINED ARGS_${arg}) + if("${arg}" STREQUAL "BUILD") + set(flag "--build") + elseif("${arg}" STREQUAL "ENV") + set(flag "--env") + elseif("${arg}" STREQUAL "ENV_HOST") + set(flag "--env:host") + elseif("${arg}" STREQUAL "ENV_BUILD") + set(flag "--env:build") + elseif("${arg}" STREQUAL "OPTIONS") + set(flag "--options") + elseif("${arg}" STREQUAL "OPTIONS_HOST") + set(flag "--options:host") + elseif("${arg}" STREQUAL "OPTIONS_BUILD") + set(flag "--options:build") + elseif("${arg}" STREQUAL "PROFILE") + set(flag "--profile") + elseif("${arg}" STREQUAL "PROFILE_HOST") + set(flag "--profile:host") + elseif("${arg}" STREQUAL "PROFILE_BUILD") + set(flag "--profile:build") + elseif("${arg}" STREQUAL "SETTINGS") + set(flag "--settings") + elseif("${arg}" STREQUAL "SETTINGS_HOST") + set(flag "--settings:host") + elseif("${arg}" STREQUAL "SETTINGS_BUILD") + set(flag "--settings:build") + endif() + list(LENGTH ARGS_${arg} numargs) + foreach(item ${ARGS_${arg}}) + if(${item} STREQUAL "all" AND ${arg} STREQUAL "BUILD") + set(${arg} "--build") + break() + endif() + set(${arg} ${${arg}} ${flag} ${item}) + endforeach() + endif() + endforeach() + if(DEFINED UPDATE) + set(UPDATE --update) + endif() + if(DEFINED BASE) + set(BASE --base) + endif() + set(lock_create_Args lock create ${PATH} ${REFERENCE} ${UPDATE} ${BASE} ${REMOTE} ${LOCKFILE} ${LOCKFILE_OUT} ${LOCKFILE_NODE_ID} ${INSTALL_FOLDER} + ${GENERATOR} ${BUILD} ${ENV} ${ENV_HOST} ${ENV_BUILD} ${OPTIONS} ${OPTIONS_HOST} ${OPTIONS_BUILD} + ${PROFILE} ${PROFILE_HOST} ${PROFILE_BUILD} ${SETTINGS} ${SETTINGS_HOST} ${SETTINGS_BUILD}) + + string(REPLACE ";" " " _lock_create_Args "${lock_create_Args}") + message(STATUS "Conan executing: ${CONAN_CMD} ${_lock_create_Args}") + + if(ARGS_OUTPUT_QUIET) + set(OUTPUT_OPT OUTPUT_QUIET) + endif() + if(ARGS_ERROR_QUIET) + set(ERROR_OPT ERROR_QUIET) + endif() + + execute_process(COMMAND ${CONAN_CMD} ${lock_create_Args} + RESULT_VARIABLE return_code + ${OUTPUT_OPT} + ${ERROR_OPT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + if(NOT "${return_code}" STREQUAL "0") + if (ARGS_ERROR_QUIET) + message(WARNING "Conan lock create failed='${return_code}'") + else() + message(FATAL_ERROR "Conan lock create failed='${return_code}'") + endif() + endif() +endfunction() + +function(conan_cmake_setup_conanfile) + conan_parse_arguments(${ARGV}) + if(ARGUMENTS_CONANFILE) + get_filename_component(_CONANFILE_NAME ${ARGUMENTS_CONANFILE} NAME) + # configure_file will make sure cmake re-runs when conanfile is updated + configure_file(${ARGUMENTS_CONANFILE} ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk COPYONLY) + file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk) + else() + conan_cmake_generate_conanfile(ON ${ARGV}) + endif() +endfunction() + +function(conan_cmake_configure) + conan_cmake_generate_conanfile(OFF ${ARGV}) +endfunction() + +# Generate, writing in disk a conanfile.txt with the requires, options, and imports +# specified as arguments +# This will be considered as temporary file, generated in CMAKE_CURRENT_BINARY_DIR) +function(conan_cmake_generate_conanfile DEFAULT_GENERATOR) + + conan_parse_arguments(${ARGV}) + + set(_FN "${CMAKE_CURRENT_BINARY_DIR}/conanfile.txt") + file(WRITE ${_FN} "") + + if(DEFINED ARGUMENTS_REQUIRES) + file(APPEND ${_FN} "[requires]\n") + foreach(REQUIRE ${ARGUMENTS_REQUIRES}) + file(APPEND ${_FN} ${REQUIRE} "\n") + endforeach() + endif() + + if (DEFAULT_GENERATOR OR DEFINED ARGUMENTS_GENERATORS) + file(APPEND ${_FN} "[generators]\n") + if (DEFAULT_GENERATOR) + file(APPEND ${_FN} "cmake\n") + endif() + if (DEFINED ARGUMENTS_GENERATORS) + foreach(GENERATOR ${ARGUMENTS_GENERATORS}) + file(APPEND ${_FN} ${GENERATOR} "\n") + endforeach() + endif() + endif() + + if(DEFINED ARGUMENTS_BUILD_REQUIRES) + file(APPEND ${_FN} "[build_requires]\n") + foreach(BUILD_REQUIRE ${ARGUMENTS_BUILD_REQUIRES}) + file(APPEND ${_FN} ${BUILD_REQUIRE} "\n") + endforeach() + endif() + + if(DEFINED ARGUMENTS_IMPORTS) + file(APPEND ${_FN} "[imports]\n") + foreach(IMPORTS ${ARGUMENTS_IMPORTS}) + file(APPEND ${_FN} ${IMPORTS} "\n") + endforeach() + endif() + + if(DEFINED ARGUMENTS_OPTIONS) + file(APPEND ${_FN} "[options]\n") + foreach(OPTION ${ARGUMENTS_OPTIONS}) + file(APPEND ${_FN} ${OPTION} "\n") + endforeach() + endif() + +endfunction() + + +macro(conan_load_buildinfo) + if(CONAN_CMAKE_MULTI) + set(_CONANBUILDINFO conanbuildinfo_multi.cmake) + else() + set(_CONANBUILDINFO conanbuildinfo.cmake) + endif() + if(ARGUMENTS_INSTALL_FOLDER) + set(_CONANBUILDINFOFOLDER ${ARGUMENTS_INSTALL_FOLDER}) + else() + set(_CONANBUILDINFOFOLDER ${CMAKE_CURRENT_BINARY_DIR}) + endif() + # Checks for the existence of conanbuildinfo.cmake, and loads it + # important that it is macro, so variables defined at parent scope + if(EXISTS "${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}") + message(STATUS "Conan: Loading ${_CONANBUILDINFO}") + include(${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}) + else() + message(FATAL_ERROR "${_CONANBUILDINFO} doesn't exist in ${CMAKE_CURRENT_BINARY_DIR}") + endif() +endmacro() + + +macro(conan_cmake_run) + conan_parse_arguments(${ARGV}) + + if(ARGUMENTS_CONFIGURATION_TYPES AND NOT CMAKE_CONFIGURATION_TYPES) + message(WARNING "CONFIGURATION_TYPES should only be specified for multi-configuration generators") + elseif(ARGUMENTS_CONFIGURATION_TYPES AND ARGUMENTS_BUILD_TYPE) + message(WARNING "CONFIGURATION_TYPES and BUILD_TYPE arguments should not be defined at the same time.") + endif() + + if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE AND NOT CONAN_EXPORTED + AND NOT ARGUMENTS_BUILD_TYPE) + set(CONAN_CMAKE_MULTI ON) + if (NOT ARGUMENTS_CONFIGURATION_TYPES) + set(ARGUMENTS_CONFIGURATION_TYPES "Release;Debug") + endif() + message(STATUS "Conan: Using cmake-multi generator") + else() + set(CONAN_CMAKE_MULTI OFF) + endif() + + if(NOT CONAN_EXPORTED) + conan_cmake_setup_conanfile(${ARGV}) + if(CONAN_CMAKE_MULTI) + foreach(CMAKE_BUILD_TYPE ${ARGUMENTS_CONFIGURATION_TYPES}) + set(ENV{CONAN_IMPORT_PATH} ${CMAKE_BUILD_TYPE}) + conan_cmake_settings(settings ${ARGV}) + old_conan_cmake_install(SETTINGS ${settings} ${ARGV}) + endforeach() + set(CMAKE_BUILD_TYPE) + else() + conan_cmake_settings(settings ${ARGV}) + old_conan_cmake_install(SETTINGS ${settings} ${ARGV}) + endif() + endif() + + if (NOT ARGUMENTS_NO_LOAD) + conan_load_buildinfo() + endif() + + if(ARGUMENTS_BASIC_SETUP) + foreach(_option CMAKE_TARGETS KEEP_RPATHS NO_OUTPUT_DIRS SKIP_STD) + if(ARGUMENTS_${_option}) + if(${_option} STREQUAL "CMAKE_TARGETS") + list(APPEND _setup_options "TARGETS") + else() + list(APPEND _setup_options ${_option}) + endif() + endif() + endforeach() + conan_basic_setup(${_setup_options}) + endif() +endmacro() + +function(conan_version result) + set(${result} "" PARENT_SCOPE) + + if(NOT CONAN_CMD) + find_program(CONAN_CMD conan) + if(NOT CONAN_CMD AND CONAN_REQUIRED) + message(FATAL_ERROR "Conan executable not found! Please install conan.") + endif() + endif() + + execute_process(COMMAND ${CONAN_CMD} --version + RESULT_VARIABLE return_code + OUTPUT_VARIABLE CONAN_VERSION_OUTPUT + ERROR_VARIABLE CONAN_VERSION_OUTPUT) + + if(NOT "${return_code}" STREQUAL "0") + message(FATAL_ERROR "Conan --version failed='${return_code}'") + endif() + + string(REGEX MATCH ".*Conan version ([0-9]+\\.[0-9]+\\.[0-9]+)" FOO "${CONAN_VERSION_OUTPUT}") + + set(${result} ${CMAKE_MATCH_1} PARENT_SCOPE) +endfunction() + +macro(conan_check) + # Checks conan availability in PATH + # Arguments REQUIRED, DETECT_QUIET and VERSION are optional + # Example usage: + # conan_check(VERSION 1.0.0 REQUIRED) + set(options REQUIRED DETECT_QUIET) + set(oneValueArgs VERSION) + cmake_parse_arguments(CONAN "${options}" "${oneValueArgs}" "" ${ARGN}) + if(NOT CONAN_DETECT_QUIET) + message(STATUS "Conan: checking conan executable") + endif() + + find_program(CONAN_CMD conan) + if(NOT CONAN_CMD AND CONAN_REQUIRED) + message(FATAL_ERROR "Conan executable not found! Please install conan.") + endif() + if(NOT CONAN_DETECT_QUIET) + message(STATUS "Conan: Found program ${CONAN_CMD}") + endif() + + conan_version(CONAN_DETECTED_VERSION) + + if(NOT CONAN_DETECT_QUIET) + message(STATUS "Conan: Version found ${CONAN_DETECTED_VERSION}") + endif() + + if(DEFINED CONAN_VERSION) + if(${CONAN_DETECTED_VERSION} VERSION_LESS ${CONAN_VERSION}) + message(FATAL_ERROR "Conan outdated. Installed: ${CONAN_DETECTED_VERSION}, \ + required: ${CONAN_VERSION}. Consider updating via 'pip \ + install conan==${CONAN_VERSION}'.") + endif() + endif() +endmacro() + +function(conan_add_remote) + # Adds a remote + # Arguments URL and NAME are required, INDEX, COMMAND and VERIFY_SSL are optional + # Example usage: + # conan_add_remote(NAME bincrafters INDEX 1 + # URL https://api.bintray.com/conan/bincrafters/public-conan + # VERIFY_SSL True) + set(oneValueArgs URL NAME INDEX COMMAND VERIFY_SSL) + cmake_parse_arguments(CONAN "" "${oneValueArgs}" "" ${ARGN}) + + if(DEFINED CONAN_INDEX) + set(CONAN_INDEX_ARG "-i ${CONAN_INDEX}") + endif() + if(DEFINED CONAN_COMMAND) + set(CONAN_CMD ${CONAN_COMMAND}) + else() + conan_check(REQUIRED DETECT_QUIET) + endif() + set(CONAN_VERIFY_SSL_ARG "True") + if(DEFINED CONAN_VERIFY_SSL) + set(CONAN_VERIFY_SSL_ARG ${CONAN_VERIFY_SSL}) + endif() + message(STATUS "Conan: Adding ${CONAN_NAME} remote repository (${CONAN_URL}) verify ssl (${CONAN_VERIFY_SSL_ARG})") + execute_process(COMMAND ${CONAN_CMD} remote add ${CONAN_NAME} ${CONAN_INDEX_ARG} -f ${CONAN_URL} ${CONAN_VERIFY_SSL_ARG} + RESULT_VARIABLE return_code) + if(NOT "${return_code}" STREQUAL "0") + message(FATAL_ERROR "Conan remote failed='${return_code}'") + endif() +endfunction() + +macro(conan_config_install) + # install a full configuration from a local or remote zip file + # Argument ITEM is required, arguments TYPE, SOURCE, TARGET and VERIFY_SSL are optional + # Example usage: + # conan_config_install(ITEM https://github.com/conan-io/cmake-conan.git + # TYPE git SOURCE source-folder TARGET target-folder VERIFY_SSL false) + set(oneValueArgs ITEM TYPE SOURCE TARGET VERIFY_SSL) + set(multiValueArgs ARGS) + cmake_parse_arguments(CONAN "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(DEFINED CONAN_COMMAND) + set(CONAN_CMD ${CONAN_COMMAND}) + else() + conan_check(REQUIRED) + endif() + + if(DEFINED CONAN_VERIFY_SSL) + set(CONAN_VERIFY_SSL_ARG "--verify-ssl=${CONAN_VERIFY_SSL}") + endif() + + if(DEFINED CONAN_TYPE) + set(CONAN_TYPE_ARG "--type=${CONAN_TYPE}") + endif() + + if(DEFINED CONAN_ARGS) + # Convert ; separated multi arg list into space separated string + string(REPLACE ";" " " l_CONAN_ARGS "${CONAN_ARGS}") + set(CONAN_ARGS_ARGS "--args=${l_CONAN_ARGS}") + endif() + + if(DEFINED CONAN_SOURCE) + set(CONAN_SOURCE_ARGS "--source-folder=${CONAN_SOURCE}") + endif() + + if(DEFINED CONAN_TARGET) + set(CONAN_TARGET_ARGS "--target-folder=${CONAN_TARGET}") + endif() + + set (CONAN_CONFIG_INSTALL_ARGS ${CONAN_VERIFY_SSL_ARG} + ${CONAN_TYPE_ARG} + ${CONAN_ARGS_ARGS} + ${CONAN_SOURCE_ARGS} + ${CONAN_TARGET_ARGS}) + + message(STATUS "Conan: Installing config from ${CONAN_ITEM}") + execute_process(COMMAND ${CONAN_CMD} config install ${CONAN_ITEM} ${CONAN_CONFIG_INSTALL_ARGS} + RESULT_VARIABLE return_code) + if(NOT "${return_code}" STREQUAL "0") + message(FATAL_ERROR "Conan config failed='${return_code}'") + endif() +endmacro() + + +function(conan_cmake_profile) + set(profileOneValueArgs FILEPATH INCLUDE) + set(profileMultiValueArgs SETTINGS OPTIONS CONF ENV BUILDENV RUNENV TOOL_REQUIRES) + cmake_parse_arguments(ARGS "" "${profileOneValueArgs}" "${profileMultiValueArgs}" ${ARGN}) + + if(DEFINED ARGS_FILEPATH) + set(_FN "${ARGS_FILEPATH}") + else() + set(_FN "${CMAKE_CURRENT_BINARY_DIR}/profile") + endif() + message(STATUS "Conan: Creating profile ${_FN}") + file(WRITE ${_FN} "") + + if(DEFINED ARGS_INCLUDE) + file(APPEND ${_FN} "include(${ARGS_INCLUDE})\n") + endif() + + if(DEFINED ARGS_SETTINGS) + file(APPEND ${_FN} "[settings]\n") + foreach(SETTING ${ARGS_SETTINGS}) + file(APPEND ${_FN} ${SETTING} "\n") + endforeach() + endif() + + if(DEFINED ARGS_OPTIONS) + file(APPEND ${_FN} "[options]\n") + foreach(OPTION ${ARGS_OPTIONS}) + file(APPEND ${_FN} ${OPTION} "\n") + endforeach() + endif() + + if(DEFINED ARGS_CONF) + file(APPEND ${_FN} "[conf]\n") + foreach(CONF ${ARGS_CONF}) + file(APPEND ${_FN} ${CONF} "\n") + endforeach() + endif() + + if(DEFINED ARGS_ENV) + file(APPEND ${_FN} "[env]\n") + foreach(ENV ${ARGS_ENV}) + file(APPEND ${_FN} ${ENV} "\n") + endforeach() + endif() + + if(DEFINED ARGS_BUILDENV) + file(APPEND ${_FN} "[buildenv]\n") + foreach(BUILDENV ${ARGS_BUILDENV}) + file(APPEND ${_FN} ${BUILDENV} "\n") + endforeach() + endif() + + if(DEFINED ARGS_RUNENV) + file(APPEND ${_FN} "[runenv]\n") + foreach(RUNENV ${ARGS_RUNENV}) + file(APPEND ${_FN} ${RUNENV} "\n") + endforeach() + endif() + + if(DEFINED ARGS_TOOL_REQUIRES) + file(APPEND ${_FN} "[tool_requires]\n") + foreach(TOOL_REQUIRE ${ARGS_TOOL_REQUIRES}) + file(APPEND ${_FN} ${TOOL_REQUIRE} "\n") + endforeach() + endif() +endfunction() diff --git a/cmake/conan_build.cmake b/cmake/conan_build.cmake new file mode 100644 index 000000000..3bf3a7225 --- /dev/null +++ b/cmake/conan_build.cmake @@ -0,0 +1,49 @@ +list(APPEND CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}") + +if(BTCPP_GROOT_INTERFACE) + find_package(ZeroMQ REQUIRED) + list(APPEND BTCPP_EXTRA_LIBRARIES ${ZeroMQ_LIBRARIES}) + list(APPEND BTCPP_EXTRA_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIRS}) + message(STATUS "ZeroMQ_LIBRARIES: ${ZeroMQ_LIBRARIES}") +endif() + +if(BTCPP_SQLITE_LOGGING) + find_package(SQLite3 REQUIRED) + list(APPEND BTCPP_EXTRA_LIBRARIES ${SQLite3_LIBRARIES}) + message(STATUS "SQLite3_LIBRARIES: ${SQLite3_LIBRARIES}") +endif() + + +set( BTCPP_LIB_DESTINATION lib ) +set( BTCPP_INCLUDE_DESTINATION include ) +set( BTCPP_BIN_DESTINATION bin ) + +mark_as_advanced( + BTCPP_EXTRA_LIBRARIES + BTCPP_LIB_DESTINATION + BTCPP_INCLUDE_DESTINATION + BTCPP_BIN_DESTINATION ) + +macro(export_btcpp_package) + + install(EXPORT ${PROJECT_NAME}Targets + FILE "${PROJECT_NAME}Targets.cmake" + DESTINATION "${BTCPP_LIB_DESTINATION}/cmake/${PROJECT_NAME}" + NAMESPACE BT:: + ) + export(PACKAGE ${PROJECT_NAME}) + + include(CMakePackageConfigHelpers) + + configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION "${BTCPP_LIB_DESTINATION}/cmake/${PROJECT_NAME}" + ) + + install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + DESTINATION "${BTCPP_LIB_DESTINATION}/cmake/${PROJECT_NAME}" + ) +endmacro() diff --git a/cmake/fuzzing_build.cmake b/cmake/fuzzing_build.cmake new file mode 100644 index 000000000..43f367001 --- /dev/null +++ b/cmake/fuzzing_build.cmake @@ -0,0 +1,153 @@ +# Fuzzing configuration +# Supports both local fuzzing and OSS-Fuzz integration + +# Detect if we're running in OSS-Fuzz environment +if(DEFINED ENV{LIB_FUZZING_ENGINE}) + set(OSS_FUZZ ON) + message(STATUS "OSS-Fuzz environment detected") +else() + set(OSS_FUZZ OFF) +endif() + +# Auto-detect AFL++ compiler if not in OSS-Fuzz mode +if(NOT OSS_FUZZ AND (CMAKE_C_COMPILER MATCHES ".*afl-.*" OR CMAKE_CXX_COMPILER MATCHES ".*afl-.*")) + set(USE_AFLPLUSPLUS ON CACHE BOOL "Use AFL++ instead of libFuzzer" FORCE) + message(STATUS "AFL++ compiler detected - automatically enabling AFL++ mode") +endif() + +# When building for fuzzing, we want static library by default +set(BTCPP_SHARED_LIBS OFF CACHE BOOL "Build static library for fuzzing" FORCE) + +# Only apply static linking settings if explicitly requested +if(FORCE_STATIC_LINKING) + set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(BUILD_SHARED_LIBS OFF) + + # Force static linking for dependencies + if(BTCPP_GROOT_INTERFACE) + set(ZeroMQ_USE_STATIC_LIBS ON) + set(ZEROMQ_STATIC_LIBRARY ON) + endif() + + if(BTCPP_SQLITE_LOGGING) + set(SQLite3_USE_STATIC_LIBS ON) + endif() +endif() + +# Set up flags for local fuzzing (not used for OSS-Fuzz) +if(NOT OSS_FUZZ) + list(APPEND BASE_FLAGS -O2) + + if(USE_AFLPLUSPLUS) + set(SANITIZER_FLAGS + -fsanitize=address,undefined + ) + else() + # For libFuzzer, use fuzzer-no-link for the library + set(SANITIZER_FLAGS + -fsanitize=address,undefined,fuzzer-no-link + ) + endif() + + # Apply sanitizer flags to the base library + list(APPEND BASE_FLAGS ${SANITIZER_FLAGS}) + + add_compile_options(${BASE_FLAGS}) + add_link_options(${BASE_FLAGS}) +endif() + +# Disable certain features during fuzzing +set(BTCPP_EXAMPLES OFF CACHE BOOL "Disable examples during fuzzing" FORCE) +set(BTCPP_BUILD_TOOLS OFF CACHE BOOL "Disable tools during fuzzing" FORCE) +set(BTCPP_UNIT_TESTS OFF CACHE BOOL "Disable tests during fuzzing" FORCE) +set(BTCPP_SHARED_LIBS OFF CACHE BOOL "Build static library for fuzzing" FORCE) + +# Function to apply fuzzing flags for local development builds +function(apply_local_fuzzing_flags target) + target_compile_options(${target} PRIVATE + ${BASE_FLAGS} + ${SANITIZER_FLAGS} + ) + + if(FORCE_STATIC_LINKING) + if(USE_AFLPLUSPLUS) + target_link_options(${target} PRIVATE + ${BASE_FLAGS} + ${SANITIZER_FLAGS} + -static-libstdc++ + -static-libgcc + -fsanitize=fuzzer + ) + else() + target_link_options(${target} PRIVATE + ${BASE_FLAGS} + -fsanitize=fuzzer + ${SANITIZER_FLAGS} + -static-libstdc++ + -static-libgcc + ) + endif() + else() + if(USE_AFLPLUSPLUS) + target_link_options(${target} PRIVATE + ${BASE_FLAGS} + ${SANITIZER_FLAGS} + -fsanitize=fuzzer + ) + else() + target_link_options(${target} PRIVATE + ${BASE_FLAGS} + -fsanitize=fuzzer + ${SANITIZER_FLAGS} + ) + endif() + endif() +endfunction() + +# Function to add fuzzing targets - compatible with both local and OSS-Fuzz builds +function(add_fuzzing_targets) + set(FUZZERS bt_fuzzer script_fuzzer bb_fuzzer) + + foreach(fuzzer ${FUZZERS}) + add_executable(${fuzzer} fuzzing/${fuzzer}.cpp) + + if(OSS_FUZZ) + # For OSS-Fuzz environment, we rely on environment variables + # like $CC, $CXX, $CFLAGS, $CXXFLAGS, and $LIB_FUZZING_ENGINE + target_link_libraries(${fuzzer} PRIVATE + ${BTCPP_LIBRARY} + ${BTCPP_EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE} + ) + else() + # For local development, use our own flags + apply_local_fuzzing_flags(${fuzzer}) + target_link_libraries(${fuzzer} PRIVATE + ${BTCPP_LIBRARY} + ${BTCPP_EXTRA_LIBRARIES} + ) + endif() + + # Setup corpus directories (useful for both environments) + set(CORPUS_DIR ${CMAKE_BINARY_DIR}/corpus/${fuzzer}) + file(MAKE_DIRECTORY ${CORPUS_DIR}) + endforeach() + + # Copy corpus files if they exist (useful for local testing) + # OSS-Fuzz provides its own corpus handling + if(NOT OSS_FUZZ) + file(GLOB BT_CORPUS_FILES "${CMAKE_SOURCE_DIR}/fuzzing/corpus/bt_corpus/*") + file(GLOB SCRIPT_CORPUS_FILES "${CMAKE_SOURCE_DIR}/fuzzing/corpus/script_corpus/*") + file(GLOB BB_CORPUS_FILES "${CMAKE_SOURCE_DIR}/fuzzing/corpus/bb_corpus/*") + + if(BT_CORPUS_FILES) + file(COPY ${BT_CORPUS_FILES} DESTINATION ${CMAKE_BINARY_DIR}/corpus/bt_fuzzer) + endif() + if(SCRIPT_CORPUS_FILES) + file(COPY ${SCRIPT_CORPUS_FILES} DESTINATION ${CMAKE_BINARY_DIR}/corpus/script_fuzzer) + endif() + if(BB_CORPUS_FILES) + file(COPY ${BB_CORPUS_FILES} DESTINATION ${CMAKE_BINARY_DIR}/corpus/bb_fuzzer) + endif() + endif() +endfunction() diff --git a/conanfile.txt b/conanfile.txt new file mode 100644 index 000000000..7b81d1d6d --- /dev/null +++ b/conanfile.txt @@ -0,0 +1,8 @@ +[requires] +gtest/1.14.0 +zeromq/4.3.4 +sqlite3/3.40.1 + +[generators] +CMakeDeps +CMakeToolchain diff --git a/contributors.txt b/contributors.txt index 578b30da4..83ef7a03d 100644 --- a/contributors.txt +++ b/contributors.txt @@ -1,4 +1,3 @@ Davide Faconti Michele Colledanchise Rocco Santomo - diff --git a/convert_v3_to_v4.py b/convert_v3_to_v4.py new file mode 100755 index 000000000..0866ffcde --- /dev/null +++ b/convert_v3_to_v4.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +"""Converts BehaviorTree.CPP V3 compatible tree xml files to V4 format. +""" + +import argparse +import copy +import logging +import sys +import typing +import xml.etree.ElementTree as ET + +logger = logging.getLogger(__name__) + + +def strtobool(val: typing.Union[str, int, bool]) -> bool: + """``distutils.util.strtobool`` equivalent, since it will be deprecated. + origin: https://stackoverflow.com/a/715468/17094594 + """ + return str(val).lower() in ("yes", "true", "t", "1") + + +# see ``XMLParser::Pimpl::createNodeFromXML`` for all underscores +SCRIPT_DIRECTIVES = [ + "_successIf", + "_failureIf", + "_skipIf", + "_while", + "_onSuccess", + "_onFailure", + "_onHalted", + "_post", +] + + +def convert_single_node(node: ET.Element) -> None: + """converts a leaf node from V3 to V4. + Args: + node (ET.Element): the node to convert. + """ + if node.tag == "root": + node.attrib["BTCPP_format"] = "4" + + def convert_no_warn(node_type: str, v3_name: str, v4_name: str): + if node.tag == v3_name: + node.tag = v4_name + elif ( + (node.tag == node_type) + and ("ID" in node.attrib) + and (node.attrib["ID"] == v3_name) + ): + node.attrib["ID"] = v3_name + + original_attrib = copy.copy(node.attrib) + convert_no_warn("Control", "SequenceStar", "SequenceWithMemory") + + if node.tag == "SubTree": + logger.info( + "SubTree is now deprecated, auto converting to V4 SubTree" + " (formerly known as SubTreePlus)" + ) + for key, val in original_attrib.items(): + if key == "__shared_blackboard" and strtobool(val): + logger.warning( + "__shared_blackboard for subtree is deprecated" + ", using _autoremap instead." + " Some behavior may change!" + ) + node.attrib.pop(key) + node.attrib["_autoremap"] = "1" + elif key == "ID": + pass + else: + node.attrib[key] = f"{{{val}}}" + + elif node.tag == "SubTreePlus": + node.tag = "SubTree" + for key, val in original_attrib.items(): + if key == "__autoremap": + node.attrib.pop(key) + node.attrib["_autoremap"] = val + + for key in node.attrib: + if key in SCRIPT_DIRECTIVES: + logging.error( + "node %s%s has port %s, this is reserved for scripts in V4." + " Please edit the node before converting to V4.", + node.tag, + f" with ID {node.attrib['ID']}" if "ID" in node.attrib else "", + key, + ) + + +def convert_all_nodes(root_node: ET.Element) -> None: + """recursively converts all nodes inside a root node. + Args: + root_node (ET.Element): the root node to start the conversion. + """ + + def recurse(base_node: ET.Element) -> None: + convert_single_node(base_node) + for node in base_node: + recurse(node) + + recurse(root_node) + + +def convert_stream(in_stream: typing.TextIO, out_stream: typing.TextIO): + """Converts the behavior tree V3 xml from in_file to V4, and writes to out_file. + Args: + in_stream (typing.TextIO): The input file stream. + out_stream (typing.TextIO): The output file stream. + """ + + class CommentedTreeBuilder(ET.TreeBuilder): + """Class for preserving comments in xml + see: https://stackoverflow.com/a/34324359/17094594 + """ + + def comment(self, text): + self.start(ET.Comment, {}) + self.data(text) + self.end(ET.Comment) + + element_tree = ET.parse(in_stream, ET.XMLParser(target=CommentedTreeBuilder())) + convert_all_nodes(element_tree.getroot()) + element_tree.write(out_stream, encoding="unicode", xml_declaration=True) + + +def main(): + """the main function when used in cli mode""" + + logger.addHandler(logging.StreamHandler()) + logger.setLevel(logging.DEBUG) + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "-i", + "--in_file", + type=argparse.FileType("r"), + help="The file to convert from (v3). If absent, reads xml string from stdin.", + ) + parser.add_argument( + "-o", + "--out_file", + nargs="?", + type=argparse.FileType("w"), + default=sys.stdout, + help="The file to write the converted xml (V4)." + " Prints to stdout if not specified.", + ) + + class ArgsType(typing.NamedTuple): + """Dummy class to provide type hinting to arguments parsed with argparse""" + + in_file: typing.Optional[typing.TextIO] + out_file: typing.TextIO + + args: ArgsType = parser.parse_args() + + if args.in_file is None: + if not sys.stdin.isatty(): + args.in_file = sys.stdin + else: + logging.error( + "The input file was not specified, nor a stdin stream was detected." + ) + sys.exit(1) + + convert_stream(args.in_file, args.out_file) + + +if __name__ == "__main__": + main() diff --git a/docs/groot-screenshot.png b/docs/groot-screenshot.png new file mode 100644 index 000000000..99a302f92 Binary files /dev/null and b/docs/groot-screenshot.png differ diff --git a/docs/substitution_sample.json b/docs/substitution_sample.json new file mode 100644 index 000000000..fd908e727 --- /dev/null +++ b/docs/substitution_sample.json @@ -0,0 +1,15 @@ +{ + "TestNodeConfigs": { + "MyTest": { + "async_delay": 2000, + "return_status": "SUCCESS", + "post_script": "msg ='message SUBSTITUED'" + } + }, + + "SubstitutionRules": { + "mysub/action_*": "TestAction", + "talk": "TestSaySomething", + "last_action": "MyTest" + } +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index eade3ca77..a42272367 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,12 +1,10 @@ -cmake_minimum_required(VERSION 3.5.1) - include_directories( ../sample_nodes ) set(CMAKE_DEBUG_POSTFIX "") function(CompileExample name) add_executable(${name} ${name}.cpp ) - target_link_libraries(${name} ${BEHAVIOR_TREE_LIBRARY} bt_sample_nodes ) + target_link_libraries(${name} ${BTCPP_LIBRARY} bt_sample_nodes ) endfunction() @@ -14,10 +12,10 @@ endfunction() # loaded dynamically at run-time. add_executable(t01_first_tree_static t01_build_your_first_tree.cpp ) target_compile_definitions(t01_first_tree_static PRIVATE "MANUAL_STATIC_LINKING") -target_link_libraries(t01_first_tree_static ${BEHAVIOR_TREE_LIBRARY} bt_sample_nodes ) +target_link_libraries(t01_first_tree_static ${BTCPP_LIBRARY} bt_sample_nodes ) add_executable(t01_first_tree_dynamic t01_build_your_first_tree.cpp ) -target_link_libraries(t01_first_tree_dynamic ${BEHAVIOR_TREE_LIBRARY} ) +target_link_libraries(t01_first_tree_dynamic ${BTCPP_LIBRARY} ) CompileExample("t02_basic_ports") CompileExample("t03_generic_ports") @@ -27,9 +25,38 @@ CompileExample("t06_subtree_port_remapping") CompileExample("t07_load_multiple_xml") CompileExample("t08_additional_node_args") CompileExample("t09_scripting") +CompileExample("t10_observer") + +if(BTCPP_GROOT_INTERFACE) + CompileExample("t11_groot_howto") +endif() +CompileExample("t12_default_ports") +CompileExample("t13_access_by_ref") +CompileExample("t14_subtree_model") +CompileExample("t15_nodes_mocking") +CompileExample("t16_global_blackboard") +CompileExample("t17_blackboard_backup") +CompileExample("t18_waypoints") CompileExample("ex01_wrap_legacy") CompileExample("ex02_runtime_ports") -CompileExample("ex03_ncurses_manual_selector") -CompileExample("ex04_waypoints") + +if(BTCPP_SQLITE_LOGGING) + CompileExample("ex03_sqlite_log") +endif() + + +############ Create plugin and executor in folder plugin_example ########## + +# library must be SHARED +add_library(test_plugin_action SHARED plugin_example/plugin_action.cpp ) +# you must set the definition BT_PLUGIN_EXPORT +target_compile_definitions(test_plugin_action PRIVATE BT_PLUGIN_EXPORT ) +# remove the "lib" prefix. Name of the file will be test_plugin_action.so +set_target_properties(test_plugin_action PROPERTIES PREFIX "") +# link dependencies as usual +target_link_libraries(test_plugin_action ${BTCPP_LIBRARY} ) + +add_executable(test_plugin_executor plugin_example/plugin_executor.cpp ) +target_link_libraries(test_plugin_executor ${BTCPP_LIBRARY}) diff --git a/examples/broken_sequence.cpp b/examples/broken_sequence.cpp deleted file mode 100644 index 032d242f9..000000000 --- a/examples/broken_sequence.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "Blackboard/blackboard_local.h" -#include "behaviortree_cpp/behavior_tree.h" -#include "behaviortree_cpp/bt_factory.h" - -using namespace BT; - -NodeStatus SayHello() -{ - printf("hello\n"); - return NodeStatus::SUCCESS; -} - -class ActionTestNode : public ActionNode -{ -public: - ActionTestNode(const std::string& name) : ActionNode(name) - {} - - NodeStatus tick() override - { - time_ = 5; - stop_loop_ = false; - int i = 0; - while (!stop_loop_ && i++ < time_) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - return NodeStatus::SUCCESS; - } - - virtual void halt() override - { - stop_loop_ = true; - } - -private: - int time_; - std::atomic_bool stop_loop_; -}; - -int main() -{ - BT::SequenceNode root("root"); - BT::SimpleActionNode action1("say_hello", std::bind(SayHello)); - ActionTestNode action2("async_action"); - - root.addChild(&action1); - root.addChild(&action2); - - int count = 0; - - NodeStatus status = NodeStatus::RUNNING; - - while (status == NodeStatus::RUNNING) - { - status = root.executeTick(); - - std::cout << count++ << " : " << root.status() << " / " << action1.status() << " / " - << action2.status() << std::endl; - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - return 0; -} -// Output -/* - -hello -0 : RUNNING / SUCCESS / RUNNING -hello -1 : RUNNING / SUCCESS / RUNNING -hello -2 : RUNNING / SUCCESS / RUNNING -hello -3 : RUNNING / SUCCESS / RUNNING -hello -4 : RUNNING / SUCCESS / RUNNING -hello -5 : SUCCESS / IDLE / IDLE - -*/ diff --git a/examples/ex01_wrap_legacy.cpp b/examples/ex01_wrap_legacy.cpp index 622d0f7d1..b6fb195da 100644 --- a/examples/ex01_wrap_legacy.cpp +++ b/examples/ex01_wrap_legacy.cpp @@ -20,7 +20,7 @@ class MyLegacyMoveTo bool go(Point3D goal) { printf("Going to: %f %f %f\n", goal.x, goal.y, goal.z); - return true; // true means success in my legacy code + return true; // true means success in my legacy code } }; @@ -33,7 +33,7 @@ Point3D convertFromString(StringView key) { // three real numbers separated by semicolons auto parts = BT::splitString(key, ';'); - if (parts.size() != 3) + if(parts.size() != 3) { throw RuntimeError("invalid input)"); } @@ -46,12 +46,12 @@ Point3D convertFromString(StringView key) return output; } } -} // namespace BT +} // namespace BT // clang-format off static const char* xml_text = R"( - + @@ -81,7 +81,7 @@ int main() // Register the lambda with BehaviorTreeFactory::registerSimpleAction - PortsList ports = {BT::InputPort("goal")}; + PortsList ports = { BT::InputPort("goal") }; factory.registerSimpleAction("MoveTo", MoveToWrapperWithLambda, ports); auto tree = factory.createTreeFromText(xml_text); diff --git a/examples/ex02_runtime_ports.cpp b/examples/ex02_runtime_ports.cpp index aea5dd1c4..25a13beeb 100644 --- a/examples/ex02_runtime_ports.cpp +++ b/examples/ex02_runtime_ports.cpp @@ -3,7 +3,7 @@ using namespace BT; // clang-format off static const char* xml_text = R"( - + @@ -17,8 +17,8 @@ static const char* xml_text = R"( class ThinkRuntimePort : public BT::SyncActionNode { public: - ThinkRuntimePort(const std::string& name, const BT::NodeConfig& config) : - BT::SyncActionNode(name, config) + ThinkRuntimePort(const std::string& name, const BT::NodeConfig& config) + : BT::SyncActionNode(name, config) {} BT::NodeStatus tick() override @@ -31,15 +31,15 @@ class ThinkRuntimePort : public BT::SyncActionNode class SayRuntimePort : public BT::SyncActionNode { public: - SayRuntimePort(const std::string& name, const BT::NodeConfig& config) : - BT::SyncActionNode(name, config) + SayRuntimePort(const std::string& name, const BT::NodeConfig& config) + : BT::SyncActionNode(name, config) {} // You must override the virtual function tick() BT::NodeStatus tick() override { auto msg = getInput("message"); - if (!msg) + if(!msg) { throw BT::RuntimeError("missing required input [message]: ", msg.error()); } @@ -54,15 +54,16 @@ int main() //-------- register ports that might be defined at runtime -------- // more verbose way - PortsList think_ports = {BT::OutputPort("text")}; + PortsList think_ports = { BT::OutputPort("text") }; factory.registerBuilder( CreateManifest("ThinkRuntimePort", think_ports), CreateBuilder()); // less verbose way - PortsList say_ports = {BT::InputPort("message")}; + PortsList say_ports = { BT::InputPort("message") }; factory.registerNodeType("SayRuntimePort", say_ports); - auto tree = factory.createTreeFromText(xml_text); + factory.registerBehaviorTreeFromText(xml_text); + auto tree = factory.createTree("MainTree"); tree.tickWhileRunning(); return 0; diff --git a/examples/ex03_ncurses_manual_selector.cpp b/examples/ex03_ncurses_manual_selector.cpp index 28172b074..3ce3c1379 100644 --- a/examples/ex03_ncurses_manual_selector.cpp +++ b/examples/ex03_ncurses_manual_selector.cpp @@ -10,7 +10,7 @@ using namespace BT; // clang-format off static const char* xml_text = R"( - + diff --git a/examples/ex03_sqlite_log.cpp b/examples/ex03_sqlite_log.cpp new file mode 100644 index 000000000..9ea8c028b --- /dev/null +++ b/examples/ex03_sqlite_log.cpp @@ -0,0 +1,149 @@ +#include "dummy_nodes.h" +#include "behaviortree_cpp/bt_factory.h" +#include "behaviortree_cpp/loggers/bt_sqlite_logger.h" +#include "behaviortree_cpp/xml_parsing.h" + +struct TaskA +{ + int type; + std::string name; +}; + +struct TaskB +{ + double value; + std::string name; +}; + +using Command = std::variant; + +// Simple Action that updates an instance of Position2D in the blackboard +class SetTask : public BT::SyncActionNode +{ +public: + SetTask(const std::string& name, const BT::NodeConfig& config) + : BT::SyncActionNode(name, config) + {} + + BT::NodeStatus tick() override + { + auto type = getInput("type").value(); + if(type == "A") + { + setOutput("task", TaskA{ 43, type }); + } + else if(type == "B") + { + setOutput("task", TaskB{ 3.14, type }); + } + return BT::NodeStatus::SUCCESS; + } + + static BT::PortsList providedPorts() + { + return { BT::InputPort("type"), BT::OutputPort("task") }; + } + +private: +}; + +// clang-format off + +static const char* xml_text = R"( + + + + + +